3-1. CDP 클라이언트와 서버 구현 25.12.20
첫날부터 전력 질주
12월 20일, 프로젝트를 시작했다. 배민 기술 블로그를 읽고 나서 참을 수 없었던 그 에너지를 그대로 코드에 쏟아부었다.
먼저 해야 할 건 명확했다. CDP(Chrome DevTools Protocol)를 웹 페이지에서 생성해서 WebSocket으로 보내는 클라이언트, 그리고 그 메시지를 Inspector로 중계하는 서버. 이 두 개가 없으면 아무것도 시작할 수 없다.
CDP 클라이언트
CDP 클라이언트는 디버깅 대상 웹 페이지에 삽입되는 스크립트다. 이 스크립트가 하는 일은 단순하다.
console.log,console.error같은 콘솔 메서드를 후킹해서 CDPRuntime.consoleAPICalled이벤트로 변환fetch,XMLHttpRequest를 후킹해서Network.requestWillBeSent,Network.responseReceived이벤트 생성- DOM 트리를 순회해서
DOM.getDocument,DOM.querySelectorAll같은 명령에 응답
레퍼런스로 삼은 devtools-remote-debugger와 chobitsu에 이미 구현이 되어 있었다. 콘솔이나 네트워크 후킹은 사실 과거에 조금 만져본 적이 있었고, CDP 프로토콜 구조도 대충은 알고 있었기 때문에 AI한테 레퍼런스 코드를 던져주고 "이거 기반으로 짜줘"라고 하니까 금방 나왔다.
내가 잘 아는 영역이라 AI의 아웃풋을 검증하기도 쉬웠다. 잘 모르는 영역에서 AI를 쓰면 질문-검증 사이클이 길어지는데, 이건 그런 게 없어서 속도가 빨랐다.
WebSocket 릴레이 서버
서버는 처음에 Bun으로 빠르게 만들었다. WebSocket 서버를 띄우고, 클라이언트와 Inspector 사이에서 CDP 메시지를 양쪽으로 중계하는 게 전부다.
구조는 단순하지만, 이게 전체 시스템의 핵심이다. 클라이언트가 보낸 CDP 이벤트를 Inspector로 넘기고, Inspector가 보낸 CDP 명령을 클라이언트로 넘긴다. 서버 자체는 메시지 내용을 해석할 필요 없이 그냥 패스스루하면 된다.
다만 클라이언트 관리는 좀 신경 써야 했다. 여러 페이지가 동시에 연결될 수 있으니 각 클라이언트에 고유 ID를 부여하고, Inspector에서 특정 클라이언트를 선택해서 디버깅할 수 있게 했다. /json 엔드포인트로 현재 연결된 클라이언트 목록을 내려주는 것까지 만들었다.
DOMStorage 도메인
콘솔과 네트워크는 레퍼런스에 이미 있었지만, DOMStorage(로컬스토리지, 세션스토리지)는 "아직 지원 안 됨"으로 남겨둔 경우가 많았다. 배민 블로그에서도 백로그에 있다고 했던 부분이다.
이걸 그냥 넘어가기엔 아까웠다. DOMStorage.getDOMStorageItems, DOMStorage.setDOMStorageItem, DOMStorage.removeDOMStorageItem 같은 CDP 명령을 구현해서, Inspector Application 패널에서 로컬스토리지를 조회하고 수정할 수 있게 했다. 디버깅할 때 상태 확인하려고 콘솔에서 localStorage.getItem() 치는 게 귀찮았는데, 이제 패널에서 바로 보인다.
첫날의 성과
하루 만에 CDP 클라이언트, WebSocket 릴레이 서버, 콘솔/네트워크/DOM/DOMStorage 기본 도메인까지 돌아가는 상태를 만들었다.
솔직히 이건 AI 덕이 크다. 레퍼런스 코드가 있고, 내가 방향을 알고 있으니, AI한테 노가다를 시킬 수 있었다. 예전 같았으면 이틀은 걸렸을 작업이다.
다만 이 시점에서는 아직 DevTools 프론트엔드가 붙어있지 않았다. 콘솔에 JSON 찍어보면서 "CDP 메시지가 잘 오가는구나" 하고 확인하는 수준이었다. 진짜 DevTools UI에서 보려면 다음 단계가 필요했다.
프로젝트 첫날, CDP 클라이언트(콘솔/네트워크/DOM/DOMStorage 후킹)와 WebSocket 릴레이 서버를 구현해서 CDP 메시지가 양방향으로 오가는 기본 파이프라인을 완성했다.