---
title: "DOM을 직접 조작하는 오픈소스 웹 에이전트 SDK, dotdotduck 공개"
published: 2026-06-29T06:38:38.000Z
canonical: https://jeff.news/article/4354
---
# DOM을 직접 조작하는 오픈소스 웹 에이전트 SDK, dotdotduck 공개

dotdotduck은 기존 웹사이트 안에 삽입돼 DOM을 직접 보고 조작하는 AI 에이전트 SDK다. 챗봇을 구석에 붙이는 방식이 아니라, Ctrl+K 팔레트, 인라인 AI, 음성, Dwell 제스처, 번역, 분석까지 한 패키지로 제공하려는 쪽에 가깝다.

- dotdotduck은 기존 사이트를 “AI-native site”로 바꾸겠다는 오픈소스 웹 에이전트 SDK임
  - 페이지 구석에 챗봇 하나 붙이는 방식이 아니라, 페이지 안에 들어와 DOM을 보고 조작하는 에이전트를 지향함
  - 사용자는 Ctrl/Command+K 팔레트, 인라인 UI, 음성, Dwell 제스처 같은 여러 진입점으로 맥락을 넘길 수 있음

- 제일 큰 주장 중 하나는 고객지원 티켓의 상당수가 페이지 안에서 해결 가능하다는 것임
  - “주문 추적 어디서 해?”, “요금제 변경 어떻게 해?”, “이 기능 어디 있어?” 같은 질문은 이미 사이트 안에 답이 있는 경우가 많음
  - dotdotduck은 DOM 기반 에이전트가 그 페이지를 실제로 탐색하고 안내해서 쉬운 티켓 70%를 사람에게 가기 전에 deflect하겠다고 말함

- 팔레트는 검색창이 아니라 작업 표면으로 설계돼 있음
  - 각 row의 detail pane과 PanelSkills는 차트, 테이블, 폼, 미니 대시보드 같은 Pieces tree를 렌더링할 수 있음
  - 금융 사이트에서는 `AAPL`을 입력하면 가격 카드와 sparkline을 보여줄 수 있음
  - 고객지원에서는 FAQ 링크만 던지는 대신 포맷된 답변을 바로 인라인으로 보여줄 수 있음
  - SaaS에서는 regex tester, JSON formatter, unit converter, 내부 lookup 같은 도구를 팔레트에 넣을 수 있음

- Dwell 제스처는 “스크린샷 찍고 설명하기”를 줄이려는 기능임
  - 사용자가 요소를 길게 누르면 에이전트가 selector와 자동 스크린샷을 같이 받음
  - 차트, 대시보드 패널, 테이블 row처럼 말로 설명하기 귀찮은 대상을 바로 가리킬 수 있음
  - 의도가 손가락에서 LLM으로 바로 흐른다는 식의 UX를 노림

> [!IMPORTANT]
> dotdotduck의 핵심은 답변 생성보다 “현재 페이지에서 실제 액션을 수행하는 AI 레이어”에 있음. 그래서 DOM 스냅샷, selector, action registry, session 처리가 제품의 중심임.

- 번역 기능도 내장 모듈로 들어감
  - immersive translate는 현재 페이지의 모든 문단을 양쪽 언어로 나란히 렌더링함
  - 200문단짜리 글은 페이지당 약 7번의 LLM call로 batch 처리할 수 있다고 설명함
  - 영어 문서나 제품 카피를 한국어, 중국어, 일본어, 스페인어 사용자에게 보여줘야 하는 SaaS에는 꽤 솔깃한 포인트임

- “여섯 벤더를 붙이지 말자”는 포지셔닝도 강함
  - 검색은 Algolia, 채팅은 Intercom, 분석은 Mixpanel, 음성은 Whisper 식으로 붙이는 대신 하나의 SDK로 묶겠다는 주장임
  - 팔레트, 에이전트, 인라인 AI, 음성, Dwell, proactive, analytics, immersive translate가 한 설치 단위로 제공됨
  - yes/no/multi-choice 응답은 사용자 의도가 명확한 강화학습(RL) 라벨로 기록할 수 있다고 봄

## v0.2.0에서 바뀐 것들

- 이번 릴리스는 webagent core의 구조 개편이 큼
  - breaking change로 기본 설치 액션이 전체 builtinActions가 아니라 `coreActions`가 됨
  - 기본 5개는 narrate, navigate, click, border, scroll_to임
  - formActions, flowActions, extraActions는 opt-in으로 넣어야 함

