오픈소스 · MIT · MCP

AI 에이전트가 사용자 휴대폰 속 WebView를 디버깅해요

H5 페이지나 앱 WebView에 스크립트 하나만 추가하세요. 사용자가 짧은 기기 코드를 알려 주면 Claude Code, Codex, Cursor 등 어떤 MCP 클라이언트든 페이지를 보고, console과 네트워크를 읽고, 직접 탭해 가며 버그를 재현할 수 있어요. 그 과정은 사용자에게 모두 보이고, 사용자는 언제든 직접 조작할 수 있어요.

  • 데이터 케이블 불필요
  • 원격 디버깅 포트 불필요
  • 앱은 스크립트 하나만 주입
  • 버그가 지난 뒤에 접속해도 괜찮아요

01라이브 데모직접 체험

실제 버그를 ‘실제’ 휴대폰에서 잡아내기이번엔 내 에이전트로 해 보세요

데모 속 휴대폰에서 돌아가는 건 데모 쇼핑몰에 들어간 진짜 페이지 SDK예요. 게이트웨이만 브라우저 안에서 모사하니 어떤 데이터도 이 탭을 벗어나지 않아요. 그래도 아래의 스냅샷, 클릭, console 출력, 라이브 패치는 모두 진짜예요.

이건 진짜예요. demo.debugger.site에 공개 데모 게이트웨이가 있고, 다른 모든 것과 분리되어 매일 초기화돼요. 휴대폰 속 쇼핑몰은 거기서 불러온 페이지예요. 키를 받아 에이전트를 연결하고, 바로 여기서 작업하는 모습을 지켜보세요.

준비됨
쇼핑몰이 온라인이 되기를 기다리는 중…
demo.debugger.site

AI 에이전트

rwd MCP 서버에 연결됨

에이전트가 휴대폰에 접속해 결제 버그를 재현하고, 근본 원인을 찾아 그 자리에서 수정을 검증하는 과정을 보세요.

  1. 내 키 받기

    클릭 한 번, 가입은 필요 없어요. 이 키는 데모 게이트웨이에서만 동작하고, 데모가 초기화될 때까지 유효해요.

    키를 받으면 아래 명령에 자동으로 채워져요.

  2. 에이전트에 데모 게이트웨이 추가하기

    터미널에서 명령을 한 번 실행하거나, MCP 클라이언트 설정에 넣으세요. 키는 이미 채워져 있어요.

    terminal
    claude mcp add --transport http rwd-demo https://demo.debugger.site/v1/mcp \
      --header "Authorization: Bearer rwd_sk_live_…"
  3. 에이전트에게 요청하기

    휴대폰 속 쇼핑몰의 기기 코드가 프롬프트에 채워져 있어요. 에이전트가 접속을 요청하면 쇼핑몰에서 허용을 탭하고 작업하는 모습을 보세요. 직접 조작이나 종료는 언제든 가능해요.

    프롬프트
    rwd-demo MCP 서버를 써서 기기 XXXX-XXXX에 접속해. 쇼핑몰에서 상품 하나에 Gift box 포장을 고르고 결제로 넘어가서, 합계가 “--”로 표시되는 이유를 찾아 줘.

    XXXX-XXXX를 쇼핑몰에 표시된 기기 코드로 바꿔 주세요.

데모의 제한

  • 방문자마다 키 하나: 에이전트는 기기 코드를 알려 준 페이지에만, 그것도 허용한 뒤에 접속해요
  • interact 권한만: 스크립트, 요청 본문, 저장소는 쓸 수 없어요
  • 에이전트는 한 페이지를 최대 15분까지 점유해요(유휴 상태면 5분)
  • IP당 분당 MCP 요청 120회
  • 매일 04:00(UTC+8)에 초기화돼요. 실제 개인정보는 입력하지 마세요

고객의 휴대폰K7Q2-9M4X

데모 쇼핑몰, 실시간····-····

쇼핑몰이 온라인이 되기를 기다리는 중…

스캔해서 휴대폰에서 열기
새 탭에서 열기

이게 바로 진짜 SDK가 돌아가고 있는 demo.debugger.site예요. 코드를 스캔하면 휴대폰에서 열 수 있어요.

