본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 15. 14:56

픽셀만이 알고 있었던 것: 캔버스 에이전트에게 시각 부여하기

요약

에이전트가 JSON 좌표 데이터뿐만 아니라 스크린샷을 통해 렌더링된 픽셀 정보를 직접 확인하며 작업을 수행하는 메커니즘을 설명합니다. 이를 통해 텍스트 잘림이나 요소 겹침 같은 시각적 문제를 해결하는 과정을 다룹니다.

핵심 포인트

  • 좌표 모델(JSON)만으로는 렌더링 시 발생하는 시각적 오류를 파악할 수 없음
  • 스크린샷을 활용해 픽셀 단위의 시각적 사실(잘림, 정렬 등)을 인지 가능
  • 읽기 전용 렌더링 경로와 토큰 기반의 안전한 시각 관찰 메커니즘 구현

금요일 오전 05:53, Easel 세션에서 한 가지 간단한 질문이 던져졌습니다: "저 이미지는 뭐야?" 에이전트는 솔직하게 대답했습니다. 에이전트는 좌표를 통해 보드 위의 두 이미지를 모두 찾아냈고, 각각 어디에 위치해 있는지 설명한 뒤, 숨겨진 진실을 말했습니다: "저는 파일 참조(file references)만 볼 수 있을 뿐, 픽셀(pixels) 자체는 볼 수 없습니다." 세 시간 후인 08:21, 다른 보드의 다른 세션에서 제목이 시각적으로 잘린 것을 발견했습니다. 에이전트는 전체 줄이 보이도록 텍스트 박스를 넓혔고, 자신이 본 내용을 설명하는 스티키 노트를 남겼습니다. 동일한 에이전트였고, 동일한 모델이었습니다. 차이점은 스크린샷(screenshot)이었습니다.

Easel은 에이전트가 보드 위에서 실시간으로 작업하는 공유 캔버스입니다. 스티키(stickies), 텍스트, 프레임, 생성된 이미지 등이 모두 하나의 JSON 문서에 담기며, 브라우저와 에이전트는 동일한 버전 관리 API를 통해 이를 변경(mutate)합니다. 금요일 오전 전까지 에이전트가 가진 보드에 대한 모든 지식은 바로 그 문서였습니다. 요소 유형, 위치, 크기, z-order, 텍스트 내용 등. 즉, 좌표 모델(coordinate model)이었습니다. 그리고 좌표 모델은 방이 아니라 가구 목록과 같습니다. 그것은 x:120 위치에 너비 260인 텍스트 요소가 존재한다는 것은 알려줄 수 있지만, 글자(glyphs)가 제대로 들어맞는지는 알려줄 수 없습니다.

문서 어디에도 존재하지 않았던 사실

해당 증명 세션은 데모 보드에서 실행되었습니다. 프롬프트는 에이전트에게 눈으로 보드를 판단하고 보이는 것을 수정하라고 요청했습니다. 에이전트는 스크린샷을 찍었고, 스크린샷에는 보드 제목이 "Midnight Bakery —"로 렌더링되어 나머지 줄이 자체 박스에 의해 잘려 있는 모습이 나타났습니다. 문서상에는 아무런 문제가 없었습니다. 요소는 존재했고, 너비는 양수였으며, JSON 내의 텍스트도 온전했습니다. 해당 텍스트가 폰트 메트릭(font metrics), 줄 바꿈(line wrapping), CSS 오버플로(CSS overflow) 과정을 거치며 살아남을 수 있는지 여부는 렌더링 시점(render time), 즉 오직 픽셀(pixels) 상에서만 존재하는 사실입니다. 에이전트는 박스를 넓혔고, 전체 줄이 보이는지 확인하기 위해 다시 한번 살펴보았으며, 관찰 내용을 스티키 노트로 작성했습니다. 46초, 30센트가 소요되었습니다.

이것이 단 하나의 버그 안에 담긴 시각(vision)의 전체 논거입니다. 겹침(Overlap), 정렬 불량(misalignment), 혼잡함(crowding), 잘림(clipping), 배경이 너무 어두워 읽을 수 없게 생성된 이미지 등은 모두 렌더링 시점(render-time)의 사실들입니다. 좌표만으로 시각적 표면을 배치하는 에이전트는 스프레드시트로 인테리어 디자인을 하는 것과 같습니다.

눈이 작동하는 방식

