우아한 디버깅 툴 1부: 웹뷰/웹페이지 원격으로 디버깅하기

7 hours ago 1

버그 재현, 생각보다 쉽지 않습니다

버그가 발견되었을 때 프론트엔드 개발자가 가장 많이 하는 말이 있습니다.

“제 기기에선 잘 되는데요…?”

이슈가 발생하면 우리는 가장 먼저 상황을 똑같이 재현해보려 합니다.
하지만 재현이 항상 되는 건 아닙니다.
간헐적으로만 나타나거나, 특정 계정·환경에서만 발생하는 경우도 많죠.

예시 상황으로 알아보기

예를 들어, ‘기석이네 상점’에서 ‘첫구매 전용’ 상품을 주문한 뒤 반품을 시도했는데, 반품 신청이 정상적으로 되지 않았다고 가정해봅시다.
개발자가 이 상황을 그대로 재현하려면 꽤 복잡한 단계를 거쳐야 합니다.

  • 구매 이력이 없는 계정을 준비하고,

  • ‘첫구매 전용’ 상품을 주문 완료 처리한 뒤,

  • 주문 상태를 ‘배달 완료’로 변경해야 비로소 반품 신청 과정을 실행해볼 수 있습니다.

이렇게 어렵게 조건을 맞춰도 문제가 API 응답의 오류인지, 프론트엔드 처리 로직 문제인지 하나씩 확인해야 합니다.

이렇듯 문제를 ‘똑같이 재현하는 과정’에는 생각보다 많은 공수가 들며, 이런 경험이 반복되면 자연스럽게 이런 생각이 들게 됩니다.

“당시의 개발자 도구 상태를 그대로 다시 열어볼 수 있다면 얼마나 좋을까?”

이 고민에서 출발해 테스트 당시의 네트워크 요청과 콘솔 로그, DOM 화면 등을 그대로 저장하고 언제나 어디서나 개발자 도구를 열어볼 수 있는 ‘우아한 디버깅 툴’을 만들게 되었습니다.

이 글에서는 테스트 당시의 환경을 그대로 확인할 수 있는 ‘우아한 디버깅 툴’을 개발하게 된 배경과, 이를 활용해 웹뷰·웹페이지를 원격으로 디버깅하는 방법을 소개합니다.

우아한 디버깅 툴은 무엇인가?

단순한 로그 수집 도구가 아닌, console, network, element 등의 디버깅에 필요한 정보를 수집하고 보여주는 원격 디버깅 플랫폼입니다.

실제 사용 시나리오

베타 환경의 배민앱에서 테스트하던 중, 예상과 다른 동작을 발견했다고 가정해봅시다.


1) 먼저 우아한 디버깅 툴이 적용된 웹 지면에서는 오른쪽 하단에 파란색 ➕ 버튼이 노출됩니다.

2) 이슈를 발견한 순간, ➕ 버튼을 눌러 ‘기록방’을 생성합니다.

3) 만들어진 기록방의 주소를 복사해 이슈 상황을 제보할 수 있습니다.

앞서 설명한 과정을 따라 사용자가 직접 기록방을 생성하면, 현재까지의 디버깅 기록이 저장된 방의 전용 URL이 발급됩니다.

기록방 예시 URL: https://remote-debug.example.com/devtools?wss=wss://{웹소켓 서버 url}?recordId={기록방ID}

개발자는 이 링크를 열기만 하면 익숙한 개발자 도구(DevTools) 화면에서 당시 상황을 아래 내용들과 함께 디버깅할 수 있습니다.

  • 사용자의 실제 화면까지 문제가 발생한 당시의 화면
  • Console 로그, Network 요청, DOM 요소

여러 화면으로 이어지는 복잡한 동선도 문제없습니다

예를 들어 장바구니 → 주문하기 → 주문 완료 → 반품 신청처럼 여러 개의 웹뷰(또는 페이지)를 거치는 흐름이 있을 수 있습니다.

이럴 때를 대비해 플랫폼은 ‘이전 기록(Previous Record)’ 기능을 제공합니다.

사용자가 이슈 제보한 페이지 이전 단계들의 기록까지 거슬러 올라가, 사용자 전체 사용 흐름을 한눈에 추적할 수 있습니다.

“사용자가 어떤 경로로 이 화면에 도달했는가?”

이제는 추측이 아니라, 데이터로 확인할 수 있습니다.

Chrome Devtools Protocol을 활용한 구현 방법

이쯤에서 자연스러운 궁금증이 하나 생깁니다.