전부 브라우저 안에서 돌아가요. iframe 안에는 진짜 SDK(sdk.js)가 있고, WebSocket 심이 그 연결을 페이지 안의 모의 게이트웨이로 넘겨 주고, 스크립트화된 에이전트가 실제 RPC를 보내요. 에이전트의 말은 페이지 언어를 따르고, 도구 결과는 게이트웨이 언어(영어 또는 중국어)를 써요.

02작동 방식

“내 휴대폰에서만 안 돼요”에서 근본 원인까지, 세 단계

  1. 페이지 SDK 추가하기

    <head>에 되도록 이른 위치로 스크립트 하나를 넣으면 console과 네트워크가 첫 줄부터 기록돼요. 앱에서는 같은 스크립트를 문서 시작 시점에 주입하세요.

  2. 사용자가 기기 코드를 알려 줘요

    휴대폰마다 K7Q2-9M4X 같은 고정 기기 코드가 작은 배지에 보여요. 사용자가 지원 요청을 탭하면 그 기기가 에이전트 목록 맨 앞으로 올라와요.

  3. 에이전트가 MCP로 접속해요

    API 키와 함께 게이트웨이의 MCP 주소를 에이전트에 추가하세요. 그다음엔 문제를 설명하기만 하면 돼요. 에이전트가 기기를 찾아 승인을 요청하고 작업을 시작해요.

index.html
<!-- In <head>, before your own scripts; pin a version in production -->
<script src="https://cdn.jsdelivr.net/npm/remote-webview-debugger/dist/sdk.js"></script>
<script>
  RemoteWebviewDebugger.run("https://rwd.example.com/v1", "rwd_pk_…", {
    userId: currentUser?.id ?? null, // optional: find devices by user
    helpButton: true,                // show the “Request assistance” button
  })
</script>

프로덕션에서는 버전을 고정하거나, 게이트웨이가 제공하는 /v1/sdk.js를 쓰세요(SRI 값은 콘솔에 있어요).

terminal
claude mcp add --transport http rwd https://rwd.example.com/v1/mcp \
  --header "Authorization: Bearer rwd_sk_live_…"
기기 K7Q2-9M4X에서 결제가 안 돼. 합계가 “--”로 나오는데, 이유를 찾아 줘.

03에이전트가 쓸 수 있는 것

익숙한 DevTools 작업 흐름을, MCP를 통해 남의 휴대폰에서

  • 접근성 스냅샷

    요소 ref가 붙은 Playwright 스타일 YAML이에요. 에이전트는 픽셀이 아니라 구조로 페이지를 읽고, ref로 정확히 조작해요.

  • 진짜 상호작용

    클릭, 채우기, 입력, 키 누르기, 선택, 호버, 스크롤, 드래그까지. 조작 가능 여부를 확인하고, React와 Vue가 받아들이는 이벤트를 발생시켜요.

  • console과 네트워크

    페이지 로드부터 크기 제한이 있는 버퍼에 기록되니, 에이전트가 접속하기 전의 오류도 그대로 남아 있어요. 본문은 필요할 때 가져와요.

  • 여러 페이지 세션

    목록 → 상세 → 결제, 또는 새로 열린 WebView까지. 한 기기의 모든 페이지가 하나의 세션을 이루고, 페이지를 넘나드는 타임라인이 있어요.

  • 변경 사항을 바로 알려 줘요

    모든 도구 결과 끝에 무엇이 바뀌었는지 붙어요. 새 페이지, 페이지 이동, 오류, 사용자의 직접 조작까지요. 폴링은 필요 없어요.

  • 휴대폰 속 작업 카드

    에이전트가 계획과 진행 상황을 기기에 보여 주니, 사용자는 AI가 무엇을 왜 하고 있는지 항상 알 수 있어요.

  • 스크린샷

    앱이 브리지를 제공하면 실제 픽셀이고, 아니면 DOM 재렌더링이에요. 두 경우 모두 마스킹이 적용돼요.

  • 기기에 남는 세션 로그

    앱이 실행된 뒤의 페이지, 탭, console, 모든 요청이 휴대폰의 IndexedDB에 보관돼요. 나중에도 읽을 수 있어요.

  • 라이브 패치

    advanced 권한에서는 에이전트가 evaluate_script로 실행 중인 페이지에 패치를 넣어 그 자리에서 수정을 검증할 수 있어요.

  • 느린 네트워크에서도

    백오프가 붙은 가벼운 하트비트를 쓰고, 세 번 실패하면 1분에 한 번만 존재를 확인해요. 필요할 때는 게이트웨이가 페이지를 깨워요.

  • 11개 언어

    휴대폰 UI, 콘솔, 에이전트용 텍스트를 11개 언어로 제공해요. 오른쪽에서 왼쪽으로 쓰는 아랍어도 있어요. 직접 만든 언어 팩과 문구도 넣을 수 있어요.

  • 어떤 MCP 클라이언트든

    /v1/mcp는 Streamable HTTP라 Claude Code, Codex, Cursor에서 바로 쓸 수 있고, stdio만 지원하는 클라이언트는 아주 작은 브리지를 쓰면 돼요.

