에이전트에게 눈을 달아준다는 것 — 시각적 검증의 병목
어느 날 에이전트에게 SVG 다이어그램을 만들어달라고 했다. 코드는 깔끔했고 타입 에러도 없었고 빌드도 한 번에 통과했다. 잘 됐네 싶어서 브라우저를 열었는데, 텍스트가 박스를 삐져나가 있었고 모바일에서는 그림이 너무 작아 읽을 수가 없었다.
다음 날은 코드 블록 스타일을 수정해달라고 했다. 에이전트는 CSS를 고치고 빌드를 통과시켰는데, 데스크톱에서는 괜찮아 보이던 게 모바일에서 열어보니 코드 블록 마진이 사라져 화면 끝까지 꽉 차 있었다. 다시 수정을 요청했더니 또 빌드는 통과했지만, 이번에는 마진은 생긴 대신 오버플로우가 잘려서 긴 코드 라인을 볼 수 없었다. 이런 사이클이 두세 번 반복됐다.
한두 번이면 우연이겠지만 반복되면 패턴이 보인다. 에이전트는 코드를 완벽히 읽는다. 문법 오류, 타입 불일치, 누락된 import처럼 텍스트 레벨의 문제라면 사람보다 정확하게 잡아낸다. 하지만 렌더링 결과는 볼 수 없어서, CSS 한 줄을 바꿨을 뿐인데 페이지 세 개의 레이아웃이 무너져도 알아채지 못한다. 빌드 통과와 시각적 정상은 전혀 다른 얘기다.
지금 AI 코딩 에이전트는 멀티 에이전트 조합이 트렌드다. Garry Tan(YC CEO)의 gstack이 대표적인데, AI를 하나의 만능 도구로 쓰지 않고 역할별 전문가로 쪼개는 접근이다. CEO 모드(제품 전략), Eng Manager 모드(아키텍처 리뷰), Staff Engineer 모드(코드 리뷰), QA Engineer 모드(테스트)를 슬래시 커맨드로 전환하면서 Claude Code 인스턴스 하나를 가상의 팀처럼 굴린다. 최대 12개 병렬 워커를 동시에 돌려 60일 동안 60만 줄 넘는 프로덕션 코드를 작성했다.
도구를 섞어 쓰는 방식도 일반화되고 있다. Claude Code는 복잡한 아키텍처 작업에, Cursor는 인라인 편집에, Copilot은 자동완성에 배치하는 식으로 6개월 이상 실전에서 운영되고 있다.
그런데 이 모든 도구의 공통 약점이 시각 검증이다. 코드를 쓰는 능력은 비약적으로 발전했지만, 그 코드가 화면에서 어떻게 보이는지 확인하는 일은 여전히 사람 몫이다. gstack이 주목받는 진짜 이유도 코드량이 아니라 /browse 명령에 있다. 에이전트에게 실제 브라우저를 연결해 렌더링 결과를 직접 피드백하게 만든 것, 그러니까 에이전트에게 눈을 달아준 것이다.
▍에이전트에게 없는 감각
에이전트의 전형적인 검증 루프는 코드 생성 → 빌드 → 타입체크 → 테스트 → 완료다. 여기에 “화면에서 실제로 보기”는 빠져 있어서, 빌드가 성공했으니 끝이라고 판단한다. 사람이라면 당연히 브라우저를 열어 확인했을 자리다.
에이전트에게 “모바일에서 코드 블록 마진 수정해줘”라고 하면 CSS를 고치고 빌드를 돌려 성공하면 끝이라고 보고한다. 그런데 실제 모바일 뷰포트에서 확인해보면 여전히 문제가 남아 있곤 한다. margin: 0 16px을 넣었는데 부모 컨테이너의 overflow: hidden이 내용을 자르고 있거나, 미디어 쿼리 순서가 엉켜서 데스크톱 스타일이 모바일을 덮어쓰고 있는 식이다. 에이전트의 “완료”와 사용자의 “완료”는 기준이 다르다.
기존 E2E 테스트도 이 문제를 풀지 못한다. getByText('Submit')은 DOM에 그 버튼이 있는지는 확인하지만, 그 버튼이 다른 요소에 가려져 클릭할 수 없는 상태인지는 모른다. expect(element).toBeVisible()조차 CSS의 overflow: hidden으로 잘린 콘텐츠까지는 잡아내지 못한다. DOM 기반 테스트의 한계다.
에이전트가 생성하는 코드의 비율이 높아질수록 이 시각 검증 간극도 비례해서 커진다. 사람이 직접 짠 코드라면 짜면서 중간중간 브라우저를 확인하지만, 에이전트가 한 번에 파일 10개를 수정하면 그 결과를 하나하나 확인할 여력이 없다. 에이전트 생산성이 올라갈수록 검증 병목도 같이 올라가는 역설이다.
에이전트에게 수정을 맡기고 렌더링 결과를 확인하지 않은 채 커밋하는 건 코드리뷰 없이 머지하는 것과 같다. 코드는 맞는데 결과가 틀린 상태가 프로덕션에 나간다.
▍선두 개발자들의 접근법
이 문제에 대한 접근은 크게 세 가지로 나뉜다.
| 접근 | 핵심 도구 | 원리 | |
|---|---|---|---|
| 1 | 에이전트에 브라우저 연결 | Playwright, Puppeteer | 실제 렌더링 결과를 스크린샷/DOM으로 피드백 |
| 2 | Visual Regression Testing | Applitools, Percy | 베이스라인 스크린샷 대비 변경점 자동 탐지 |
| 3 | Intent 기반 로케이터 | Momentic | 자연어로 UI 요소를 찾아 셀렉터 변경에 강건 |
에이전트에게 붙이는 진짜 브라우저
gstack의 /browse 명령은 Playwright 기반 Chromium 데몬을 쓰고 응답 시간은 100ms다. diff가 생기면 영향받는 라우트를 자동으로 골라내 실제 브라우저에서 렌더링한 뒤 스크린샷을 에이전트에게 돌려준다.
핵심은 단일 뷰포트가 아니라는 점이다. /qa 에이전트가 git diff를 분석해 변경이 영향을 미치는 라우트 목록을 뽑으면, /browse가 각 라우트를 데스크톱(1440px), 태블릿(768px), 모바일(375px) 세 뷰포트에서 캡처한다. 사람이 에디터와 브라우저를 Alt-Tab으로 오가며 확인하던 행위를 그대로 자동화한 셈이다.
에이전트가 직접 보고, 문제를 발견하고, 고치고, 다시 확인하는 루프가 만들어진다. 사람이 개입하지 않아도 셀프 교정이 되고, 여기에 시각 회귀 테스트까지 자동 생성하면 같은 문제가 재발하지 않는다. 눈이 있는 에이전트와 없는 에이전트의 차이는 생각보다 크다.
나도 비슷한 문제를 다른 각도에서 풀고 있다. 병원 EMR SaaS를 개발하면서 만든 파이프라인인데, gstack이 런타임에 에이전트가 브라우저를 조작하는 방식이라면 내 쪽은 빌드타임에 코드를 정적 분석해 테스트를 미리 생성하는 방식이다.
구조는 이렇다. ts-morph로 TSX 파일을 파싱해서 Page → Modal → Button → Input 같은 컴포넌트 계층 관계를 그래프 JSON으로 뽑는다. 이 그래프를 Claude API에 넘기면 테스트 케이스 스키마가 나오고, 그걸 다시 Playwright 스펙으로 변환한다. “수납 확인 모달에서 결제 버튼을 클릭하면 영수증 페이지로 이동한다” 같은 시나리오가 코드 변경에 맞춰 자동으로 만들어진다.
TSX 파싱 (ts-morph) → 컴포넌트 그래프 JSON → Claude API (TC 스키마) → Playwright 스펙
gstack의 /qa가 git diff에서 영향받는 라우트를 찾듯이, 이 파이프라인도 변경된 컴포넌트에서 영향받는 페이지와 플로우를 그래프 관계로 추적한다. 다만 접근이 다르다. gstack은 이미 렌더링된 화면을 에이전트가 직접 보는 것이고, 이 파이프라인은 렌더링 전에 무엇을 테스트해야 하는지를 코드 구조에서 미리 도출한다. 런타임 시각과 빌드타임 분석은 둘 다 필요해서, 이상적으로는 빌드타임에 테스트를 만들고 런타임에 그 테스트를 브라우저에서 실행하며 스크린샷까지 캡처하는 이중 루프가 된다.
이 파이프라인의 토대가 data-testid 기반 시맨틱 레이어다. 모든 주요 UI 요소에 의미 있는 testid를 붙여두면 DOM 구조가 바뀌어도 테스트가 깨지지 않는 안정적인 접점이 생긴다. 구현 세부는 이전 글에서 다뤘으니 여기서는 시각 검증 도구 비교에 집중하자.
스크린샷 비교로 잡는 시각 회귀
Applitools Visual AI나 Percy 같은 도구는 PR마다 변경 전후의 스크린샷을 비교해서, 의도치 않은 시각 변경을 자동으로 잡아낸다.
순수 픽셀 비교는 false positive가 너무 많다. 폰트 렌더링 차이나 안티앨리어싱, 서브픽셀 렌더링 때문에 실제 문제가 아닌데도 경고가 울린다. 그래서 Applitools는 AI 기반 비교를 도입해 구조적으로 의미 있는 변경만 잡고 나머지는 넘긴다.
Percy는 GitHub PR 워크플로우와 긴밀하게 붙는다. PR을 올리면 시각 diff 대시보드가 자동으로 생기고 리뷰어가 변경을 승인하거나 거부하는데, “visual review approved”가 머지 조건이 되는 식이다.
여기서 솔직히 짚고 넘어갈 게 있다. 이런 도구는 변경 감지는 탁월하지만, 그게 의도된 변경인지 버그인지 판단하는 일은 여전히 사람 몫이다. 테마를 리뉴얼하면 스크린샷 수십 개가 한꺼번에 바뀌고 누군가 새 베이스라인을 승인해야 한다. Applitools가 유사한 변경을 묶어서 일괄 승인하게 해주고 self-healing 로케이터로 셀렉터 변경에도 테스트가 안 깨지게 해주긴 한다. 하지만 완전 자동은 아니고 반자동이다.
결국 이 도구는 단독 해법이 아니라 CI/CD 파이프라인의 안전망으로 봐야 한다. 브라우저 연결이 에이전트에게 능동적 시각을 주는 것이라면 Visual Regression Testing은 수동적 감시 카메라에 가깝다. 에이전트가 직접 보고 고친 뒤 최종적으로 베이스라인 비교가 잡아내는 이중 루프를 갖췄을 때 비로소 실질적인 시각 검증이 완성된다.
프로세스 쪽도 문제다. 누가 베이스라인을 승인할지(디자이너인지, 프론트엔드 리드인지, PM인지)가 명확하지 않으면 승인 대기 자체가 병목이 된다. 도구를 도입하는 것보다 프로세스를 설계하는 게 더 어려운 경우가 많다.
줄여야 할 테스트 유지보수 비용
시각 검증 파이프라인을 만들어도 테스트 자체의 유지보수가 병목이 되면 의미가 없다. DOM 구조가 바뀔 때마다 셀렉터가 깨지고, 고치면 또 깨지는 악순환에 빠진다.
테스트 유지보수는 자동화의 숨은 비용이다. 한 팀이 E2E 테스트를 500개 작성했다고 하자. 초기에는 잘 돌아가지만 6개월이 지나면 UI 리뉴얼과 컴포넌트 라이브러리 교체, 라우트 구조 변경으로 셀렉터가 깨지기 시작한다. 엔지니어링 시간의 30%가 테스트 유지보수에 들어간다는 조사 결과도 있다. 테스트를 짜는 것보다 유지하는 게 더 비싸다.
Momentic은 자연어 로케이터로 이 문제를 공략한다. “로그인 버튼을 클릭”이라고 쓰면 DOM 구조가 바뀌어도 해당 요소를 알아서 찾으니, #btn-login-v3이 [data-testid="auth-submit"]으로 바뀌어도 테스트가 깨지지 않는다. 셀렉터 관리라는 지루한 작업에서 벗어나는 셈이다.
트레이드오프는 있다. 자연어는 CSS 셀렉터보다 정밀도가 떨어져서, “장바구니 버튼”이 페이지에 두 개 있으면 어느 것을 클릭할지 모호해진다. 하지만 유지보수 비용을 생각하면 약간의 정밀도를 내주고 훨씬 높은 회복력을 얻는 교환이다. 테스트 500개를 30% 시간으로 유지할지 자연어 기반으로 5% 시간으로 유지할지, 대부분의 팀에게 답은 명확하다.
앞서 언급한 내 파이프라인은 이 문제를 data-testid 시맨틱 레이어로 푼다. DOM 구조가 바뀌어도 테스트가 안 깨지게 하려는 목적이니 Momentic이 자연어로 “OK 버튼 클릭”을 해석하는 것과 방향은 같고, 차이는 추상화 수준이다.
“셀렉터가 깨지면 테스트가 깨진다”는 하나의 근본 원인을 세 가지 도구가 각각 다른 높이에서 우회하고 있다.
| 접근 | 추상화 수준 | 장점 | 약점 |
|---|---|---|---|
| Applitools self-healing | AI가 요소를 재탐색 | 기존 테스트 수정 불필요 | 반자동, 베이스라인 승인 필요 |
data-testid 시맨틱 레이어 | 명시적 식별자 부여 | 정밀하고 결정론적 | 초기 세팅 비용, testid 유지 필요 |
| Momentic 자연어 | 의도 기반 해석 | 유지보수 최소 | 모호성, 정밀도 트레이드오프 |
Applitools는 기존 테스트를 건드리지 않고 AI가 알아서 적응하게 하니 노력도 통제력도 가장 낮다. 내 testid 레이어는 모든 주요 UI 요소에 data-testid="payment-confirm-button" 같은 명시적 계약을 심어서 초기 비용은 들지만 결정론적이다. Momentic은 “결제 버튼 클릭”이라는 자연어로 가장 높은 추상화를 제공하니 유지보수가 거의 없는 대신 모호한 경우에 취약하다.
어느 쪽이 정답이라기보다 프로젝트 성격에 따라 선택이 갈린다. 빠르게 변하는 스타트업 UI라면 Momentic의 유연함이 맞고, 의료 SaaS처럼 정확한 플로우 검증이 필수인 도메인이라면 testid 기반이 안전하다. 레거시 테스트가 대량으로 쌓인 팀이라면 Applitools의 self-healing이 현실적인 출발점이 된다.
▍마무리
코드를 빨리 쓰는 건 이미 에이전트가 잘한다. 코드 작성 능력은 빠르게 commoditize되고 있어서, 모든 에이전트가 코드를 잘 쓰게 되면 차이를 만드는 건 얼마나 빨리 쓰느냐가 아니라 결과가 진짜 맞는지 어떻게 확인하느냐가 된다.
에이전트 시대의 차별화 포인트는 에이전트에게 눈을 달아주는 사람이다. 시각 검증 파이프라인을 설계하고, 렌더링 피드백 루프를 만들고, 시각 회귀 테스트를 자동화하는 쪽에 있다. 결국 중요한 건 에이전트가 무엇을 만들 수 있느냐가 아니라, 만들어진 게 정말로 맞는지를 어떻게 확인하느냐다.
에이전트의 눈은 단순히 스크린샷을 찍는 장치가 아니라 사용자의 의도와 렌더링 결과를 대조하는 장치다. “이 버튼은 모바일에서도 누를 수 있어야 한다”는 의도를 375px 뷰포트의 스크린샷에서 검증하는 일, 이 간극을 메우는 게 다음 단계의 엔지니어링이다.