“URL로 접속한 페이지가 어떻게 DevTools(개발자도구)처럼 동작할 수 있을까요?”

“그 화면은 Chrome 내부 기능을 불러온 건가요?”

Chrome DevTools, 그 안을 들여다보다

Chrome의 개발자 도구는 별도의 오픈소스 프로젝트(devtools-frontend)로 공개되어 있습니다.

👉 Chrome DevTools Frontend on GitHub

저장소를 내려받아 직접 빌드 결과물로 나온 devtools_app.html만 실행해보면 익숙한 개발자도구 Console, Network, Elements 패널 UI가 있는 독립된 웹 애플리케이션 형태로도 동작시킬 수 있는 것을 확인할 수 있습니다.

이미지처럼 Chrome DevTools(개발자도구)만을 직접 페이지로 서빙할 수 있다는 것까지 확인할 수 있습니다.

그럼 다음 궁금증이 생길 수 있습니다.

“UI는 실행되었는데, 그럼 이 화면에 데이터는 어떻게 채워지는 것이지?”

DevTools가 콘솔 로그, 네트워크 요청, DOM 같은 정보를 표시할 수 있는 이유는 브라우저와 DevTools가 서로 데이터를 주고받는 구조로 설계되어 있기 때문입니다.

Chrome Devtools Protocol (CDP)

DevTools 와 브라우저 간 통신의 기반이 되는 것이 바로 Chrome DevTools Protocol(CDP)입니다.

👉 CDP 공식 문서 바로가기

CDP는 기본적으로 양방향 통신 구조로 이루어져있습니다.

대표적으로 많이 사용하는 Network 도메인을 기준으로 CDP 동작 방식을 함께 살펴보겠습니다.

우선 CDP 의 동작은 크게 Method, Event로 나뉘고 아래와 같은 차이점이 있습니다.

구분 설명 예시
Method DevTools → 브라우저로 명령 전송 Network.enable (네트워크 수집 시작)
Event 브라우저 → DevTools로 알림 전달 Network.responseReceived (응답 수신)

CDP 동작을 직접 관찰해보기(Protocol Monitor)

실제로 Protocol 동작 과정은 Chrome DevTools(개발자 도구)에서 protocol monitor 기능을 활성화한 뒤 볼 수 있습니다.

👉 Chrome Devtools Protocol Monitor 참고 문서

간단하게 빈 페이지(about:blank)에서도 CDP 동작을 확인할 수 있습니다.

Network 도메인을 예로 들어, 하나의 API 요청이 발생할 때 어떤 프로토콜이 오가는지 함께 살펴보겠습니다.

1) about:blank 빈 페이지에 직접 들어가고, 개발자도구의 protocol monitor를 활성화시켜줍니다.

2) 실제 API 요청 상황을 알아보기 위해 직접 개발자 도구에서 API 호출을 진행합니다.

이미지는 예시 API fetch(‘https://jsonplaceholder.typicode.com/todos/1′)를 호출할 예정입니다.

3) API 를 호출하게 되면 Network 도메인 관련 CDP가 탐지됩니다.

탐지된 프로토콜을 공식문서를 참고하면 아래와 같습니다.

브라우저에서 Devtools로 보내는 이벤트

  • Network.requestWillBeSent – HTTP 요청이 전송되기 직전 발생
  • Network.responseReceived – 응답 수신 시 발생
  • Network.dataReceived – 데이터 청크 수신
  • Network.loadingFinished – 요청 완료

4) DevTools에서 해당 API 요청을 클릭해 Response를 볼 때는 Network.getResponseBody Method가 탐지됩니다.

이번에 탐지된 프로토콜은 DevTools가 브라우저에게 보내는 요청(Method)입니다.

API 요청시 DevTools와 브라우저는 여러 차례의 메시지를 주고받으며 Network 도메인 디버깅에 필요한 데이터를 교환합니다.

원격 디버깅 구현하기

DevTools와 CDP 통신을 저장된 정보로 전달하기

앞서 Network 도메인으로 알아본 것처럼, 브라우저와 DevTools 사이에서는 CDP(Chrome DevTools Protocol) 정보가 오갑니다.

CDP를 미리 저장해두고, DevTools UI(devtools_app.html) 와 통신을 통해 전달이 가능합니다.