전체 도구 34

  • list_devices
  • connect_device
  • wait_for_device
  • list_pages
  • select_page
  • wait_for_page
  • take_snapshot
  • find_elements
  • take_screenshot
  • click
  • fill
  • type_text
  • press_key
  • select_option
  • hover
  • scroll
  • drag
  • batch_actions
  • navigate_page
  • go_back
  • reload_page
  • wait_for
  • list_console_messages
  • list_network_requests
  • get_network_request
  • get_storage
  • evaluate_script
  • set_task
  • get_session_timeline
  • get_session_overview
  • get_session_log
  • wait_for_event
  • wait_for_control
  • disconnect_device

04하이라이트

바깥에서만 생기는 버그를 위해 만들었어요

케이블이나 원격 디버깅 포트, 화면 녹화로는 얻을 수 없는 세 가지예요.

세션 로그 · 기기에 보관

버그가 이미 지나갔나요? 로그는 휴대폰에 남아 있어요.

앱이 실행된 순간부터 SDK는 기기의 IndexedDB에 세션 로그를 써요. 방문한 페이지, 사용자의 탭과 입력, console 출력, 그리고 모든 요청까지요. 실패한 요청은 헤더와 본문도 함께 남아요. 이 데이터는 어디로도 전송되지 않고, 같은 마스킹 규칙 아래 휴대폰에만 머물러요. 사용자가 허용한 에이전트가 get_session_overview와 get_session_log로 요청할 때까지요. 최근 3회 실행분을 각각 최대 10 MB까지 보관해요.

라이브 패치 · evaluate_script

출시 전에, 고객 휴대폰에서 수정을 검증해요.

사용자가 advanced를 허용하면 evaluate_script가 페이지 안에서 실행돼요. 에이전트는 잘못된 상태를 바로잡고, 함수를 덮어쓰고, 화면을 다시 렌더링해서 실제 기기에서 결과를 확인할 수 있어요. 진단을 증명하는 라이브 패치죠. 패치는 그 페이지의 메모리에만 있고 아무것도 저장되지 않아요. 민감한 클릭은 여전히 사용자의 확인이 필요해요.

에이전트 중심 · MCP

DevTools를 든 사람이 아니라, 에이전트를 위해 만들었어요.

모든 도구 결과 끝에는 지난 호출 이후 기기에서 바뀐 것이 붙어서, 에이전트가 폴링할 일이 없어요. 페이지 내용은 신뢰할 수 없는 데이터로 전달돼요. 휴대폰의 작업 카드가 에이전트의 계획을 보여 주고, 게이트웨이는 에이전트의 언어로 말해요. 도구 설명과 결과가 11개 언어로 제공돼요.

05개인정보와 제어

사용자의 휴대폰은 끝까지 사용자의 것이에요