- TaskAgent가 WebAgent, InlineAgent에 이어 세 번째 agent kind로 추가됨
  - DOM 없이 대화와 host-defined tool calling을 처리하는 plain protocol 에이전트임
  - `ask()`와 `streamAsk()`를 제공함
  - 같은 AgentSession shape을 써서 같은 세션에 연결된 여러 TaskAgent가 대화 히스토리를 공유할 수 있음

- WebAgent는 멀티 인스턴스와 공유 세션을 지원함
  - `dddk.sessions` named-session registry와 `dddk.agents` named-instance registry가 생김
  - route별 persona가 다른 WebAgent를 두고, route change 때 `dddk.agents.setActive(name)`으로 전환하는 식의 구성이 가능함

- 액션과 커서 관련 API도 많이 늘었음
  - `hold_key`, `double_click`, `long_press`, `drag`가 새로 들어옴
  - `press_key`는 modifiers를 지원함
  - `narrate`는 CoT 전용 primitive에서 action registry의 first-class action으로 승격됨
  - `cursorTrail: true`는 click, border, highlight, fill_input, scroll_to, narrate-with-about까지 커버함

- planner가 DOM을 직접 보게 된 것도 큰 변화임
  - planning call마다 현재 페이지 snapshot이 `hostContext`로 들어감
  - briefed sitemap에 빠진 route나 link도 planner가 현재 화면에서 찾을 수 있음
  - 기본 cap은 `plannerDomMaxLength` 8000임

- navigate에는 경로 검증이 붙음
  - sitemap에 없는 path는 reject함
  - 유효한 path list를 LLM에 돌려줘서 재시도하게 함
  - 환각 경로를 따라 404로 빠지는 루프를 줄이려는 장치임

- 스트리밍 envelope parser는 반응성 개선 포인트임
  - scanner 기반 incremental JSON parser가 tool args의 `{ }` 균형이 맞는 순간 action을 dispatch함
  - 바깥 envelope가 완전히 닫힐 때까지 기다리지 않아도 됨
  - `enableStreamingEnvelope: true`로 opt-in함

```mermaid
sequenceDiagram
    participant 사용자
    participant 팔레트
    participant 웹에이전트
    participant 현재페이지
    participant 엘엘엠
    사용자->>팔레트: 명령 입력 또는 요소 Dwell
    팔레트->>웹에이전트: 선택자와 페이지 맥락 전달
    웹에이전트->>현재페이지: DOM 스냅샷 수집
    웹에이전트->>엘엘엠: hostContext와 도구 목록 전달
    엘엘엠-->>웹에이전트: 도구 호출 JSON 스트리밍
    웹에이전트->>현재페이지: click/scroll/navigate 실행
    웹에이전트-->>사용자: subtitle 또는 feedback UI 표시
```

## 분석, 세션, 운영 쪽도 손봤다

- live registry로 도구와 context provider를 런타임 등록/해제할 수 있음
  - `webagent.registerTool(def)`는 ToolHandle을 반환함
  - `webagent.registerContextProvider(role, fn)`도 handle을 반환하고, `remove()`로 제거할 수 있음
  - context provider를 제거하면 빈 슬롯이 아니라 SDK 기본값으로 복구됨

- context provider는 6개 슬롯으로 분리됨
  - `url`, `page_summary`, `dom`, `screenshot`, `history`, `selection`이 기본 슬롯임
  - WebAgent constructor에서 기본 provider가 설치됨
  - 다만 `dom`은 selector resolution의 `currentIndexMap`과 엮여 있어 아직 완전 migration은 뒤로 미뤄짐

- 자체 분석 레이어도 포함됨
  - `@perhapxin/dddk/analytics`는 IndexedDB 기반 EventStore를 제공함
  - `toCSV`, `toNDJSON`, `toSQL` exporter가 있음
  - SQLite, Postgres, MySQL용 canonical `dddk_events` DDL도 제공함
  - 미니 대시보드는 vanilla SVG 차트 6개와 영어/번체중문 라벨을 지원함

- 세션 lifecycle은 더 보수적으로 바뀜
  - F5, Ctrl+R, Ctrl+Shift+R 같은 hard reload는 `sessionContinuityMs`와 무관하게 세션을 항상 지움
  - 기본 `sessionContinuityMs`는 5분에서 0으로 바뀜
  - 즉 host가 opt-in하지 않으면 각 ask가 자기 세션을 갖는 방식임

> [!WARNING]
> v0.2.0은 기본 action bundle이 바뀌는 breaking change가 있음. 기존에 전체 builtin action이 자동 설치된다고 가정한 코드는 migration guide를 봐야 함.

## 도입 전에 봐야 할 현실적인 부분

