ChatGPT, Claude 및 Gemini를 감시하는 Chrome 확장 프로그램 제작을 통해 배운 5가지 것
요약
ChatGPT, Claude, Gemini 등 주요 AI 채팅 UI의 코드 블록을 감지하여 라이브 URL로 배포하는 Chrome 확장 프로그램을 개발한 경험을 공유합니다. 핵심은 스트리밍 DOM 변화를 안정적으로 추적하고, 클래스 이름 대신 구조적 패턴에 의존하며, 오탐을 줄이는 점수 기반 로직을 적용하는 것입니다.
핵심 포인트
- MutationObserver는 필수지만, 'DOM 안정성' 디바운싱이 더 신뢰할 수 있다.
- 클래스 이름보다 `pre` 태그 내의 구조적 패턴(예: `<!DOCTYPE`) 일치에 의존해야 한다.
- 단순히 첫 번째 블록을 가져오는 대신, 콘텐츠 유형별로 점수를 매겨 오탐률을 낮추는 것이 효과적이다.
- 보안을 위해 `srcdoc`만 가진 iframe이 아닌, 적절한 `sandbox` 속성을 갖춘 샌드박스가 필요하다.
저는 지난 몇 달 동안 ChatGPT, Claude, 그리고 Gemini 내부의 HTML 코드 블록을 감지하여 라이브 URL로 바로 배포할 수 있게 해주는 Chrome 확장 프로그램을 만들었습니다. '배포' 부분은 쉬운 20%였습니다. 어려운 80%는 매주마다 고장 나지 않도록 완전히 다른 세 개의 채팅 UI를 안정적으로 감시하는 것이었습니다.
실제로 저에게 무언가를 가르쳐 준 것은 다음과 같습니다.
1. MutationObserver는 필수적이지만, 여전히 거짓말을 할 수 있다
이 채팅 앱들 중 어느 것도 전체 응답을 한 번에 렌더링하지 않습니다. 토큰을 스트리밍 방식으로 전송하기 때문에, 관찰하는 DOM은 옵저버가 발동할 때마다 거의 항상 _불완전_합니다.
제 첫 번째 버전은 나타나는 순간 완성된 <pre><code 블록을 감지하려고 했습니다. 결과는요? 저는 스트림 중간의 HTML을 잡았고, <div> 중간에서 잘렸습니다.
실제로 작동한 것은 DOM 존재 여부가 아니라 DOM 안정성에 대한 디바운싱(debouncing)이었습니다:
let debounceTimer;
const observer = new MutationObserver(() => {
clearTimeout(debounceTimer);
...
600ms 동안 '아무것도 변하지 않음'이라는 신호가 '태그가 이제 존재함'이라는 신호보다 훨씬 더 신뢰할 수 있는 신호였습니다. 우아하진 않지만, 세 사이트의 스트리밍 속도 전반에 걸쳐 작동합니다.
2. 모든 AI 채팅 UI는 아무에게도 알리지 않고 DOM 구조를 재구성한다
ChatGPT, Claude, Gemini 모두 빈번한 프론트엔드 업데이트를 진행하며, 그들 중 어느 것도 제가 연결할 수 있도록 안정적인 클래스 이름을 유지할 의무가 없습니다. 저는 처음에 클래스 이름(.language-html, .hljs 등)으로 코드 블록을 선택했고, 출시 2주 만에 프로덕션 환경에서 셀렉터들이 조용히 깨져버렸습니다.
더 잘 작동한 것은 클래스 이름 대신 구조적 패턴을 일치시키는 것이었습니다. 즉, <!DOCTYPE 또는 <html로 시작하는 텍스트 콘텐츠를 가진 <pre> 안에 있는 <code>입니다. 처음 작성할 때는 느리지만, 이번 달 프레임워크가 어떤 CSS 클래스를 사용하기로 결정했는지 신경 쓰지 않습니다.
3.
하나의 AI 응답에는 여러 개의 코드 블록이 포함될 수 있습니다. 전체 HTML 페이지, 모델이 별도로 설명하는 CSS 스니펫, 나중에 제안되는 JS 수정 등이 그 예입니다. 단순히 '첫 번째 <pre> 태그'를 가져오는 것은 생각보다 자주 잘못된 방법입니다.
저는 첫 번째 일치 항목을 가져오는 대신 후보 블록에 점수를 매기는 방식으로 변경했습니다: <!DOCTYPE> 또는 <html>의 존재 여부가 가장 높은 점수를 받고, <head>/<body>의 존재 여부가 다음으로 높은 점수를 받으며, 50자 미만의 블록은 아예 폐기됩니다 (보통 실제 페이지라기보다는 인라인 예제에 불과합니다). 완벽하지는 않지만, 오탐(false positives)을 극적으로 줄여주었습니다.
4. 클라이언트 측 미리보기에는 srcdoc만 가진 iframe이 아닌 실제 샌드박스가 필요하다
제가 처음 구현한 미리보기 방식은 추출된 HTML을 단순히 srcdoc을 가진 iframe에 쏟아붓는 것이었습니다. 이는 누군가의 AI 생성 페이지가 window.parent에 접근하려는 <script>를 포함할 때까지 작동했습니다. 이는 그들의 경우 완전히 무해했지만, 콘텐츠 스크립트(content-script)를 통해 현재 페이지에 접근 권한을 가진 확장 프로그램에서 조용히 허용하고 싶지 않은 바로 그런 종류의 문제입니다.
<iframe
sandbox="allow-scripts allow-same-origin"
srcdoc="...">
...
sandbox를 명시적으로 제한하고, 어떤 권한을 부여할지 신중하게 결정하는 것은 겉보기보다 훨씬 중요하며 큰 영향을 미치는 다섯 분짜리 수정 사항 중 하나입니다.
5. 자격 증명(credential) 질문이 사용자들이 실제로 가장 먼저 하는 질문이다
저는 출시 후 첫 질문들이 어떤 호스트가 지원되는지에 관한 것이일 거라고 예상했습니다. 하지만 실제로는 거의 모든 사람의 첫 질문이 다음과 같은 버전이었습니다: "제 FTP 비밀번호는 어디에 넣어야 하나요?"
이는 저를 모든 종류의 자격 증명(credential)을 다루는 브라우저 확장 프로그램을 구축하는 누구에게나 추천할 만한 설계로 이끌었습니다:
– 필요한 경우가 아니라면 자격 증명(credential)을 자체 백엔드(backend)를 통해 라우팅하지 마세요.
– 특히 FTP의 경우, 모든 것이 확장 프로그램과 호스트 간에 직접 이루어지며, 제가 중간에 개입하지 않습니다.
– 자체 호스팅 설정(self-hosted setup)의 경우, 사용자가 자신의 서버에 올리는 가벼운 PHP "에이전트(agent)" 스크립트를 추가했는데, 이 스크립트는 FTP 비밀번호를 확장 프로그램의 저장소(storage) 안에 전혀 저장하지 않으면서 인증된 업로드(authenticated upload)를 수락합니다.
이 모든 것이 특이한 보안 엔지니어링은 아닙니다. 하지만 "제 비밀번호는 어디로 가나요"라는 질문에 문서를 통해 명확하게 답변할 수 있는 것이, 사용자 신뢰도(trust-impacting)에 영향을 미치는 가장 중요한 문장인 것 같습니다.
이 확장 프로그램의 이름은 HTML Deployer입니다. 이 도구는 ChatGPT/Claude/Gemini가 생성한 HTML을 가져와 Netlify, GitHub Pages, FTP 또는 자체 호스팅 대상(self-hosted target)으로 전송하며, 그 사이에 미리보기 단계(preview step)를 거칩니다. 만약 이러한 채팅 사용자 인터페이스(UI) 위에서 개발하면서 비슷한 DOM 안정성(DOM stability)이나 샌드박싱(sandboxing) 문제를 겪으셨다면, 댓글에서 경험을 공유하고 싶습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기