react-dev-bot
v1.5.8
Published
Vite/Next.js 플러그인 — 브라우저에서 Claude Code와 대화 + 컴포넌트 인스펙터.
Downloads
7,274
Maintainers
Readme
react-dev-bot
Vite / Next.js 플러그인 — 브라우저에서 AI(Claude Code) 대화·스크린샷·클릭 히스토리 수집 + 에디터↔브라우저 컴포넌트 인스펙터.
설치
pnpm add -D react-dev-bot
# 또는 npm i -D react-dev-bot / yarn add -D react-dev-bot사전 조건:
- Claude Code CLI가 로컬에 설치되어 있어야 함 (
claude커맨드가 PATH에 있어야 함) - Vite 5+ 또는 Next.js 14+ (App Router)
- 컴포넌트 인스펙터 원본 경로 해석은 현재 Next.js 16 전용 (Turbopack/Webpack 모두). Next 13–15에서는 번들 경로가 표시될 수 있음 — 추후 지원 예정.
Vite 설치
vite.config.ts:
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react-swc";
import { devBot, inspectorCursorServer } from "react-dev-bot/vite";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
const locatorEnabled = env.VITE_LOCATOR === "true";
if (env.REACT_EDITOR) process.env.REACT_EDITOR = env.REACT_EDITOR;
return {
build: {
sourcemap: locatorEnabled ? "hidden" : false,
...(locatorEnabled && {
rollupOptions: { output: { sourcemapExcludeSources: true } },
}),
},
plugins: [
...(locatorEnabled ? [inspectorCursorServer()] : []),
...(mode === "development" ? [devBot()] : []),
react(),
],
};
});하위 호환:
import { devBot } from "react-dev-bot"도 계속 동작합니다.
Next.js 설치 (App Router)
1) API 라우트 (catch-all):
// app/api/dev/[...path]/route.ts
export { GET, POST } from "react-dev-bot/next/route";/api/dev/bot, /api/dev/sessions, /api/dev/reload, /api/dev/inspector/open 등을 한 번에 처리합니다.
2) 루트 레이아웃에 <DevBot/> 마운트:
// app/layout.tsx
import { DevBot } from "react-dev-bot/next";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<DevBot />
{children}
</body>
</html>
);
}<DevBot/>는 process.env.NODE_ENV === "development"에서만 활성화됩니다. 프로덕션에선 null 리턴.
3) (선택) 커스텀 경로:
<DevBot basePath="/api/my-dev" />라우트 파일 위치도 app/api/my-dev/[...path]/route.ts로 맞추면 됩니다.
4) (권장) global-error.tsx — SSR throw 시에도 DevBot 살리기:
// app/global-error.tsx
"use client";
export { default } from "react-dev-bot/next/global-error";page/route handler 의 throw 로 루트 레이아웃이 날아가는 상황에서도 DevBot 마운트를 유지해 narrative 를 뽑을 수 있게 합니다. 커스텀 UI가 필요하면 직접 global-error.tsx 를 만들고 안에 <DevBot /> 만 넣으면 됩니다.
5) (강력 권장) <DevBotErrorBoundary> — React render 에러 자동 리포트:
React render 에러는 window.error로 올라오지 않습니다. 바운더리가 없으면 트리 전체가 언마운트되고(DevBot UI 포함), 유저 앱에 이미 바운더리가 있어도 dev-bot 은 console.error 훅으로 부분적으로만 수거합니다 — DevBotErrorBoundary를 앞단에 두면 React 컴포넌트 스택까지 포함해 narrative 에 정확히 들어갑니다.
// app/layout.tsx
import { DevBot, DevBotErrorBoundary } from "react-dev-bot/next";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<DevBotErrorBoundary>{children}</DevBotErrorBoundary>
<DevBot />
</body>
</html>
);
}componentDidCatch 에서 reportReactError 를 자동 호출해 React render 단계 에러도 narrative 에 포함됩니다. 개발 환경에서만 동작 (force prop 으로 prod 활성 가능).
환경변수
.env.development 예시:
VITE_LOCATOR=true
REACT_EDITOR=cursor
VITE_INSPECTOR_SSH_HOST=myhost # SSH remote 환경만
VITE_INSPECTOR_ROOT=/home/user/src # 프로젝트 절대경로(SSH 환경)| 변수 | 역할 |
|---|---|
| VITE_LOCATOR | (Vite) true면 인스펙터 + sourcemap 생성 |
| REACT_EDITOR | Alt+클릭 시 열 에디터 CLI (cursor, code 등) |
| VITE_INSPECTOR_SSH_HOST | SSH remote 환경에서 딥링크 호스트 |
| VITE_INSPECTOR_ROOT | SSH remote 환경에서 프로젝트 절대경로 |
dev 서버 프로세스에서 인식하는 추가 환경변수:
| 변수 | 역할 |
|---|---|
| DEVBOT_TOKEN | HTTP/MCP 엔드포인트 Bearer 토큰. 미설정 시 부팅 시점에 랜덤 UUID 생성 후 stderr에 출력 |
| DEVBOT_REQUIRE_TOKEN | 1이면 Origin 기반 same-origin fallback을 끄고 토큰을 강제 |
| DEVBOT_BUFFER_SIZE | 이벤트 링버퍼 용량 (기본 500) |
| DEVBOT_LOG_BUFFER_SIZE | raw stdout/stderr 로그 링버퍼 용량 (기본 2000줄) |
| DEVBOT_BLOB_MAX_BYTES | 단일 blob 최대 크기 (기본 64MB) |
| DEVBOT_BLOB_TTL_MS | blob 보존 시간 (기본 10분) |
기능
1. AI 채팅 패널
페이지 우하단의 🔧 버튼으로 Claude와 대화. 자동 수집되는 정보:
- 현재 화면 스크린샷 (html2canvas)
- 최근 클릭 히스토리 + 각 클릭의 컴포넌트 스택
- JS 런타임 에러 / (Vite) 빌드 에러 자동 감지
- 과거 세션 재개 — 로컬에 저장된 Claude Code 세션 파일 자동 검출
- 서브에이전트 위임 (v1.1.5+) —
Task/TodoWrite툴이 허용돼 복잡한 작업을 fresh-context 서브에이전트로 나눠 처리 가능
2. 컴포넌트 인스펙터
- Alt + 클릭: 클릭한 컴포넌트의 JSX 선언 위치를 에디터에서 열기
- 🎯 버튼: 컴포넌트 피커 모드 — 요소를 선택해 컴포넌트 스택을 채팅창에 삽입
- (Vite only) 에디터 커서 ↔ 브라우저 하이라이트:
inspectorCursorServer플러그인이 활성화된 경우에만 지원 (Next.js는 별도 HMR 채널 없음)
Next.js 버전 주의: 원본 경로 해석은 Next 16에서만 검증됨. Next 16의
/__nextjs_original-stack-framesAPI 포맷({frames, isServer, ...},line1/column1,file://${distDir}/...)에 맞춰져 있어, 13–15에서는 바디 포맷 차이로 500이 반환되고 번들 경로로 표시됩니다. 구버전 지원은 추후 추가 예정.
3. dev_page_outline — 페이지 기능 구조 조회 (MCP)
Claude가 dev_page_outline을 호출하면 현재 열린 브라우저 탭의 accessibility tree + React 컴포넌트 이름 + 이벤트 핸들러 플래그 합본을 텍스트 outline으로 반환.
main
├─ form [onSubmit] @LoginForm (src/auth/LoginForm.tsx:12)
│ ├─ textbox "Email" required empty @EmailInput
│ ├─ textbox "Password" required empty @PasswordInput
│ └─ button "Sign in" disabled @LoginSubmitButton
└─ generic "Settings" [onClick] @SettingsButton용도:
- "이 화면에 뭐가 있나 / 어떤 요소가 클릭 가능한가" 질문을 스크린샷 없이 해결 — 토큰 효율.
<div onClick>같이 DOM 노드엔 리스너가 없지만 React props엔 있는 핸들러를 fibermemoizedProps경로로 복구 (Playwright MCP/CDP가 구조적으로 못 잡는 정보).- 각 노드의 소스 컴포넌트 이름 → Claude가 수정 대상 파일 추론.
파라미터: rootSelector / depth / includeHidden / maxNodes (기본 2000) / maxTextBytes (기본 64KB).
제약:
- 연결된 브라우저 탭이 없으면 "no browser connected" 에러 — dev 서버 띄우고 탭 열어둔 상태여야 함.
- Accessibility tree는 80% 휴리스틱 (정식 WAI-ARIA 스펙 완전 준수 아님).
- 놓치는 핸들러 패턴: 직접
useEffect + ref.current.addEventListener기반 커스텀 훅. Radix/React Aria/Floating UI/dnd-kit/Framer Motion은 모두 커버. - Shadow DOM / iframe / Server Component 트리 제외.
4. Edit-verify 루프 닫기 (MCP, v1.1+)
Claude가 코드 수정 → "내 수정이 뭘 만들었나" 확인하는 자율 사이클을 위한 3개 도구:
1) cp = dev_checkpoint() // 수정 직전 시점 cursor
2) [코드 edit]
3) dev_wait_for_change() // 다음 HMR/compile 이벤트까지 대기
4) dev_events_since(cp, severity) // 내가 만든 변화만 isolate핵심 의의: dev_events에 직접 sinceMono 넘기는 것을 id 기반 cursor로 추상화 — Claude가 timestamp 직접 관리하지 않고 "이 시점부터" 질문을 안전하게 만듦.
중요한 한계: dev_wait_for_change는 syntactic verify만 가능 (컴파일·HMR 성공). "의도한 동작 동작" 같은 semantic 정합성은 별도 검증 필요. 응답에 warning 필드 자동 포함.
관련 도구:
dev_event(id)응답에curl·harEntry자동 (network 이벤트일 때) — 실패 요청 즉시 재현dev_events(format="har")— 여러 network 이벤트를 HAR 1.2 archive로 export (Playwright/proxy 도구 import 용)
5. 프로덕션/preview에서 인스펙터 사용 (Vite)
React _debugStack은 개발 모드에서만 생성되므로 일반 빌드에선 동작하지 않습니다. 권장 방식: devBot({ useDevReact: true }) 옵션 — Vite mode 는 production 유지(minify/treeshake 정상)하면서 React 만 dev 번들로 해석. 자세한 설정/비용은 #useDevReact 원리 / 비용 섹션 참고.
정보 카탈로그 — Push vs Pull
dev-bot이 수집하는 정보를 Claude에게 전달하는 두 경로:
- Push (🔧 버튼): 유저가 버튼을 눌러 메시지를 보낼 때 자동 첨부되는 컨텍스트.
- Pull (MCP): Claude 인스턴스가 필요할 때 스스로 호출하는 도구.
runBotStream이claude --mcp-config로 자동 주입 — Claude가 참조하는 mcp-config 키 이름은dev-bot(MCPinitialize응답의serverInfo.name은react-dev-bot).
| 정보 유형 | 수집 위치 | Push (🔧 자동첨부) | Pull (MCP 도구) | 관련 HTTP |
|---|---|---|---|---|
| 페이지 기능 outline (a11y tree + 컴포넌트 이름 + onClick 플래그) | 브라우저 | ❌ | dev_page_outline() | POST /dev/browser-rpc (SSE) |
| 현재 화면 스크린샷 | 브라우저 (html2canvas) | ✅ | ❌ (Phase C 예정) | POST /dev/blob |
| 클릭 히스토리 + 컴포넌트 스택 | 브라우저 | ✅ | ❌ (Phase C 예정) | — |
| 브라우저 런타임 에러 (window.error, unhandledrejection, Error Boundary) | 브라우저 reporter | ✅ (narrative에 포함) | dev_events(source="browser-runtime") | GET /dev/events |
| 브라우저 console 로그 | 브라우저 reporter | ⚠️ 최근 에러 윈도우 내만 | dev_events(source="console") | GET /dev/events |
| 컴파일 에러 (Vite/Next) | dev 서버 훅 | ✅ | dev_events(source="compile") | GET /dev/events |
| SSR throw / Route handler 에러 | console.error 훅 | ✅ | dev_events(source="ssr") | GET /dev/events |
| Dev 서버 stderr (에러 패턴만) | process stderr 훅 | ✅ | dev_events(source="dev-server") | GET /dev/events |
| Dev 서버 raw stdout/stderr 전체 | process stdout/stderr 훅 | ❌ (narrative 노이즈 방지) | dev_logs() | GET /dev/logs |
| HMR 업데이트/전체리로드 | Vite HMR 훅 | ⚠️ 최근 윈도우 내만 | dev_events(source="hmr") | GET /dev/events |
| 네트워크 요청/응답 | 브라우저 reporter | ⚠️ 최근 윈도우 내만 | dev_events(source="network") | GET /dev/events |
| 에러 단건 전체 페이로드 (stack·meta·blobIds) | 링버퍼 byId 인덱스 | 🔧 버튼 대화 중 참조 가능 | dev_event({id}) | GET /dev/event/:id |
| 에러 중심 타임라인 + markdown | 링버퍼 윈도우 | ✅ (🔧 버튼이 이걸로 구성) | dev_narrative({errorId?, windowMs?}) — errorId 생략 시 최근 error 자동 탐색. 응답에 uncertaintyWarning 포함 | GET /dev/narrative/:id, GET /dev/narrative/recent |
| 시점 cursor (체크포인트) | 메모리 Map | ❌ | dev_checkpoint(label?) → {id, mono, wall} | — |
| 체크포인트 이후 이벤트 | 링버퍼 since 쿼리 | ❌ | dev_events_since(checkpointId) | — |
| HMR/컴파일 다음 이벤트 대기 | event-buffer subscriber | ❌ | dev_wait_for_change(timeoutMs?) | — |
| 네트워크 → curl/HAR 변환 | network-export | 🔧 dev_event 응답 필드 | dev_event(id) 응답에 curl·harEntry 자동, dev_events(format="har") bulk | — |
| 컴포넌트 트리 / props / state | — | ❌ (Phase C) | ❌ (Phase C) | — |
| 라우트 인벤토리 | — | ❌ (Phase C) | ❌ (Phase C) | — |
| Runtime env 스냅샷 | — | ❌ (Phase C) | ❌ (Phase C) | — |
요약
- Push는 "유저가 보고 있는 순간의 맥락"(스크린샷·클릭·최근 에러 narrative)에 특화 — 정보 선별·토큰 효율 우선.
- Pull은 "Claude가 필요할 때 조회" —
dev_logs는 특히 raw 출력이라 push에 안 들어감. 모호한 질문("왜 이래", "화면이 안 떠")에 Claude가 자발적으로dev_narrative호출해 맥락 파악.
MCP 통합 — 동작 방식
runBotStream이 claude CLI를 spawn할 때 자동으로 수행:
- 현재 dev 서버 포트에서 MCP 엔드포인트 URL 계산 (예:
http://127.0.0.1:3000/api/dev/__mcp). - 임시 디렉터리에
mcp-config.json작성 — Bearer 토큰(DEVBOT_TOKEN또는 자동 생성)을Authorization헤더로 포함. --mcp-config <temp-path>를 CLI 인자에 추가.- 시스템 프롬프트에
dev_logs/dev_events/dev_event/dev_narrative/dev_checkpoint/dev_events_since/dev_wait_for_change/dev_page_outline사용법 안내.
인증: MCP 엔드포인트는 일반 HTTP 엔드포인트와 같은 DEVBOT_TOKEN 인증을 공유. Origin 검증으로 DNS rebinding 기본 방어. 배포/원격 시나리오에선 DEVBOT_REQUIRE_TOKEN=1로 토큰 필수화.
지원 메서드: initialize, tools/list, tools/call, 그리고 standard notifications. 리소스/프롬프트/스트리밍·샘플링은 미지원 (Phase B 후속).
엔드포인트 레퍼런스
<DevBot/>와 devBot()이 등록하는 경로:
| 경로 (Vite 기본) | 경로 (Next.js 기본) | 메서드 | 역할 |
|---|---|---|---|
| /dev/bot | /api/dev/bot | POST | Claude 채팅 (SSE) |
| /dev/__mcp | /api/dev/__mcp | POST | MCP JSON-RPC 엔드포인트 |
| /dev/logs | /api/dev/logs | GET | raw stdout/stderr 덤프 |
| /dev/events | /api/dev/events | GET/POST | 이벤트 링버퍼 조회/수집 |
| /dev/event | /api/dev/event | POST | 단건 이벤트 수집 |
| /dev/event/:id | /api/dev/event/:id | GET | 단건 이벤트 상세 |
| /dev/narrative/:id | /api/dev/narrative/:id | GET | 에러 중심 narrative |
| /dev/narrative/recent | /api/dev/narrative/recent | GET | 최근 윈도우 narrative |
| /dev/browser-rpc | /api/dev/browser-rpc | GET (SSE) | 서버→브라우저 RPC (dev_page_outline 등) |
| /dev/browser-rpc/result | /api/dev/browser-rpc/result | POST | 브라우저 RPC 결과 반환 |
| /dev/blob | /api/dev/blob | POST | blob 업로드 |
| /dev/blob/:id | /api/dev/blob/:id | GET | blob 조회 |
| /dev/handshake | /api/dev/handshake | GET | 토큰 핸드셰이크 |
| /dev/reload | /api/dev/reload | GET/POST | 브라우저 reload SSE |
| /dev/sessions | /api/dev/sessions | GET | 세션 목록 |
| /dev/session/:id | /api/dev/session/:id | GET | 세션 메시지 |
| /dev/restart | /api/dev/restart | POST | Vite dev 재시작 (Next.js: no-op) |
| /__inspector/open | /api/dev/inspector/open | POST | 에디터에서 파일 열기 |
| — | /api/dev/dist-dir | GET | (Next.js only) 인스펙터용 dist 경로 조회 |
쿼리 파라미터:
/dev/logs?limit=500&stream=stdout|stderr&sinceMono=.../dev/events?limit=100&source=...&minSeverity=error&sinceMono=.../dev/narrative/recent?windowMs=10000
Claude가 브라우저를 새로고침하도록 요청할 때:
curl -s -X POST http://localhost:5173/dev/reload # Vite
curl -s -X POST http://localhost:3000/api/dev/reload # Next.js보안 (preview/외부 배포)
sourcesContent: false(위 Vite config에 포함) —.map파일에 원본 코드 미포함hiddensourcemap — 브라우저 DevTools 자동 로드 안 됨, 인스펙터만 명시적 fetch- 어드민 패널이 인증 뒤에 있다면
.map파일도 같은 인증으로 보호됨 - 정적 파일을 공개 CDN으로 서빙한다면
.map차단 필요 (인스펙터도 차단되므로 프록시 엔드포인트 구현)
원리
- React dev 모드에서 모든 JSX element에
_debugStack = new Error()부여 - 브라우저에서 DOM 요소의
__reactFiber$로 fiber 접근 - fiber chain을 walk하며
_debugStack에서 JSX 호출 위치 추출 - sourcemap으로 번들 줄번호 → 원본 TS 줄번호 역추적
React 19의 formatOwnerStack()에 의해 _debugStack이 string으로 교체되는 경우도 처리합니다.
배포 환경 (prod 빌드) 모드 — Phase E
dev 모드와 별도로, build & deploy 된 환경에서도 동작하는 standalone 리포터가 있습니다. vite preview, next start, 사내 dogfooding 용 develop 배포 등.
설치
pnpm add -D react-dev-bot
# 또는: npm i -D react-dev-bot / yarn add -D react-dev-bot빠른 시작 (Vite — 사내 develop 배포)
1) 빌드 환경변수 분리 — 진짜 production 에는 react-dev-bot 코드를 한 바이트도 넣지 않는 방식 (권장):
// app/entry.tsx
if (import.meta.env.VITE_DEVBOT_DEPLOY === "develop") {
const { initDevBotProd } = await import("react-dev-bot/prod");
initDevBotProd(); // ← v1.5.0 부터 명시적 호출 필요. 옵션 없으면 기본값.
}VITE_DEVBOT_DEPLOY=develop pnpm build # 사내 dogfooding 빌드 (react-dev-bot 동봉)
pnpm build # 진짜 production (react-dev-bot 0KB)2) Vite 플러그인 설정 — env 게이팅을 함께 적용:
// vite.config.ts
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";
import { devBot } from "react-dev-bot/vite";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
const devbotDeploy = env.VITE_DEVBOT_DEPLOY === "develop";
return {
plugins: [
react(),
// dev 모드는 늘 활성, build 모드에선 develop 배포일 때만 useDevReact 켬.
...(mode === "development" || devbotDeploy
? [devBot({ useDevReact: devbotDeploy })]
: []),
],
build: {
// 진짜 prod 에 .map 안 들어가게 게이팅. 'hidden' + sourcesContent: false 로
// .map 만 동봉하고 원본 소스는 노출 안 함 (Stack 해석은 가능).
sourcemap: devbotDeploy ? "hidden" : false,
...(devbotDeploy && {
rollupOptions: { output: { sourcemapExcludeSources: true } },
}),
},
};
});왜 조건부인지: useDevReact: true 고정으로 두면 진짜 prod 빌드에도 React dev 번들이 들어가 ~80KB gzipped 낭비 (React 19 react-dom 의 dev↔prod 차이 측정값). sourcemap: true 고정도 .map 이 prod 산물에 동봉돼 노출. 둘 다 devbotDeploy 게이팅이 안전.
이렇게 빌드한 develop 배포에서는:
- 모든 에러/console/network/click 이벤트가 IndexedDB 에 자동 영속 (기본 5000개 ring)
- 우하단 📋 버튼 → 패널 (목록 / 상세 / 전체 timeline / 전체 dump / 제보하기)
- 직원이 "제보하기" 클릭 → 문제 요소 짚기 → 코멘트 → 전송 (sendUrl 설정 시) 또는 IDB 만 저장
빠른 시작 (Next 16)
// app/devbot-loader.tsx
"use client";
import { useEffect } from "react";
export function DevBotLoader() {
useEffect(() => {
if (process.env.NEXT_PUBLIC_DEVBOT_DEPLOY !== "develop") return;
void import("react-dev-bot/prod").then(({ initDevBotProd }) => {
initDevBotProd(); // ← v1.5.0 부터 명시적 호출 필요
});
}, []);
return null;
}
// app/layout.tsx
import { DevBotLoader } from "./devbot-loader";
// ...
<body><DevBotLoader />{children}</body>// next.config.ts
export default { productionBrowserSourceMaps: true };NEXT_PUBLIC_DEVBOT_DEPLOY=develop pnpm build # 사내
pnpm build # 진짜 productioninitDevBotProd 옵션
v1.5.0 breaking change: side-effect import (
import "react-dev-bot/prod") 만으로 자동 init 하던 동작 제거. ESM 평가 순서상 사용자의 옵션 설정이 무시되는 버그가 있었음. 이제 반드시 명시 호출:
import { initDevBotProd } from "react-dev-bot/prod";
initDevBotProd({
enabled: import.meta.env.VITE_DEVBOT_DEPLOY === "develop", // 런타임 kill switch
capture: true, // IDB 영속 (기본 true)
viewer: true, // 📋 floating 패널 (기본 true)
clicks: true, // 클릭 트래커 (기본 true)
captureScreenshots: false, // 클릭마다 스크린샷 (기본 false, opt-in — webhook payload 에 인터리브)
maxEvents: 5000, // IDB ring 상한 (기본 5000)
sendUrl: "https://example.com/devbot/ingest", // 제보 전송 endpoint (선택, 단일 sender 단축형)
});sendUrl 미설정 시 제보가 IDB 의 user-report event 로만 저장 — 직원이 뷰어에서 JSON 다운로드 해서 수동 공유.
여러 sender 등록 — prodSenders 네임스페이스
webhook 한 곳뿐 아니라 GitHub Issue prefill / mailto / 자체 sender 까지 동시에 노출하려면 senders 옵션:
import { initDevBotProd, prodSenders } from "react-dev-bot/prod";
initDevBotProd({
captureScreenshots: true,
senders: [
prodSenders.webhook({
url: "https://devbot-receiver.example.com/devbot",
label: "🚀 Claude 분석",
// (선택) HMAC 서명 — receiver 가 secret 으로 검증해 비악의적 요청 필터링.
// ⚠️ secret 이 client bundle 에 포함됨 — anti-spam 용도, 진짜 인증 X.
hmac: { secret: import.meta.env.VITE_DEVBOT_WEBHOOK_SECRET ?? "" },
}),
prodSenders.githubIssue({ repo: "myorg/myrepo", labels: ["bug", "from-devbot"] }),
prodSenders.mailto({ to: "[email protected]" }),
],
});모달 "전송" 버튼이 등록한 sender 별로 노출. 첫 번째가 primary 색상. 자체 Sender 구현 (Slack worker / 사내 ticketing API 등) 도 같은 인터페이스로 등록 가능 — 자세한 시그니처와 receiver 구현 예시는 docs/webhook-payload.md 참조.
이름의
prodprefix 가 곧 "prod 모드 전용" 임을 알려줌. dev 모드(🔧 Claude 직결)에는 webhook 개념 자체가 없습니다.
빌드 메타 (소비자 앱 git sha / branch)
배포된 산출물에서 "어떤 commit 으로 빌드됐는지" 알면 회귀 추적이 빨라집니다. webhook payload meta.appBuild + window.__devBotProd.appBuild 로 노출.
Vite — 자동 (무설정)
devBot() 플러그인이 빌드 시점에 자동 캡처해 define 주입. 사용자가 따로 설정할 게 없음.
탐지 우선순위:
- CI/PaaS 환경변수 — Vercel(
VERCEL_GIT_COMMIT_SHA/REF), Netlify(COMMIT_REF/BRANCH), Cloudflare Pages(CF_PAGES_*), GitHub Actions(GITHUB_SHA/REF_NAME), Render(RENDER_GIT_*). - 로컬 git —
git rev-parse HEAD+git status --porcelain(dirty flag). - 둘 다 실패 시
builtAt만 +source: "unknown".
확인:
window.__devBotProd.appBuild
// → { sha: "9ee0d52", branch: "main", dirty: false, builtAt: "...", source: "vercel" }Next.js — 수동 주입 (env var → initDevBotProd 옵션)
Next 의 webpack/turbopack 은 우리 vite plugin 의 define 경로가 안 맞아서 직접 주입:
# 빌드 스크립트 (package.json scripts 또는 CI)
NEXT_PUBLIC_DEVBOT_APP_SHA=$(git rev-parse --short HEAD) \
NEXT_PUBLIC_DEVBOT_APP_BRANCH=$(git rev-parse --abbrev-ref HEAD) \
next build// app/devbot-loader.tsx
"use client";
import { useEffect } from "react";
export function DevBotLoader() {
useEffect(() => {
if (process.env.NEXT_PUBLIC_DEVBOT_DEPLOY !== "develop") return;
void import("react-dev-bot/prod").then(({ initDevBotProd }) => {
initDevBotProd({
appBuild: {
sha: process.env.NEXT_PUBLIC_DEVBOT_APP_SHA,
branch: process.env.NEXT_PUBLIC_DEVBOT_APP_BRANCH,
source: "manual",
},
});
});
}, []);
return null;
}Vercel 에서 Next 배포 시 VERCEL_GIT_COMMIT_SHA 가 빌드 환경에 이미 있으니 process.env.VERCEL_GIT_COMMIT_SHA 를 그대로 써도 됨 (단 NEXT_PUBLIC_* 접두 없으면 client bundle 에 안 들어가니, next.config 의 env 로 노출 필요):
// next.config.ts
export default {
env: {
NEXT_PUBLIC_DEVBOT_APP_SHA: process.env.VERCEL_GIT_COMMIT_SHA?.slice(0, 7),
NEXT_PUBLIC_DEVBOT_APP_BRANCH: process.env.VERCEL_GIT_COMMIT_REF,
},
};옵션 override
자동 감지 결과를 덮고 싶거나 별도 메타를 추가하려면 initDevBotProd({ appBuild }) 옵션이 항상 우선:
initDevBotProd({
appBuild: { sha: "abc1234", branch: "feature/x", source: "manual" },
});Webhook payload (제보 전송 시)
prodSenders.webhook / sendUrl 로 보내지는 본문은 DevBotReport v1 schema. LLM-ready content blocks(text/image 인터리브) + 구조화 events + 라우팅 meta 형태로, receiver 가 받자마자 multimodal LLM(Claude/GPT-4V/Gemini)에 zero-translation 으로 forward 가능.
전체 TypeScript 타입, 샘플 payload, receiver 구현 예시(Hono/Express), HMAC 검증 패턴 등은 별도 문서 참고:
요약:
interface DevBotReport {
schemaVersion: "1";
id: string; // browser-issued UUID
createdAt: string; // ISO 8601
userMessage: string; // 사용자 코멘트
source: { url: string; title?: string; userAgent?: string; viewport?: {...} };
content: ContentBlock[]; // Anthropic Messages API 포맷, chronological text/image 인터리브 (사용자 코멘트+첨부 → 컨텍스트 → 에러 → 타임라인)
events: ReportEvent[]; // 구조화 NormalizedEvent[] + imageIndex
meta?: { devBotVersion?, platform?, env?, clickCount?, consoleErrorCount?, appBuild?: { sha?, branch?, dirty?, builtAt?, source? }, ... };
pickedElement?: PickedElement | null;
}동작 매트릭스 (Vite vs Next prod)
| 기능 | Vite prod | Next 16 prod | 비고 |
|---|---|---|---|
| 에러 자동 캡처 (window.error / unhandledrejection) | ✅ | ✅ | reporter capture phase 등록 |
| 에러 stack → 원본 .tsx:line 해석 | ✅ | ✅ | .map 동봉 필수. Next 는 chunk↔map basename 다름 → sourceMappingURL 주석 fetch 경로 사용 |
| Console / fetch / XHR 캡처 | ✅ | ✅ | reporter 그대로 |
| 클릭 트래커 (tag, text, 좌표, selector) | ✅ | ✅ | DOM 정보만 |
| Intrinsic JSX 위치 (data-devbot-src) | ✅ | ❌ | Vite 플러그인 babel transform. Next 는 turbopack 이라 미적용 |
| 컴포넌트 owner chain | useDevReact: true 시 ✅ | ❌ | React dev 번들 + fiber._debugSource |
| IndexedDB 영속 + view-time 그룹/카운트 | ✅ | ✅ | DB react-dev-bot-prod, 기본 5000 events ring |
| 📋 floating 뷰어 (목록/상세/dump) | ✅ | ✅ | vanilla DOM |
| 제보하기 (인스펙터 + 코멘트 + 전송) | ✅ | ✅ | sendUrl 옵션으로 외부 endpoint POST |
| window.__devBotProd API (events.*, startSendFlow, ...) | ✅ | ✅ | 콘솔 검증 / 외부 도구 통합 |
useDevReact 원리 / 비용
devBot({ useDevReact: true }) 가 빌드 모드일 때 자동으로:
process.env.NODE_ENV='development'강제- Vite
define으로 bundle 안의process.env.NODE_ENV도"development"치환 resolve.conditions: ['development', ...]추가 → react/jsx-dev-runtime 이 실제 jsxDEV 함수 export
Vite mode 자체는 production 유지 (minify/treeshake 정상). React 만 dev 번들로 해석돼 fiber._debugSource 가 채워지고 기존 dev 모드 fiber walker 가 prod 에서 그대로 동작.
비용: 번들 +~80KB gzipped (React 19 react-dom dev↔prod 차이 실측), 런타임 2~3x. 사내 dogfooding 한정, 외부 사용자 prod 에선 끄기.
Next 동등 메커니즘은 turbopack JSX 옵션 조사 필요 (현재 미지원).
Phase E 한계 요약
- 컴포넌트 owner chain 은 Vite +
useDevReact옵션 한정 (Next prod 미지원) - Intrinsic 위치 (
data-devbot-src) 는 Vite 한정 - 운영 안전장치 (PII redact / quota graceful degradation 등) 일부 미구현
production 배포 vs develop 배포 — 환경 분리
진짜 production (외부 사용자) 과 develop 배포 (사내 dogfooding) 를 구분하려면 두 축:
축 1 — 번들 자체 포함 여부 (build-time, 핵심)
react-dev-bot 코드가 진짜 prod 번들에 0바이트 들어가게 하려면 conditional dynamic import:
// app/entry.ts
if (import.meta.env.VITE_DEVBOT_DEPLOY === "develop") {
const { initDevBotProd } = await import("react-dev-bot/prod"); // develop 배포만 번들 포함
initDevBotProd();
}빌드 명령:
VITE_DEVBOT_DEPLOY=develop pnpm build # 사내 dogfooding 용
pnpm build # 진짜 production — react-dev-bot 0 KBNext.js 도 동일 패턴 — process.env.NEXT_PUBLIC_DEVBOT_DEPLOY === "develop" 같이.
축 2 — 포함됐을 때 feature 토글 (runtime)
dynamic import 가 어려운 환경 (CSP / 구식 번들러) 또는 직원이 인스턴스별로 끄고 싶을 때:
import { initDevBotProd } from "react-dev-bot/prod";
initDevBotProd({
enabled: import.meta.env.VITE_DEVBOT_DEPLOY === "develop",
// false 면 모든 install 스킵, window.__devBotProd 도 노출 안 함.
});주의: 이 경로는 코드는 번들에 들어감 — react-dev-bot/prod entry 자체 ~56KB gzipped (실측), useDevReact 추가 시 +80KB. 진짜 prod 에서 0KB 원하면 축 1 필수.
옵션 조합 가이드
| 시나리오 | 축 1 | 축 2 옵션 |
|---|---|---|
| 사내 develop 배포 (직원 dogfooding) | dynamic import 활성 | 기본값 그대로 (모든 feature on) |
| 진짜 production | dynamic import 비활성 | n/a (번들 0) |
| Sentry-like 가벼운 prod 캡처 | 활성 | { viewer: false, clicks: false } 로 에러 캡처만 |
| 일시 비활성 (디버깅 / opt-out) | 활성 | { enabled: false } |
dev / prod 저장소 분리 — 의도적 split
| | dev | prod |
|---|---|---|
| In-memory ringbuffer | server-side core/event-buffer.ts (Node 메모리) | browser-side 같은 모듈 (브라우저 메모리) |
| 영속 layer | 옵션 (devBot({ persistEvents: true }) 시 IDB 추가) | 기본 ON — IDB ring (기본 5000개) |
| MCP/Claude 접근 | server memory 에 zero-latency 직접 query | n/a (외부 ingest 채널로) |
| 외부 전송 | n/a (Claude 가 직접 spawn) | prodSenders.{webhook,githubIssue,mailto} (or sendUrl 단축형) / JSON dump 수동 공유 |
왜 통일 안 했나 — 한쪽으로 통일해 봤더니 잃는 게 더 컸음:
- 양쪽 IDB only 로 가면: dev 의 MCP 도구가 browser IDB 에 접근하려 매번 browser-rpc 우회 (~수백 ms 지연), 또 server 에서 발생하는 이벤트 (
dev-serverstderr / SSR throw / process-capture) 를 IDB 로 자연스레 못 적재. browser tab 이 닫혀 있는 동안엔 데이터 접근 불가. - 양쪽 server memory only 는 prod 에 서버 부재라 불가능 — 사이드카/ingest 서버 추가 인프라 부담.
- 양쪽 IDB + server mirror 는 정합성 관리 복잡, 중복 저장.
대신 API surface 는 양쪽 동일 — 핵심 함수가 core/narrative.ts 의 buildNarrative 하나로 묶여 있고, 데이터 스키마 (core/events.ts) 도 단일. 즉 narrative markdown / JSON export 형식은 어디서든 동일 shape. 어떤 환경에서 만든 dump 든 같은 ingest 채널로 흘려보낼 수 있음.
옵션 정리:
devBot({ persistEvents: true, maxPersistedEvents: 5000 })— dev 도 IDB 영속 켜기 (서버 재시작/브라우저 종료 너머 보존)initDevBotProd({ sendUrl: "https://..." })— prod 의 "제보하기" 가 narrative+코멘트+element 를 POST (단일 sender 단축형)initDevBotProd({ senders: [prodSenders.webhook(...), prodSenders.githubIssue(...), ...] })— 멀티 sender 등록 (모달에 버튼 여러 개)initDevBotProd({ maxEvents: 1000 })— prod IDB ring 줄이기
알려진 한계
dev-bot 이 수집하지 못하는 에러 유형 (설계상 또는 React/Next 구조상):
- Hydration mismatch — React 19는 mismatch 를 recoverable recovery 로 처리 (throw 가 아니라 내부
onRecoverableError콜백).createRoot제어권이 없으면 후킹 불가. dev 모드에서는 Next 오버레이가 서버/클라 값 diff 까지 매우 선명하게 보여주니 그걸로 확인. - 초기 hydration 완료 전 발생한 에러 — reporter 가
useEffect기반이라 hydration 이후에 설치됨. 그 이전 구간의window.error/unhandledrejection은 누락 가능. - Web Worker / Service Worker / iframe 내부 에러 — 별도 실행 context 라 현재 reporter 가 도달 못함.
- try/catch 로 삼킨 에러 — 근본적으로 불가.
- 유저 Error Boundary 가 삼킨 뒤
console.error까지 억제한 에러 — React 는 바운더리가 잡은 에러를console.error로 자동 로그하고 dev-bot 이 console 훅으로 수거하므로 대부분 잡힘. 하지만 유저 바운더리가 로그까지 억제하면(또는 React 의 내부 로깅을 패치로 제거하면) 못 봄. 일반적으로는<DevBotErrorBoundary>마운트 + React 기본 로그 유지로 커버됨. - SSR fetch 실패 (Next 서버 컴포넌트/route handler 의 외부 API 호출) — Node fetch 미패치. Phase D (사이드카) 로 검토했으나 현재 보류.
- Next turbopack 빌드 실패 시 전체
/api/dev/*작동 불능 — 유저 파일 구문 에러 하나로 앱 전체 컴파일 실패 → dev-bot 라우트도 500. 프로세스 분리(Phase D 사이드카) 로 검토했으나 현재 보류.
라이선스
MIT