먼저 앞에서 살펴본 오픈소스 devtools_app.html 내부의 스크립트에서는 URL Query로 전달된 WebSocket 서버 주소(wss://...)에 연결해 CDP 메시지를 송수신하고 있습니다.

// devtools_app.html 내부 (요약 예시) const socket = new WebSocket(getQueryParam('wss')); socket.onmessage = (msg) => window.InspectorFrontendHost.sendMessageToEmbedder(msg.data);

더 나아가 프로토콜 모니터 예시로 확인했던 CDP를 활용해 서버와 devtools_app.html을 직접 소켓통신으로 동작시킬 수 있습니다.

아래 예제 코드는 일부 프로토콜만 추려서 Protocol 통신 방식을 보여주고 있습니다.

const requestCDPList = [ { "method": "Network.requestWillBeSent", "result": { "requestId": {requestId}, "request": { "url": "https://jsonplaceholder.typicode.com/todos/1", "method": "GET" }, "timestamp": 341220.306746, "type": "Fetch" } }, ... { "method": "Network.responseReceived", "result": { "requestId": {requestId}, "response": { "status": 200, "headers": { "content-type": "application/json; charset=utf-8", "…" : "…" } }, "timestamp": 341221.744184 } }, ... { "method": "Network.loadingFinished", "result": { "requestId": "…", "encodedDataLength": 1235, "timestamp": 341221.743344 } } ] const responseCDP = { id: {requestId}, result: { body: { userId: 1, id: 1, title: 'delectus aut autem', completed: false, }, base64Encoded: false, } } wss.on("connection", (ws) => { // 네트워크 기록 활성화 ws.send({ "id": 1, "method": "Network.enable", }) // 연결되면 request CDP 목록 전송 requestCDPList.forEach((cdp) => { ws.send(cdp); }); ws.on("message", (msg) => { const data = JSON.parse(msg.toString()); // getResponseBody 요청이면 responseCDP 응답 전송 if (data.method === "Network.getResponseBody") { ws.send(JSON.stringify({ id: data.id, ...responseCDP })); } });

아래 이미지와 같이 저장된 CDP로 devtools 화면에 필요한 정보를 보여줄 수 있습니다.

아키텍처 흐름

디버깅 플랫폼의 전체 동작은 “데이터가 생성 → 전송 → 저장 → 재현”되는 흐름으로 이해할 수 있습니다.

Network 기준으로 흐름을 살펴보겠습니다.

Network 이외 기능도 같은 아키텍처 흐름을 가지고 있습니다.

  • 디버깅 툴 SDK: 사용중인 웹뷰 / 웹페이지에서는 API 요청시 request, response 를 가로채어 CDP 양식으로 전환하여 웹소켓 서버로 전송합니다.
  • 웹소켓 서버: CDP 양식을 해당 기록지면에 매칭하여 DB에 저장합니다.
  • devtools_app: 매칭되는 기록방의 CDP 데이터를 불러와 디버깅 정보를 보여줍니다.

위 같이 Network 플로우를 이용해 동일한 방식으로 DOM, Console, Runtime 등 다른 DevTools 도메인 영역도 쉽게 확장할 수 있었습니다.

또한, 같은 플로우로 CDP 구조를 응용해 커스텀 도메인을 추가하는 것도 가능했습니다.

  • 예를 들어 Chrome DevTools에서는 지원하지 않는 ScreenPreview 기능을 구현하고자 스크롤이나 터치 같은 사용자 액션을 받습니다.
  • 사용자 액션을 통해 당시의 HTML 상태를 캡처해 마지막 화면을 기록합니다.
  • DevTools 화면에서는 이를 실제 미리보기 형태로 재현합니다.

마무리 디버깅의 허들을 낮추다

‘우아한 디버깅 툴’이 도입된 이후, QA·기획·디자인·개발 등 다양한 직군의 동료들이 이슈를 훨씬 손쉽게 제보할 수 있게 되었습니다.

기록방 URL 하나로 개발자는 당시의 DevTools 화면을 그대로 열어 문제를 바로 파악할 수 있습니다.

이렇게 ‘우아한 디버깅 툴’은 디버깅의 허들을 낮추며, 개발 효율과 협업 경험 모두를 한층 더 우아하게 만들어가고 있습니다.

이 글에 이어서 우아한 디버깅 툴의 기능을 확장해 개발 생산성을 향상시킨 "우아한 디버깅 툴 2부: 좀 더 편하게 일하기 위한 개선들" 글도 확인해 보세요.

참고문서

참고 1: Chrome Devtools Protocol 문서

참고 2: devtools-frontend Repository

참고 3: devtools-frontend 를 활용한 remote-debugger 사례

Read Entire Article