AI 에이전트에게 브라우저를 쥐어주면, 토큰이 순식간에 사라집니다.
Playwright MCP로 버튼 하나를 클릭하면 12,891자가 컨텍스트를 차지하고, 스크린샷 한 번이면 15,000 토큰이 날아갑니다. 실제로 5시간짜리 토큰 할당량이 자동화 몇 스텝 만에 바닥나는 사례가 보고되었어요.
왜 이런 일이 벌어지는 걸까요? 그리고 Vercel은 이 문제를 어떻게 해결했을까요?
이 글에서는 Vercel이 만든 오픈소스 도구 agent-browser의 등장 배경, 설계 철학, 그리고 동작 원리를 정리해볼게요.
agent-browser는 Vercel이 만든 오픈소스 CLI입니다. AI 에이전트가 브라우저를 직접 조작하기 위한 도구예요.
기존의 Playwright나 Puppeteer는 사람이 스크립트를 작성하는 도구입니다. CSS 셀렉터를 쓰고, XPath를 쓰고, DOM 구조를 사람이 이해한 뒤 코드를 짜는 게 전제돼요.
agent-browser는 전제 자체가 다릅니다. 사용자가 사람이 아니라 AI 에이전트예요. Claude Code, Cursor, GitHub Copilot, OpenAI Codex 같은 AI 코딩 에이전트가 셸 명령어로 브라우저를 직접 제어합니다.
한 줄로 요약하면, AI 에이전트 전용 브라우저 리모컨이에요.
Legacy
Playwright
Puppeteer
사용자
사람 (개발자)
CSS 셀렉터, XPath로
스크립트를 직접 작성
Agent-First
agent-browser
사용자
AI 에이전트
셸 명령어 한 줄로
브라우저를 직접 제어
AI 에이전트가 프론트엔드를 만들면, 누군가 결과를 확인해야 합니다. 브라우저를 열고, 클릭해보고, 제대로 동작하는지 검증해야 해요. 이 검증을 사람이 하면 병목이 됩니다.
그래서 등장한 개념이 자기검증 루프(self-verifying loop)예요. AI가 코드를 짜고, 직접 브라우저를 열어 테스트하고, 문제가 있으면 스스로 고치는 순환 구조입니다.
이 루프를 돌리려면 AI 에이전트에게 브라우저 자동화 도구를 줘야 합니다. 가장 먼저 시도된 것이 Playwright MCP였어요.
그런데 문제가 심각했습니다.
Playwright MCP는 클릭, 입력, 스크롤 등 동작 하나하나마다 페이지 전체의 accessibility tree를 반환합니다. 일반적인 웹 페이지의 DOM 노드는 3,000개가 넘어요. 버튼 하나를 클릭했을 뿐인데, 3,000개 노드의 모든 속성이 응답으로 돌아오는 겁니다.
실제 수치를 보면 분명합니다.
GitHub issue #889에서는 Playwright MCP 버전 간 토큰 사용량이 6배 증가했다는 보고도 있었어요.
AI 에이전트의 컨텍스트 윈도우는 유한합니다. 브라우저 자동화가 컨텍스트를 다 차지해버리면, 정작 중요한 추론을 할 공간이 없어져요. 자기검증 루프를 한두 바퀴 돌리면 토큰이 바닥나는 겁니다.
핵심 원인은 하나입니다. 사람을 위한 도구를 AI에게 억지로 쥐어준 것.
Vercel은 이 문제를 해결하기 전에, 내부에서 흥미로운 실험을 하나 했습니다.
D0라는 text-to-SQL 에이전트를 만들면서, 도구의 개수와 성능의 관계를 테스트한 거예요. 결과는 직관과 정반대였습니다.
More Tools
17개 도구
특화된 도구 세트
80%
성공률
102K
평균 토큰
Less Is More
2개 도구
범용 도구 세트
100%
성공률
61K
평균 토큰
도구를 17개에서 2개로 줄이자, 성공률은 80%에서 100%로 올라갔고, 토큰 사용량은 37% 줄었습니다.
Vercel의 결론은 명확했어요.
"We were constraining reasoning because we didn't trust the model to reason."
(모델의 추론을 신뢰하지 않아서, 오히려 추론을 제약하고 있었다.)
도구를 많이 주면 AI가 더 잘할 거라는 생각 자체가 틀렸던 겁니다. 도구가 많을수록 AI는 "어떤 도구를 써야 하지?"를 고민하느라 토큰을 낭비하고, 정작 문제 해결에 쓸 추론 공간은 줄어들어요.
이 철학이 agent-browser의 설계를 관통합니다.
Done 6글자로 응답agent-browser의 토큰 효율은 마법이 아닙니다. 원리를 이해하면 납득이 돼요.
웹 브라우저는 화면을 렌더링할 때 내부적으로 두 가지 트리를 만듭니다.
DOM Tree는 우리가 아는 HTML 구조 그대로예요.
<div class="nav-wrapper mx-4 flex items-center">
<button class="btn-primary-v2 px-6 py-3 rounded-lg
text-white font-semibold hover:bg-blue-600"
id="sign-in-btn-2024"
data-testid="auth-cta"
aria-label="Sign In">
Sign In
</button>
</div>
클래스명, id, data 속성, 스타일 정보까지 시각적 렌더링에 필요한 모든 것이 담겨 있습니다.
Accessibility Tree는 시각 장애인용 스크린 리더가 사용하는 트리입니다. DOM에서 의미만 추출한 것이에요.
button "Sign In"
15글자. 클래스명, id, 스타일 전부 사라지고, "이건 버튼이고, Sign In이라고 적혀있다"만 남습니다.
AI 에이전트 입장에서 생각해보면 이 차이가 결정적이에요. "로그인 버튼을 클릭해"라는 작업을 수행할 때, DOM 200자를 읽을 필요가 없습니다. 역할(button)과 이름(Sign In)만 알면 충분해요.
여기에 하나 더 중요한 장점이 있습니다. CSS 셀렉터는 UI가 바뀌면 깨지지만, accessibility tree는 의미 기반이라 UI가 변경돼도 안 깨져요. btn-primary-v2가 button-main으로 바뀌어도, accessibility tree에서는 여전히 button "Sign In"입니다.
원래 시각 장애인을 위해 만든 트리인데, AI 에이전트에게도 최적의 웹 표현이 된 거예요. 화면을 볼 수 없다는 점에서, 둘의 처지가 같기 때문입니다.
agent-browser는 이 accessibility tree를 그대로 쓰지 않고, 한 단계 더 압축합니다. 이게 Snapshot + Refs 시스템이에요.
1단계: Accessibility Tree 추출
Playwright의 page.accessibility.snapshot() API를 호출해서 브라우저 내부의 접근성 트리를 가져옵니다.
2단계: 인터랙티브 요소만 필터링
전체 트리에서 클릭/입력이 가능한 요소만 골라냅니다. onclick 핸들러, cursor: pointer 스타일, tabindex 속성이 있는 요소들이에요.
3단계: Ref 할당
필터링된 요소에 @e1, @e2, @e3... 순서로 짧은 참조 ID를 부여합니다.
- button "Sign In" [ref=e1]
- textbox "Email" [ref=e2]
- textbox "Password" [ref=e3]
- link "Forgot Password" [ref=e4]
4단계: Ref Map 캐싱
이 매핑을 데몬의 메모리(BrowserManager.refMap)에 저장합니다.
@e1 → { role: "button", name: "Sign In" }
@e2 → { role: "textbox", name: "Email" }
이후 click @e1 명령이 오면, 데몬이 refMap에서 @e1을 찾아 Playwright의 getByRole("button", { name: "Sign In" })로 변환하고, 해당 요소를 클릭합니다.
A11y Tree
추출
page.accessibility
.snapshot()
인터랙티브
필터링
onclick, pointer,
tabindex 감지
Ref
할당
@e1, @e2, @e3...
순서대로 부여
RefMap
캐싱
BrowserManager
메모리에 저장
click @e1
→Done
결과적으로 페이지 전체 DOM 대신 4줄짜리 스냅샷만 컨텍스트에 들어가고, 클릭 응답은 Done 한 단어로 끝납니다. Playwright MCP 대비 93% 토큰 절감이 여기서 나오는 겁니다.
전체 아키텍처는 세 계층으로 나뉩니다.
Rust CLI
~1ms
명령어 파싱
플래그 추출
IPC 연결
Node.js Daemon
상주 프로세스
명령 검증 (Zod)
Ref 해석
세션/상태 관리
Chromium
브라우저 엔진
페이지 렌더링
JS 실행
A11y Tree 생성
AI 에이전트는 하나의 작업에 수백 개의 브라우저 명령을 실행합니다. 매번 브라우저를 새로 띄우면 2~5초씩 걸려요. 100개 명령이면 200~500초가 브라우저 시작에만 쓰이는 겁니다.
그래서 데몬이 브라우저를 계속 살려두고, CLI는 명령어 파싱만 합니다. 첫 명령에서 데몬이 자동으로 뜨고, 이후 명령은 이미 떠 있는 데몬에 연결돼요.
agent-browser click @e1을 실행하면 내부에서 이런 일이 벌어집니다.
Step 1: Rust CLI 파싱 (~1ms)
Rust로 작성된 네이티브 바이너리가 명령어를 파싱합니다. click이라는 액션과 @e1이라는 셀렉터를 추출하고, Unix 소켓(macOS/Linux) 또는 TCP(Windows)로 데몬에 연결해요.
Step 2: JSON 메시지 전송
파싱된 명령을 JSON으로 변환해서 소켓으로 보냅니다.
{
"action": "click",
"selector": "@e1",
"options": {}
}
Step 3: Daemon 처리
Node.js 데몬이 JSON을 받아 Zod 스키마로 검증하고, @e1을 refMap에서 조회해 Playwright 로케이터로 변환한 뒤, 실제 클릭을 실행합니다.
Step 4: 응답 반환
Done
6글자. Playwright MCP라면 여기서 전체 accessibility tree 12,891자를 다시 보내지만, agent-browser는 이걸 하지 않습니다. 상태를 확인하고 싶으면 에이전트가 직접 snapshot 명령을 실행하면 돼요. 필요할 때만 보는 겁니다.
$ agent-browser click @e1
Rust CLI 파싱
click + @e1 추출, 소켓 연결
JSON 전송
{ action: "click", selector: "@e1" }
Daemon 처리
Zod 검증 → refMap 조회 → Playwright 클릭
응답 반환
전체 트리 재전송 없이, 결과만
Output
Done
브라우저 자동화에서 로그인이 필요한 작업은 빈번합니다. agent-browser는 세 단계의 상태 지속성을 제공해요.
| 레벨 | 방식 | 저장 범위 |
|---|---|---|
| Ephemeral | 기본값, 저장 없음 | 명령 끝나면 소멸 |
| Session | --session-name 옵션 |
쿠키, localStorage 저장 |
| Profile | --profile 옵션 |
IndexedDB, 서비스워커 포함 전체 저장 |
Google 로그인을 한 번 해두면, 다음 명령에서도 로그인 상태가 유지됩니다. 이게 실제 활용에서 핵심적인 차이를 만들어요.
agent-browser의 진짜 가치는 기술 스펙이 아니라 활용에 있습니다. 사람이 브라우저에서 하던 모든 일을, AI 에이전트가 대신할 수 있게 돼요.
agent-browser에는 dogfood라는 스킬이 있습니다. AI 에이전트가 웹 애플리케이션을 실제 사용자처럼 탐색하면서, 버그를 찾고, 스크린샷을 찍고, 재현 스텝이 포함된 리포트를 마크다운으로 작성합니다.
사람이 하면 반나절 걸리는 탐색적 테스트를, 에이전트가 자율적으로 수행하는 거예요.
세션과 프로필 기능 덕분에, 로그인이 필요한 사이트도 크롤링할 수 있습니다. 한 번 인증하면 그 상태가 유지되기 때문에, 관리자 대시보드의 데이터를 주기적으로 수집하거나, 로그인 뒤에만 보이는 콘텐츠를 가져오는 작업이 가능해요.
Gmail에서 메일을 작성하거나, Slack 메시지를 확인하거나, 사내 도구에서 리포트를 뽑는 일 — 브라우저에서 사람이 반복적으로 하던 작업들을 에이전트에게 맡길 수 있습니다.
agent-browser의 slack 스킬은 API 토큰 없이도, 브라우저 기반으로 Slack의 채널을 탐색하고 메시지를 읽을 수 있어요. electron 스킬은 VS Code, Figma 같은 데스크톱 앱까지 자동화 범위를 넓힙니다.
핵심은 단순합니다. 브라우저에서 사람이 할 수 있는 일이라면, AI 에이전트도 할 수 있습니다.
QA 자동화
웹 크롤링
업무 자동화
agent-browser를 살펴보면서 한 가지 분명해진 것이 있어요.
AI의 인터페이스는 화면이 아니라 텍스트입니다. AI는 버튼의 색상이나 레이아웃을 볼 필요가 없어요. 역할과 이름, 즉 의미만 있으면 됩니다. 그런데 지금까지 우리는 사람용 UI 위에 자동화 레이어를 덧씌우는 방식으로 AI에게 도구를 줘왔어요.
agent-browser가 보여주는 것은, AI를 위한 도구는 처음부터 AI에 맞게 설계해야 한다는 것입니다. DOM 대신 accessibility tree, JSON 대신 compact text, 전체 반환 대신 필요할 때만 조회. 이런 설계가 93%의 토큰 절감이라는 결과를 만들어냈습니다.
이 흐름은 브라우저 자동화에만 국한되지 않을 거라고 생각해요. AI 에이전트가 다양한 서비스와 상호작용하게 되면서, 사람용 UI를 거치지 않고 API로 직접 연결하는 서비스가 더 많아질 겁니다. 이미 MCP(Model Context Protocol)라는 표준이 만들어지고 있고, 다양한 서비스들이 AI 에이전트 전용 인터페이스를 제공하기 시작했어요.
사람은 화면을, AI는 텍스트를. 같은 서비스를 쓰더라도 인터페이스가 달라지는 시대가 오고 있습니다.
이런 변화를 함께 탐구하고 싶다면, 회원 가입해 주세요. 앞으로도 직접 써보고 발견한 것들을 이곳에서 나누려 해요. 😊