개발도구
에이전트는 멋진 HTML 산출물을 잘 만든다. 그런데 그걸 사람이 검토하고 고치는 과정은 여전히 "스크린샷 찍고 → 이거 저거 바꿔줘"라는 긴 글로 퇴화한다. lavish-axi는 그 망가진 루프를, 요소를 직접 클릭해 코멘트를 다는 로컬 에디터로 갈아끼운다. MCP가 아니라 "CLI"로 에이전트와 연결되는 새로운 패턴(AXI)의 교본이다.
lavish-axi(Lavish Editor)는 AI 에이전트가 만들어 낸 HTML 파일을 로컬 브라우저로 열어, 사람이 화면 위 요소를 콕 집어 피드백을 보내면 그 피드백이 정확한 DOM 위치 정보와 함께 에이전트에게 되돌아가는 로컬 전용 리뷰 에디터입니다. 한 줄 슬로건은 "HTML is the new markdown." — 이제 에이전트의 결과물은 텍스트가 아니라 인터랙티브한 HTML 아티팩트이고, 그걸 다루는 전용 에디터가 필요하다는 주장입니다.
설치도, 서버도, 클라우드 계정도 필요 없습니다. npx lavish-axi plan.html 한 줄이면 로컬에 작은 HTTP 서버가 뜨고 브라우저가 열립니다. 개발자(또는 에이전트)는 lavish-axi poll plan.html로 사람의 코멘트를 기다리고, 사람은 화면에서 문단을 클릭하거나 텍스트를 드래그해 "여기 이렇게 바꿔줘"라고 답니다. npm 패키지명은 lavish-axi, 라이선스는 MIT, 만든 사람은 Kun Chen입니다.
워드 문서에 빨간펜으로 "이 문장 고쳐"라고 메모를 달면 작성자가 그 위치를 정확히 안다. lavish-axi는 똑같은 경험을 AI가 생성한 HTML에 제공한다. 사람이 화면 요소에 코멘트를 달면, 에이전트는 "어느 요소에, 무슨 말을" 했는지를 좌표가 아니라 DOM 셀렉터로 정확히 받아 수정한다.
차이는 방향이다. 구글 독스는 사람끼리 협업하지만, lavish-axi는 사람 ↔ AI 에이전트가 같은 화면을 보며 협업한다.
2026년 들어 Claude Code·Codex·OpenCode 같은 코딩 에이전트가 일상화되면서, 에이전트가 내놓는 결과물이 "코드"를 넘어 "리치 HTML 산출물"(기획서·다이어그램·비교표·리포트)로 확장됐습니다. 그런데 이걸 검토하는 방법이 원시적이었습니다. lavish-axi는 바로 그 "검토 루프(review loop)"의 빈자리를 정확히 찔러서 트렌딩에 올랐습니다.
README의 문제 정의가 날카롭습니다. "에이전트는 리치 HTML을 잘 만들지만, 그걸 두고 사람과 협업하는 고리가 빈약해서 결국 스크린샷을 찍고 '이거 바꿔줘'라는 긴 답글로 되돌아간다. 그 과정에서 HTML이 가장 잘하는 것 — 인터랙티비티 — 을 잃는다." lavish-axi는 사람이 화면에서 직접 요소를 클릭/선택해 코멘트를 달게 만들어, 글로 위치를 설명할 필요를 없앱니다.
대부분의 에이전트 연동은 MCP 서버를 띄우고 설정 파일을 건드려야 합니다. lavish-axi는 그냥 CLI 한 줄입니다. 저자는 이를 AXI라 부릅니다 — "어떤 유능한 에이전트든 설정 없이 실행할 수 있는 CLI. 스킬도 필요 없다. 에이전트는 써 보면서 배운다." 출력은 토큰 효율적인 TOON 포맷이고, 응답 안에 "다음에 뭘 해야 하는지"까지 적어 에이전트를 다음 단계로 안내합니다.
클라우드가 전혀 없습니다. 모든 게 127.0.0.1에서 돕니다. 더 중요한 건 원본 HTML을 거의 건드리지 않는다는 점입니다. 서버는 파일에 <script src="/sdk.js"> 태그 딱 하나만 끼워 넣습니다. 그래서 저장된 HTML을 서버 없이 그냥 브라우저로 열어도 똑같이 보입니다. "도구에 갇히지 않는다(no lock-in)"는 철학이 코드 수준에서 지켜집니다.
경쟁 도구가 "에이전트가 만든 걸 어떻게 보여줄까"에 머문다면, lavish-axi는 "사람이 그걸 보고 어떻게 되돌려줄까"를 푼다. 생성이 아니라 피드백의 왕복이 진짜 문제라는 통찰이 차별점이다.
lavish-axi는 화려한 프레임워크 더미가 아닙니다. 순수 JavaScript(ESM) + Express 서버 한 개로 된, 의외로 단출한 도구입니다. React도, 빌드된 프론트엔드 번들도 없습니다. 브라우저 UI("chrome"이라 부름)조차 서버가 렌더한 평범한 HTML + 바닐라 JS입니다. "작지만 단단하게" 만드는 법을 보여 주는 좋은 표본입니다.
| 항목 | 내용 |
|---|---|
| 언어 | 순수 JavaScript, ESM 전용("type":"module"). TS 소스는 없고 checkJs로 .js를 타입 검사 |
| 런타임 | Node.js 22+ (CI는 Node 24 · ubuntu/macOS/windows) |
| 테스트 | 내장 node:test + node:assert/strict (node --test). 8개 파일 약 2,542줄 |
| 빌드 | esbuild로 bin/lavish-axi.js → dist/cli.mjs 번들. 버전·텔레메트리 env를 빌드 타임에 인라인 |
| 패키지 매니저 | pnpm 11. minimumReleaseAge 7일 — 갓 나온 의존성 업데이트는 일주일 묵혔다 받는 공급망 안전장치 |
| 품질 | ESLint 10(flat config) + Prettier 3, release-please가 CHANGELOG·버전 자동 관리 |
| 패키지 | 역할 |
|---|---|
| axi-sdk-js | AXI 프레임워크의 뼈대. runAxiCli(TOON 직렬화 CLI), 에이전트별 SessionStart 훅 설치. "에이전트 친화적" 스토리 전체가 여기서 나옴 |
| express ^5 | 로컬 HTTP 서버 — 라우트 · JSON 바디 · 정적 자산 · SSE 스트림 |
| chokidar | 파일 감시 → 아티팩트 HTML이 바뀌면 브라우저 자동 새로고침(live reload) |
| open | 세션 URL을 시스템 브라우저로 띄움 |
| @tailwindcss/browser 4 | 옵션형 디자인 시스템. /design 경로에서 브라우저용 Tailwind v4 런타임 제공 |
| daisyui 5 | 같은 디자인 경로의 DaisyUI v5 CSS 컴포넌트 |
리뷰 에디터를 만들면서 프레임워크·상태관리·번들러 묶음을 끌어오지 않았다. 서버는 Express 하나, UI는 바닐라 JS, 테스트는 Node 내장 러너. 플랫폼(Node·브라우저)에 이미 있는 것을 최대한 쓴다는 절제가 코드 전반에 깔려 있다. "작은 도구"의 의존성 다이어트를 배우기 좋은 예다.
EventSource). WebSocket보다 가볍고 HTTP 위에서 동작한다. lavish-axi는 이걸로 "파일 변경됨 → 새로고침", "에이전트 답장 도착", "에이전트 작업 중" 같은 이벤트를 화면에 흘려보낸다.lavish-axi의 진짜 배울 점은 "에이전트 ↔ 사람 비동기 협업"을 단순한 부품으로 조립한 방식입니다. 핵심 흐름은 에이전트가 HTML을 쓴다 → CLI가 연다 → 사람이 코멘트를 단다 → CLI가 poll로 받아 고친다는 루프이고, 그 사이를 롱폴링 · SSE · postMessage · 파일감시라는 네 가지 검증된 웹 기술이 잇습니다.
session-store.js의 핵심 아이디어입니다. 세션 ID를 따로 발급하지 않고, 파일의 실제 경로(realpath)를 sha256 해시한 앞 16자를 세션 키로 씁니다. 그래서 같은 파일을 다시 열면 자동으로 같은 세션이 이어지고, 심볼릭 링크나 중복 경로도 하나로 합쳐집니다. "불투명한 ID"를 아예 없애는 깔끔한 설계입니다.
호텔 객실을 "예약번호"가 아니라 "방의 실제 주소(3층 302호)"로 식별하는 것과 같다. 같은 방을 다시 찾으면 새 번호를 받을 필요 없이 그 방으로 바로 간다. 파일 경로 자체가 주소이자 신분증이다.
에이전트가 만든 HTML은 신뢰할 수 없는 코드일 수 있습니다. 그래서 아티팩트는 sandbox="allow-scripts ..." iframe 안에서 돌되 allow-same-origin은 일부러 뺍니다. 즉 아티팩트 안의 스크립트는 에디터(chrome)의 DOM을 절대 못 읽습니다. 둘 사이 통신은 전부 postMessage로만 오갑니다. 심지어 스크롤 위치조차 메시지로 주고받아야 할 만큼 철저히 격리돼 있습니다.
아티팩트 HTML에 악의적 스크립트가 섞여 있어도, 에디터의 쿠키·상태·다른 세션을 훔칠 수 없다. 권한을 allow-scripts만 주고 allow-same-origin을 빼는 한 끗 차이가 "유리벽"을 만든다. 신뢰 경계(trust boundary)를 코드로 긋는 정석이다.
아티팩트에 주입되는 SDK(artifact-sdk.js)는 별도 번들 파일이 아닙니다. 서버가 createArtifactSdk.toString()으로 함수 자체를 문자열로 바꿔 (${...})() 형태로 응답에 실어 보냅니다. 서버에서 작성한 코드가 브라우저에서 실행되는데, 그 파일만큼은 빌드 단계가 필요 없습니다. 영리한(때론 논쟁적인) 코드 배송 기법입니다.
에이전트의 poll은 사람이 코멘트를 달 때까지 무한정 기다려야 합니다. WebSocket을 쓰지 않고, 대신 HTTP 응답을 열어 둔 채 공백 바이트를 찔끔찔끔 흘려보내(heartbeat) 프록시·방화벽이 연결을 끊지 못하게 합니다. 사람이 코멘트를 보내는 순간 그 응답에 JSON을 담아 닫습니다. WebSocket 없이 "기다림"을 구현하는 실전 패턴입니다.
모든 핵심 로직이 src/ 한 폴더에 모여 있습니다. 파일 하나가 책임 하나를 가지는, 읽기 쉬운 구조입니다.
| 파일 | 역할 |
|---|---|
| cli.js | 명령 디스패처(610줄). open/poll/end/stop/playbook/design/setup/server 처리. 서버 버전 핸드셰이크(/health), 백그라운드 서버 spawn, 에이전트용 홈 출력 생성 |
| server.js | Express 서버(620줄). 세션 API · 롱폴링 · SSE · chrome 페이지 · 아티팩트 서빙 · 유휴 자동종료 · presence 상태머신 · live-reload |
| artifact-sdk.js | 아티팩트 iframe에 주입되는 주석 엔진(398줄). 요소 호버 하이라이트, 클릭/텍스트선택 → 코멘트, CSS 셀렉터 생성, DOM 스냅샷 |
| session-store.js | 모든 세션을 state.json 하나에 저장. 경로 sha256 = 세션 키(162줄) |
| chrome-client.js | 부모 프레임(에디터 UI)의 브라우저 측 로직(290줄). 코멘트 큐 · 채팅 로그 · presence 배너 · SSE 수신 |
| html-transform.js | 딱 7줄. injectLavishSdk() — </body> 앞에 SDK 스크립트 1줄만 추가. 휴대성의 핵심 |
| skill.js | createHomeOutput()에서 설치형 SKILL.md를 생성. CLI 홈·훅·스킬이 한 소스에서 나옴 |
| playbooks.js | 7개 시각화 플레이북(diagram·table·comparison·plan·code·input·slides). "배터리 포함" 디자인 가이드(216줄) |
| telemetry.js | 익명 Umami 분석. 비활성 시 Noop, 모든 에러 삼킴 — "도구 동작에 절대 영향 없게" |
| paths.js | 경로 헬퍼: 상태 디렉토리(~/.lavish-axi), 기본 포트(4387), 루프백 호스트 |
| 경로 | 역할 |
|---|---|
| bin/lavish-axi.js | CLI 엔트리포인트(dist/cli.mjs로 번들됨) |
| skills/lavish/ | 생성된 설치형 스킬(SKILL.md). 에이전트에게 "언제·어떻게 lavish-axi를 쓸지" 알려 줌 |
| .agents/skills/, .claude/skills/ | 디자인 시스템 스킬(lavish-design)과 기여 게이트 스킬(no-mistakes) 번들 |
| lavish-editor-marketing/ | README 상단 데모 GIF의 원본 — HyperFrames(HTML로 영상 코딩)로 만든 마케팅 영상 프로젝트 |
| test/ | node:test 테스트 8개. server.test.js 한 파일만 1,483줄 |
| scripts/ | build.js(esbuild 번들), build-skill.js(스킬 생성·드리프트 검사) |
server.js는 WebSocket 없이도 양방향 실시간을 구현하는 교본입니다. SSE(서버→브라우저 푸시)와 롱폴링(에이전트가 무한정 기다림)을 조합했습니다.
믿을 수 없는 코드를 안전하게 실행하는 핵심은 sandbox 속성과 postMessage입니다. allow-same-origin을 빼면 격리가 완성됩니다.
불투명한 ID 대신 내용/경로의 해시를 키로 쓰는 발상. 멱등성과 중복 제거가 공짜로 따라옵니다.
createHomeOutput() 하나가 CLI 홈 출력, SessionStart 훅, SKILL.md를 동시에 만들어 냅니다. CI(build-skill --check)가 드리프트를 막습니다.
MCP의 대안으로, CLI 출력 안에 "다음에 뭘 하라"는 지시(next_step)를 넣어 에이전트를 루프로 끌고 가는 발상. 자기문서화 CLI의 사례입니다.
요구사항이 매우 가볍습니다. 무거운 GPU·DB·컨테이너가 전혀 없습니다.
| 항목 | 요구 |
|---|---|
| 런타임 | Node.js 22 이상 (그 외 의존성 없음) |
| 설치 | 제로셋업 npx lavish-axi <파일> 또는 npm i -g lavish-axi |
| 포트 | 로컬 127.0.0.1:4387 (LAVISH_AXI_PORT로 변경) |
| OS | macOS · Linux · Windows (CI에서 셋 다 테스트). 서버 종료 시 lsof 활용 |
| 브라우저 | 아무 최신 브라우저 (UI는 바닐라 HTML/JS) |
| 상태 저장 | ~/.lavish-axi/state.json (LAVISH_AXI_STATE_DIR로 변경) |
CLI가 필요할 때 백그라운드 서버를 detached로 띄우고, 활성 연결(SSE·poll)이 없으면 기본 30분 후 스스로 종료한다(LAVISH_AXI_IDLE_TIMEOUT_MS). 사용자가 프로세스 관리를 신경 쓸 필요가 없다.
HTML 파일 하나를 만들고 npx lavish-axi index.html로 열어, 브라우저에서 문단을 클릭해 코멘트를 달아 보세요. 그 다음 다른 터미널에서 npx lavish-axi poll index.html을 실행해 방금 단 코멘트가 JSON으로 돌아오는지 확인합니다. lavish-axi playbook으로 내장 시각화 가이드도 출력해 보세요.
브라우저 개발자도구로 페이지 소스를 보고, </body> 앞에 어떤 스크립트가 한 줄 끼워졌는지 찾으세요. 그게 html-transform.js가 한 일의 전부입니다. 원본 파일을 직접 열었을 때와 비교해 "휴대성"을 체감해 보세요.
Express로 /poll(롱폴링)과 /comment(POST)만 가진 50줄짜리 서버를 만들어, 브라우저에서 보낸 텍스트가 대기 중인 poll 응답으로 흘러가게 하세요. lavish의 핵심 루프를 최소 재현하는 과제입니다.
sandbox iframe 안에서 클릭한 요소의 CSS 셀렉터를 계산해 부모 창으로 postMessage하는 스크립트를 작성하세요. artifact-sdk.js의 selector() 로직을 참고해 "고유 셀렉터"를 만드는 법을 익힙니다.
axi-sdk-js 또는 직접 만든 래퍼로, 출력에 next_step 지시를 담는 CLI 도구를 설계하세요. 예: "스크린샷을 찍어 분석 → 다음 명령 안내". 에이전트가 설정 없이 그 CLI를 다단계로 따라오게 만드는 것이 목표입니다.
detached 프로세스로 서버를 띄우고, /health 버전 핸드셰이크 + 유휴 타이머 기반 자동종료 + PID로 강제종료(fallback)를 구현하세요. cli.js의 ensureServer()가 좋은 참고 자료입니다.
| 주차 | 주제 | 학습 내용 |
|---|---|---|
| 1주차 | Node 기본기 | ESM 모듈, node:test 내장 러너, fs/path, child_process(detached spawn) |
| 2주차 | Express 5 | 라우팅·미들웨어·정적 서빙, 그리고 SSE(EventSource)로 실시간 푸시 |
| 3주차 | 롱폴링 & 비동기 통신 | 응답을 열어 두는 패턴, 하트비트, WebSocket과의 비교·선택 기준 |
| 4주차 | 브라우저 보안 | iframe sandbox, postMessage, same-origin 정책, CSP의 기초 |
| 5주차 | DOM 정밀 조작 | 고유 CSS 셀렉터 생성, Selection/Range API, Shadow DOM |
| 6주차 | 에이전트 연동 | MCP vs AXI(CLI) 비교, Claude Code SessionStart 훅, 토큰 효율 출력(TOON) 설계 |
| 7주차 | OSS 운영 | esbuild 번들링, release-please 자동 릴리스, 생성 파일 드리프트 검사 CI |
poll이 사람 코멘트를 무한정 대기할 때 쓴다.waiting(아직 poll 안 함) → listening(poll 대기 중) → working(피드백 받아 작업 중)으로 추적해 사람에게 배너로 보여 주는 모델.