그 메커니즘은 의도적으로 지루할 만큼 단순합니다. 사이트는 보드(board)를 JavaScript 없이 일반 HTML로 미러링하는 읽기 전용 렌더링 경로(render route)를 노출하며, CSS는 라이브 캔버스(live canvas)와 동일하게 사용합니다. 에이전트 세션을 실행하는 브리지(bridge)는 세션당 해당 경로를 위한 토큰을 발행합니다. 이 토큰은 브리지 비밀키(bridge secret)를 키로 사용하여 보드 ID의 HMAC을 생성한 뒤, 32자 16진수로 절삭(truncated)한 값입니다. 토큰은 보드 범위(board-scoped)로 제한되며 읽기 전용이므로, 관찰을 수행하는 서브프로세스(subprocess)는 쓰기 권한이 있는 그 어떤 것도 보유하지 않으며 마스터 베어러(master bearer) 토큰도 전혀 보유하지 않습니다. 잘못된 토큰은 403 오류를 받지만, 발행된 토큰은 보드에 접근할 수 있습니다.

에이전트의 screenshot_board 도구는 형제 컨테이너(sibling container)로 실행되는 Playwright 브라우저를 구동하여, 토큰화된 렌더링 경로로 이동하고, 스테이지를 JPEG로 스크린샷을 찍은 뒤, 이미지 블록을 모델로 직접 전달합니다. 예산은 세션당 5회의 샷(shot)이며, 이는 충분한 것으로 드러났습니다. 여기서 나타난 작업 리듬은 '보고, 움직이고, 다시 보는 것'입니다. 문서를 통해 생각하고, 픽셀로 판단하십시오.

왜 저렴한 이미지가 아닌 실제 브라우저인가

유혹적인 지름길은 브라우저를 건너뛰는 것입니다. 즉, 서버 측에서 JSON을 기반으로 보드를 래스터화(rasterize)하거나, 모델에게 레이아웃을 말로 설명하는 방식입니다. 두 방식 모두 동일한 실수입니다. 이들은 두 번째 렌더러(renderer)이며, 두 번째 렌더러는 첫 번째 렌더러로부터 벗어나게(drift) 됩니다. 잘린 제목(clipped title)이 존재했던 이유는 실제 너비에서 실제 글리프(glyphs)가 실제 CSS에 의해 줄바꿈되는 방식 때문이었습니다. 자체 제작한 래스터라이저(rasterizer)가 가치가 있으려면 이러한 줄바꿈 버그까지 똑같이 재현해야 할 것입니다. 브라우저는 사용자가 보는 것을 증언하는 유일하고 정직한 목격자이므로, 에이전트는 브라우저를 통해 세상을 봅니다. 렌더링 경로는 이러한 관찰 과정을 저렴하고, 안정적이며, 권한 부여가 안전하게 만들기 위해 존재합니다.

조용한 이점도 존재합니다. 스크린샷이 사용자가 열어둔 것과 동일한 표면(surface)을 나타내기 때문에, 에이전트와 사용자는 동일한 그림을 두고 논의하게 됩니다. 만약 에이전트가 제목이 잘렸다는 내용의 스티키(sticky) 노트를 남긴다면, 사용자는 위로 스크롤하여 정확히 어떤 부분이 잘렸는지 확인할 수 있습니다. 증거가 공유되는 것입니다.

한 번 언급된 교훈

시각적 표면(visual surface)에서 작동하는 에이전트에게는 하나의 채널이 아닌 두 개의 채널이 필요합니다. 문서 모델(document model)은 변이(mutation)를 위한 것입니다: 정밀하고, 버전이 관리되며, 차이(diff)를 비교할 수 있어야 합니다. 픽셀(pixels)은 판단을 위한 것입니다: 렌더링 시점의 진실(render-time truth)이 존재하는 유일한 장소입니다. Easel은 첫날부터 첫 번째 채널을 보유하고 있었으며, 이를 통해 유용한 세션들을 출시했습니다. 하지만 자신이 볼 수 없음을 정중하게 고백했던 05:53 세션은, 제품이 자신에게 무엇이 결여되어 있는지를 말해주는 것이었습니다. 08:21 세션이 바로 그 해답이었습니다.

에이전트가 잘린 제목을 포착한 보드는 공개되어 있습니다: 열기. 에이전트 자신의 언어로 작성된 초록색 관찰(observation) 스티키도 여전히 그곳에 있습니다. 토큰을 발행(mint)하고 하위 프로세스(subprocess)를 소유하는 브릿지(bridge)를 포함하여, 이 모든 것을 실행하는 기질(substrate)은 github.com/ghostwright/phantom에 공개되어 있습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0