원격 디버깅은 신뢰가 있어야 성립해요. 모든 세션은 눈에 보이고, 허용을 받고, 언제든 끊을 수 있어요.

  • 먼저 허용을 받아요

    에이전트가 접속을 요청하면 사용자가 누가 요청하는지 보고 휴대폰에서 권한 수준을 골라요. 실제 탭만 인정되니, 스크립트가 스스로 허용할 수는 없어요.

  • 언제든 직접 조작하거나 종료해요

    에이전트가 작업하는 동안 화면 테두리가 빛나고 페이지는 잠겨요. 직접 조작을 한 번 탭하면 에이전트가 멈추고, 종료를 탭하면 연결이 끊겨요.

  • 기본값은 마스킹

    비밀번호와 data-rwd-mask가 붙은 내용은 스냅샷, 스크린샷, console, 네트워크 데이터, 세션 로그에서 ••••로 보여요. 프로젝트마다 선택자와 정규식을 더할 수 있어요.

  • 민감한 클릭은 확인을 받아요

    “주문하기”, “계정 삭제” 같은 버튼에 data-rwd-confirm을 붙이면, 에이전트가 그 버튼을 클릭할 때마다 사용자가 확인해요.

  • 권한 수준

    observe는 읽고, interact는 조작하고, advanced는 스크립트, 요청 본문, 저장소까지 더해요. 키마다, 프로젝트마다 상한을 둘 수 있어요.

  • 감사 로그

    접속, 승인, RPC 호출, 직접 조작 전환이 누가 무엇을 언제 했는지와 함께 기록돼요. 페이지 내용과 매개변수 값은 남기지 않아요.

06네이티브 앱

앱에서는 사용자 스크립트 하나예요

SDK를 한 번 내려받아 문서 시작 시점에 주입하세요. iOS는 WKUserScript, Android는 addDocumentStartJavaScript를 쓰면 돼요. 웹 페이지는 한 줄도 바꾸지 않아요.

  • 어디서나 같은 기기 코드

    고정된 deviceId를 넘기고 deviceToken을 보관하면, WebView와 도메인이 달라져도 기기 코드가 그대로예요.

  • 네이티브 컨트롤

    nativeControl()로 직접 조작 / AI에 맡기기 / 종료 버튼을 직접 만들 수 있어요. 페이지 스크립트는 이 함수를 호출할 수 없어요.

  • 네이티브 승인 창

    nativeApproval: true로 설정하면 접속 요청과 확인을 앱의 UI가 처리해요.

  • 실제 픽셀 스크린샷

    작은 브리지(WKWebView 스냅샷, Android 비트맵)만 있으면 에이전트가 DOM 재렌더링이 아닌 진짜 화면을 받아요.

RemoteDebugger.swift
import WebKit

/// Call before webView.load(…): fetch the SDK once, inject it at document start.
func installRemoteDebugger(into controller: WKUserContentController,
                           userId: String?, done: @escaping () -> Void) {
    let gateway = "https://rwd.example.com/v1"
    URLSession.shared.dataTask(with: URL(string: gateway + "/sdk.js")!) { data, _, _ in
        DispatchQueue.main.async {
            defer { done() }
            guard let data, let sdk = String(data: data, encoding: .utf8) else { return }
            let options: [String: Any] = [
                "deviceId": stableDeviceId, // a UUID kept in UserDefaults
                "userId": userId ?? NSNull(),
                "helpButton": true,
            ]
            let json = String(data: try! JSONSerialization.data(withJSONObject: options), encoding: .utf8)!
            let boot = sdk + "\n;RemoteWebviewDebugger.run(\"\(gateway)\", \"rwd_pk_…\", \(json));"
            controller.addUserScript(WKUserScript(source: boot,
                                                  injectionTime: .atDocumentStart,
                                                  forMainFrameOnly: true))
        }
    }.resume()
}

07셀프 호스팅

Go 바이너리 하나. 내 서버, 내 데이터.

게이트웨이가 웹 콘솔과 페이지 SDK를 함께 담고 있어요. 기본은 SQLite, 필요하면 Postgres를 쓰고, HTTPS는 Docker Compose와 Caddy가 맡아요.

  • 콘솔

    기기, API 키, 멤버와 역할, QR 초대, 정책, 감사 로그. 내 키가 채워진 연동 가이드까지 들어 있어요.

  • 저장소

    기본은 SQLite 파일 하나예요. RWD_DATABASE_URL=postgres://…를 설정하면 Postgres를 써요.

  • 운영

    /v1/metrics에서 Prometheus 메트릭, 버전과 SRI가 붙은 SDK 주소, /v1/healthz 헬스 체크를 제공해요.

배포 가이드

빠른 시작
# 컨테이너 하나: 콘솔 + 게이트웨이 + SDK
docker run -d --name rwd -p 8080:8080 -v rwd-data:/data \
  -e RWD_ADMIN_PASSWORD='change-me' \
  ghcr.io/zanel1u/remote-webview-debugger:latest

