chrome-agent: 모든 LLM을 스마트한 웹 브라우징 에이전트로 변환하기
요약
chrome-agent는 CDP를 통해 Chrome을 제어하여 LLM이 웹 페이지를 효율적으로 브라우징할 수 있게 돕는 도구입니다. 불필요한 HTML 태그를 제거하고 접근성 트리(Accessibility Tree)를 압축된 텍스트로 변환하여 토큰 소모량을 획기적으로 줄여줍니다.
핵심 포인트
- 접근성 트리를 활용해 HTML 대비 토큰 사용량을 최대 99% 절감
- CSS selector 대신 안정적인 UID를 사용하여 에이전트의 동작 정확도 향상
- MCP 서버나 Node 런타임 없이 3MB 단일 바이너리로 동작
- 브라우저 리더 모드를 기반으로 유용한 콘텐츠를 결정론적으로 추출
LLM에 전체 웹 페이지를 전달하고 사용되는 토큰 양을 지켜본 적이 있나요?
단일 제품 목록만 해도 모델이 필요한 정보를 찾기 전까지 wrapper div, css class, SVG, JSON blob 등 20~30K 토큰에 달하는 'soup(불필요한 데이터의 나열)'이 발생합니다. 에이전트에게 실제로 필요한 것은 아마도 그중 300 토큰 정도(아이템과 가격)일 것입니다. LLM에 가공되지 않은 DOM을 입력하면, 마크업 정리 비용을 대신 지불하게 됩니다.
이것이 바로 chrome-agent가 존재하는 이유입니다. 이 도구는 DevTools Protocol (CDP)을 통해 Chrome을 제어하는 단 하나의 3 MB 바이너리이며, 언어 모델이 저렴하게 읽고 행동할 수 있는 형태로 페이지를 에이전트에게 전달합니다. MCP 서버도, Node 런타임도 필요 없습니다.
예를 들어, 에이전트에게 다음과 같이 요청한다고 가정해 봅시다: "한 상점에서 400달러 미만의 스탠딩 데스크를 찾아 상위 5개를 가격과 함께 반환해줘". 지루한 작업입니다. 기본적인 "WebSearch"를 사용하는 기본 에이전트 워크플로우(agentic workflow)를 사용한다면 제대로 처리하기 어려울 것입니다.
왜 chrome-agent가 이 작업을 수행하는 데 가장 적합한지 보여드리겠습니다.
페이지에 접속하고 살펴보기
chrome-agent goto "https://store.example.com/search?q=standing+desk" --inspect
goto는 페이지를 탐색합니다. --inspect는 유용한 콘텐츠(div soup 제외)를 가져오므로, 에이전트는 단 한 번의 왕복(round trip)으로 유용한 최소한의 콘텐츠를 얻습니다. 반환되는 것은 HTML이 아닙니다. 그것은 스크린 리더가 받는 것과 동일한 역할 및 레이블 뷰인 접근성 트리(accessibility tree)를 압축된 텍스트로 평탄화(flattened)한 것입니다:
uid=n1 heading "Standing Desks (48 results)"
uid=n14 searchbox "Search products"
uid=n22 button "Apply filters"
...
사라진 것들에 주목하세요: div, 클래스 이름, base64 <img 태그가 없습니다. 쓸모없는 HTML 노드들이 제거되었으며, 이는 가공되지 않은 접근성 트리(raw accessibility tree) 대비 약 70% 절감된 수치입니다(가공되지 않은 HTML 대비로는 99% 절감).
chrome-agent는 또한 브라우저의 리더 모드(Reader modes)를 기반으로 페이지에서 무엇이 중요한지를 결정론적으로 추출하는 inspect와 같은 마법 같은 명령어를 가지고 있습니다. 동일한 페이지의 가공되지 않은 HTML이 모델에 20~30K 토큰의 비용을 발생시키는 것에 반해, 이 명령어는 약 50 토큰 정도를 반환할 수 있습니다. 정말 놀라운 수치입니다!
이제 페이지는 모델이 감당할 수 있는 토큰으로 읽을 수 있고, uid를 가리킴으로써 행동할 수 있는 무언가가 됩니다.
왜 CSS selector가 아니라 UID인가요? CSS selector는 형편없고 훨씬 더 많은 토큰을 소모하기 때문입니다.
CSS selector 대신 uid를 사용하는 이유
에이전트가 CSS를 새로 만들어내지 않고도 가리킬 수 있는 안정적인 핸들(handle)이 필요하기 때문입니다. 저 n41, n47 값들은 Chrome 자체의 내부 ID입니다. 이 값들은 동일한 페이지 내에서 반복적으로 검사(inspect)하더라도 안정적으로 유지되므로, 에이전트가 검사하고 추론한 뒤 몇 단계의 명령 후에 n47에 대해 행동하더라도 여전히 동일한 요소를 타격할 수 있습니다.
chrome-agent는 에이전트가 상황에 따라 선택할 수 있는 여러 가지 타겟팅 모드를 제공합니다:
uid(inspect 결과물) => 기본값, 저렴함, 구체적임--selector "css"=> DOM 구조를 이미 알고 있는 경우--xy 100,200=> 캔버스(canvas), 지도, 또는 실제 DOM 노드가 없는 모든 것
우리의 예시에서 에이전트는 모든 링크가 필요한 것이 아니라 가격 컨트롤(price control)이 필요합니다. 그래서 범위를 좁힙니다:
chrome-agent inspect --filter "textbox,button" --urls
--filter는 지정한 역할(role)만 유지합니다 (별칭이 적용되어 있어 textbox를 입력하면 searchbox와 combobox도 함께 가져옵니다). 이제 트리는 몇 줄 안 되는 형태로 압축됩니다:
uid=n14 searchbox "Search products"
uid=n62 textbox "Max price"
uid=n22 button "Apply filters"
여기에 n62가 있습니다. 에이전트가 이를 확인했으므로, 이를 가리킬 수 있습니다.
행동하고, 그리드가 안정될 때까지 기다리기
chrome-agent fill "400" --uid n62
chrome-agent click n22
fill은 최대 가격(max-price) 필드에 텍스트를 입력하고, click은 필터를 적용합니다. 작업이 필요할 때 사용할 수 있는 전체 액션 세트는 다음과 같습니다: click, fill, dblclick, select (옵션 값으로 매칭한 후 표시되는 텍스트로 매칭), check/uncheck (멱등성(idempotent)을 보장하므로, 단계를 재실행해도 잘못된 상태로 토글되지 않음), upload, drag, 그리고 원시 키 입력을 위한 press가 있습니다.
SPA(Single Page Application)를 사용 중이라 실제 페이지 이동이 없고 DOM이 계속 교체되나요? 그것도 문제없습니다! 필터를 클릭하면 XHR이 발생하고 클라이언트 측에서 그리드가 다시 렌더링됩니다. chrome-agent는 네트워크를 기다리므로, 가짜 대기 시간(pseudo-sleep)을 줄 필요가 없습니다:
chrome-agent wait network-idle --idle-ms 500
이 명령은 진행 중인 요청(in-flight requests)을 추적하며, 네트워크가 500ms 동안 조용해지면(모든 JavaScript 호출이 완료되었음을 보장하기에 충분한 시간) 반환됩니다.
데이터를 추출하는 것은 명령이 아니라 결정의 문제입니다
필터링된 그리드(grid)가 화면에 표시되어 있고, 에이전트는 상위 5개의 항목을 구조화된 데이터(structured data)로 가져와야 합니다. 여기에는 계층 구조가 존재하며, 핵심 기술은 매번 동일한 명령을 사용하는 것이 아니라 해당 단계(rung)를 _데이터의 형태(shape of the data)_에 맞추는 것입니다:
read=> 하나의 기사 또는 긴 산문 본문. Mozilla의 Readability를 주입하여 약 15,000개의 HTML 토큰을 약 500개의 깨끗한 토큰으로 변환합니다. 여기서는 잘못된 도구입니다 (이것은 에세이가 아니라 그리드입니다).extract=> 반복되는 레코드: 제품 그리드, 검색 결과, 피드, 테이블. MDR/DEPTA 스타일의 휴리스틱(heuristics)(형제 구조적 유사성, 텍스트 대 링크 비율, 숨겨진 요소 제외 등)이 반복되는 영역을 스스로 찾아냅니다. 이것이 우리가 사용할 단계입니다.text --selector "main"=> 특정 영역의 단어만 필요한 경우, 범위가 지정된 가시적 텍스트(visible copy)를 가져옵니다.eval "..."=> 휴리스틱이 놓칠 수 있는 단일 계산된 값(computed value).network=> 그리드가 JSON API로부터 그려진 경우, DOM을 건너뛰고 그리드에 데이터를 공급한 응답(response)을 읽습니다.
이 작업의 경우, extract를 사용합니다:
chrome-agent extract --limit 5 --json
{
"ok": true,
"records": [
...
만약 상점이 스크롤 시 행을 지연 로딩(lazy-load)한다면, extract --scroll이 페이지를 아래로 내리며 새로운 콘텐츠가 나타날 때까지 MutationObserver를 감시합니다.
때로는 가장 저렴한 단계가 페이지 자체가 아닐 수도 있습니다. 해당 그리드는 JSON 호출에 의해 채워졌습니다. 에이전트는 이를 직접 가져올 수 있습니다:
chrome-agent network --filter "/api/search" --body --limit 1
스크래핑 없이 동일한 가격 정보를 얻습니다.
오류가 발생하면, 에러 메시지가 에이전트에게 시도할 방법을 알려줍니다
에이전트는 끊임없이 실패합니다. 에이전트가 복구할 수 있을지 여부를 결정하는 것은 실패 시 반환되는 정보입니다. --json 모드에서 오류가 발생하면 종료 코드 1(exit 1)로 종료되지만, 여전히 표준 출력(stdout)에 파싱 가능한 객체를 출력하며, 명확한 다음 단계가 있는 경우 hint와 함께 반환됩니다.
{"ok": false, "error": "No element with uid=n47", "hint": "Page may have changed. Re-run inspect to get current uids."}
에이전트는 ok:false를 읽고, hint를 읽은 뒤, 다시 검사(re-inspect)하고 재시도합니다. 모든 명령에 대해 복구 로직을 일일이 수동으로 작성할 필요 없는 자가 치유(Self-healing) 기능입니다.
수백 개의 프로세스 생성 대신 단 하나의 연결
명령마다 바이너리를 한 번씩 실행하면 매번 프로세스 시작 비용과 새로운 Chrome 연결 비용이 발생합니다. 단계가 많은 작업에서는 이 비용이 누적됩니다. 그래서 파이프 모드(pipe mode)가 있습니다. 하나의 지속적인 연결을 유지하며, stdin으로 JSON 명령을 받고 stdout으로 JSON 결과를 출력합니다.
chrome-agent pipe <<'EOF'
{"cmd":"goto","url":"https://store.example.com/search?q=standing+desk","inspect":true}
{"cmd":"fill","selector":"input[name=maxPrice]","value":"400"}
...
Playwright가 이미 존재하는데, 언제 이것을 사용해야 할까요?
사용 사례가 다릅니다:
- Playwright / Puppeteer => 결정론적(deterministic) 엔드 투 엔드(end-to-end) 테스트: 단언(assertion), 픽스처(fixture), 특정 셀렉터 대기, 시각적 차이 비교(visual diffing). 테스트 스위트를 작성하는 사람을 위한 완전하고 성숙한 자동화 API입니다.
- chrome-agent => 한 번도 본 적 없는 페이지를 읽고 동작해야 하며, 감당 가능한 토큰 범위 내에서 움직이고, 스스로 실패로부터 복구해야 하는 LLM 에이전트용입니다.
역할이 다릅니다. 단언(assertion) 중심의 CI 스위트를 작성 중이라면 Playwright를 사용하세요. 만약 웹 조사를 수행하고 에이전트가 웹을 브라우징해야 한다면, 에이전트는 컨텍스트 윈도우(context window)를 낭비하며(HTML 수프를 파싱하며) 비용을 발생시키고 있는 것입니다. 그러지 마세요.
더욱 놀라운 기능!
chrome-agent는 LLM 에이전트가 필요로 하는 더 많은 기능을 지원합니다:
- 요소를
drag(드래그)할 수 있습니다. - 요소를
download(다운로드)할 수 있습니다. iframe을 처리할 수 있습니다.--stealth모드를 통해 Cloudflare 및 Turnstile을 우회하기 위한 패치를 적용할 수 있습니다. 다만, DataDome이나 Kasada에 대해서는 보장되지 않습니다. 이들은 번들된 Chromium의 핑거프린트(fingerprint)를 채취하며 지속적으로 탐지 업데이트를 배포하기 때문입니다.--copy-cookies를 사용하여 실제 사용자 쿠키를 활용함으로써 로그인된 사이트에 접근할 수 있습니다.
시도해 보세요
설치하고 잊어버릴 수 있는 간편한 방법으로 시작하세요:
시도해 보세요
설치하고 잊어버릴 수 있는 간편한 방법으로 시작하세요:
npx skills add sderosiaux/chrome-agent
# 또는
cargo install chrome-agent
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기