- 프로젝트는 active development 상태라 rough edge를 예상하라고 직접 말함
  - 문서는 길지만, 작성·유지에 Claude Code를 사용한다고 밝힘
  - 뭔가 이상하면 문서보다 repo를 grep해서 실제 구현을 확인하라는 태도임
  - 공식 랜딩 페이지인 dddk.perhapxin.com도 실제 테스트베드로 사용됨

- 모델 선택은 SDK에 고정돼 있지 않음
  - 데모는 webagent와 planner에 `gpt-5.4-nano`, InlineAgent와 voice cleanup에 `gpt-5.4-mini`를 쓴다고 설명함
  - 패키지는 OpenAI, Google, proxy, OpenAI-compatible vendor adapter를 제공함
  - DeepSeek, Qwen, OpenRouter 같은 baseURL 호환 벤더도 붙일 수 있음
  - 음성 인식은 기본적으로 브라우저 Web Speech API지만, 프로덕션은 Whisper나 Deepgram 같은 `transcribe(audio)` 확장을 연결하는 구조임

- 설치와 사용은 프론트엔드 SDK답게 단순한 편임
  - `pnpm add @perhapxin/dddk` 또는 `npm i @perhapxin/dddk`로 설치함
  - `DotDotDuck`과 `OpenAIProvider`를 만들고 `dddk.mount()`를 호출함
  - CSS custom properties로 `--dddk-bg`, `--dddk-accent`, `--dddk-radius`, `--dddk-font` 등을 오버라이드할 수 있음
  - 다크 모드는 `[data-theme="dark"]`나 `prefers-color-scheme: dark`를 따라감

- 라이선스는 AGPL-3.0-or-later임
  - 내부 서비스에 붙이는 SDK라면 라이선스 영향 범위를 반드시 검토해야 함
  - “오픈소스니까 바로 붙이자”가 아니라, 배포 형태와 수정 여부까지 따져야 하는 타입임

---

## 기술 맥락

- dotdotduck의 기술적 선택은 챗봇을 제품 밖에 두지 않고, 제품 UI 안에 에이전트 런타임을 심는 쪽이에요. 그래서 자연어 답변보다 DOM snapshot, selector, action registry, session, feedback event가 더 중요해져요. 사용자가 원하는 건 설명이 아니라 현재 페이지에서 일이 끝나는 것이기 때문이에요.

- planner가 DOM을 보게 한 것도 같은 이유예요. 사전에 준 sitemap만 믿으면 LLM이 없는 경로를 상상하거나, 현재 화면에 보이는 링크를 놓칠 수 있거든요. DOM을 hostContext로 넣으면 토큰 비용은 늘지만, 실제 페이지 상태에 근거한 계획을 세울 수 있어요.

- 기본 액션을 전체 12개가 아니라 coreActions 5개로 줄인 건 보안과 예측 가능성 쪽 선택으로 보여요. 폼 입력, 드래그, 복잡한 플로우 액션을 기본으로 다 열어두면 데모는 화려하지만 호스트 입장에서는 제어면이 넓어지거든요. 필요한 bundle만 opt-in하는 구조가 운영에는 더 낫습니다.

- 스트리밍 JSON 파서는 UX를 위한 구현 디테일이에요. LLM이 도구 호출 전체를 끝까지 출력할 때까지 기다리면 웹 액션이 굼떠 보이니까, tool args가 완성되는 순간 바로 dispatch하려는 거예요. 웹 에이전트는 사용자가 화면에서 기다리기 때문에 이런 수백 밀리초 단위의 반응성도 체감이 커요.

- 자체 analytics를 넣은 건 단순 이벤트 로깅 이상의 의미가 있어요. yes/no, accept/reject 같은 명시적 사용자 피드백은 나중에 에이전트 평가나 파인튜닝 데이터가 될 수 있거든요. 외부 SaaS에 흩어지는 clickstream보다 “어떤 의도였고 어떤 액션을 받아들였는지”를 한 이벤트 모델로 모으려는 설계예요.

## 핵심 포인트

- 고객지원, 페이지 탐색, 추천, 번역, 폼 조작 같은 작업을 사이트 내부 DOM 기반 에이전트가 처리
- v0.2.0에서 TaskAgent, WebAgent 멀티 인스턴스, 액션 번들 opt-in, 스트리밍 JSON 파서, 자체 분석 레이어가 추가
- 기본 설치 액션이 전체 12개에서 coreActions 5개로 바뀌는 breaking change가 있음

## 인사이트

웹 에이전트가 별도 채팅창에서 답변만 하는 단계를 넘어, 실제 제품 UI 안에서 액션을 실행하는 방향으로 가는 사례임. 다만 AGPL 라이선스, 빠르게 바뀌는 API, AI로 작성된 문서라는 점 때문에 도입 전 코드를 직접 읽고 검증해야 함.