# 또는 직접 빌드해 올리고 Caddy로 HTTPS 붙이기
git clone https://github.com/ZaneL1u/remote-webview-debugger
RWD_DOMAIN=rwd.example.com \
  remote-webview-debugger/deploy/platform/deploy.sh you@your-server amd64
# → 콘솔        https://rwd.example.com
# → 게이트웨이  https://rwd.example.com/v1  (MCP는 /v1/mcp)

서버에는 Docker만 있으면 되고, 소스에서 빌드할 때는 Go와 Node가 필요해요.

08자주 묻는 질문

많이 받는 질문

사용자가 무언가 설치해야 하나요?

아니요. H5 페이지에서는 SDK가 페이지의 일부이고, 앱에서는 네이티브 코드로 주입해요. 사용자에게는 기기 코드가 적힌 작은 배지와, 에이전트가 접속할 때 뜨는 승인 창만 보여요.

에이전트는 무엇을 보고 무엇을 할 수 있나요?

사용자가 허용한 것만요. 그 기기의 WebView 페이지를, 부여된 권한 수준으로 다뤄요. WebView 밖은 전혀 조작할 수 없어요. 마스킹된 입력란은 계속 마스킹되고, 페이지 내용은 신뢰할 수 없는 데이터로 모델에 전달돼요.

버그는 에이전트가 접속하기 전에 났어요. 그럼 못 보나요?

볼 수 있어요. SDK가 앱 실행 시점부터 기기에 세션 로그를 남기니까요. 페이지, 탭, console, 요청이 모두 들어 있어서 나중에 접속한 에이전트도 get_session_log로 버그까지 이어지는 과정을 전부 읽을 수 있어요. 그 밖에는 로그가 휴대폰을 떠나지 않아요.

에이전트가 휴대폰에서 버그를 고칠 수 있나요?

수정을 검증할 수 있어요. advanced 권한에서 evaluate_script가 실행 중인 페이지를 메모리 안에서 패치해요. 상태를 바로잡고, 함수를 덮어쓰고, 다시 렌더링해서 문제가 사라지는지 보는 거예요. 실제 수정은 평소의 배포 과정을 그대로 따르고, 기기에는 아무것도 남지 않아요.

어떤 에이전트에서 쓸 수 있나요?

MCP 클라이언트라면 모두요. Claude Code, Codex, Cursor는 호스팅된 Streamable HTTP 주소를 바로 쓰고, stdio만 지원하는 클라이언트는 같은 주소로 이어 주는 작은 브리지 rwd mcp를 쓰면 돼요.

어떤 플랫폼을 지원하나요?

요즘 쓰는 모바일 WebView와 브라우저라면 다 돼요. iOS의 WKWebView, Android System WebView, 앱 내장 브라우저, 그리고 휴대폰의 Safari와 Chrome까지요. 데스크톱 브라우저도 돼요.

네트워크가 나쁘거나 WebSocket이 막혀 있으면요?

페이지는 지수 백오프를 쓰는 가벼운 하트비트 WebSocket만 유지해요. 세 번 연속 실패하면 1분에 한 번 존재를 확인하는 방식으로 바꾸고, 에이전트가 접속하려 할 때 게이트웨이가 페이지를 깨워요.

페이지가 느려지지 않나요?

스크립트 하나예요. console과 네트워크 기록은 크기가 정해진 링 버퍼에 담기고(각각 500개, 응답 본문은 최대 5 MB), 세션 로그는 백그라운드에서 쓰고, 유휴 상태의 연결은 하트비트만 주고받아요.

eruda, vConsole, PageSpy와 같이 써도 되나요?

네. 스냅샷과 히트 테스트는 그 도구들의 노드를 건너뛰고, SDK 자신의 트래픽은 그 도구들의 네트워크 로그에 나타나지 않아요. eruda 안의 탭으로 넣을 수도 있어요.

위의 데모는 진짜인가요?

SDK는 진짜이고 전혀 고치지 않았어요. 게이트웨이만 브라우저 안에서 모사해요. WebSocket 심이 postMessage로 SDK의 연결을 그쪽으로 넘기고, 스크립트화된 에이전트가 실제 게이트웨이와 똑같은 RPC를 보내요. 처음부터 끝까지 진짜를 보고 싶다면 내 에이전트로 해 보세요.