사용 중인 AI 채팅 플랫폼 감지하기: ChatGPT, Claude, Gemini, Copilot을 위한 URL 및 DOM 패턴
요약
Chrome 확장 프로그램 개발 시 ChatGPT, Claude, Gemini 등 다양한 AI 채팅 플랫폼을 안정적으로 감지하는 기술적 방법을 소개합니다. URL 기반의 빠른 매칭과 DOM 구조 패턴을 활용한 정밀 감지 방식을 결합하여 정확도를 높이는 전략을 다룹니다.
핵심 포인트
- URL(hostname)을 활용한 1차 검증은 빠르고 신뢰도가 높음
- 클래스명 대신 구조적 DOM 시그니처를 사용하여 안정성 확보
- 오탐 방지를 위해 다수결 방식(최소 2개 확인) 적용 권장
- SPA 환경을 고려하여 document_idle 시점에 감지 실행
프롬프트 인젝션 (Prompt Injection), 세션 내보내기 (Session Export), UI 커스터마이징 (UI Customization) 등 AI 채팅 플랫폼과 연동되는 Chrome 확장 프로그램을 개발하고 있다면, 어떤 작업을 수행하기 전에 사용자가 어떤 플랫폼에 있는지 알아야 합니다. ChatGPT의 텍스트 영역 (textarea)에 프롬프트를 주입하는 컨텐츠 스크립트 (Content Script)는 DOM 구조가 완전히 다른 Claude에서는 조용히 실패할 것입니다.
UI 재설계 중에도 유지되는 패턴을 사용하여 컨텐츠 스크립트에서 플랫폼을 안정적으로 감지하는 방법을 소개합니다.
URL을 통한 매칭 (Tier 1: 빠르고 신뢰할 수 있음)
가장 빠른 확인 방법은 location.hostname을 사용하는 것입니다. AI 플랫폼들은 예측 가능한 구조를 가진 별도의 도메인을 사용합니다:
const AI_PLATFORM_HOSTS = {
'chatgpt.com': 'chatgpt',
'chat.openai.com': 'chatgpt', // 레거시 도메인 (legacy domain)
...
이 방식은 일반적인 경우를 처리합니다: 사용자가 claude.ai에 있고 호스트 이름이 일치하면 플랫폼이 식별됩니다. DOM 접근이 필요하지 않습니다.
이 방식이 놓치는 예외 상황(Edge cases):
- 임베디드 인터페이스 (다른 제품에 내장된 ChatGPT)
- 주요 채팅 UI가 아닌 API 플레이그라운드 (API playgrounds)
- 커스텀 도메인에서 실행되는 화이트 라벨 배포 (White-label deployments)
이러한 경우에는 DOM 기반 감지로 넘어갑니다.
DOM 기반 감지 (Tier 2: 레이아웃 시그니처)
각 플랫폼은 클래스 이름(class names)이나 요소 ID(element IDs)보다 더 안정적인 고유한 DOM 시그니처 (DOM signatures)를 가지고 있습니다 (클래스 이름 등은 자주 변경됩니다). 구현 세부 사항이 아닌 구조적 패턴을 찾으세요:
const PLATFORM_SIGNATURES = [
{
platform: 'chatgpt',
...
다수결 방식 (최소 2개의 확인을 통과해야 함)을 사용하면, 관련 없는 페이지에 우연히 하나의 시그니처 요소가 존재하는 경우 발생하는 오탐 (False Positives)을 줄일 수 있습니다.
각 확인 과정을 try/catch로 감싸면 DOM 상태 문제를 처리할 수 있습니다. 요소가 예상한 방식으로 존재하지 않을 경우 일부 확인 과정에서 오류가 발생할 수 있기 때문입니다.
두 가지 접근 방식 결합하기
function detectPlatform() {
// Tier 1: URL 기반 (즉시 실행)
const byHost = detectPlatformByHost();
...
실제 상황에서는 Tier 1이 실제 사용 사례의 95% 이상을 처리합니다. Tier 2는 안전망 역할을 합니다.
타이밍: 감지를 실행해야 하는 시점
AI 채팅 플랫폼은 SPA (Single Page Application)입니다. 초기 HTML은 보통 셸(shell) 형태이며, 실제 UI는 비동기적으로 로드됩니다. document_start 시점에 즉시 감지를 실행하면 DOM이 아직 구축되지 않았기 때문에 실패하게 됩니다.
옵션:
document_idle 주입 (가장 간단한 방법):
// manifest.json
"content_scripts": [{
"matches": ["https://chatgpt.com/*", "https://claude.ai/*", "..."],
...
document_idle은 DOMContentLoaded 이후 및 모든 지연된 스크립트(deferred scripts)가 실행된 후, 하지만 이미지가 로드되기 전에 발생합니다. 대부분의 AI 플랫폼의 경우 이 시점이면 충분합니다. 그때쯤이면 UI가 렌더링되기 때문입니다.
SPA를 위한 MutationObserver:
플랫폼이 초기 로드 이후에 상태를 변경하는 경우(대화 간 이동, 모델 전환 등), DOM 변경 사항에 반응해야 합니다:
const observer = new MutationObserver(() => {
const platform = detectPlatform();
if (platform && platform !== currentPlatform) {
...
여기서 subtree: false는 의도적인 설정입니다. 대화 스레드 내부의 모든 DOM 변형(mutation)이 아니라, 주요 레이아웃 변경만을 감지하면 되기 때문입니다.
입력 텍스트 영역(Input Textarea) 찾기
플랫폼을 파악했다면, 프롬프트를 주입하기 위한 텍스트 영역(textarea)이 필요합니다:
const INPUT_SELECTORS = {
chatgpt: [
'#prompt-textarea',
...
ContentEditable div (Claude 및 Gemini에서 사용됨)는 표준 textarea와는 다른 주입 코드가 필요합니다:
function injectPrompt(element, text) {
if (element.tagName === 'TEXTAREA') {
// 표준 textarea
...
document.execCommand는 기술적으로 지원 중단(deprecated)되었으나, contentEditable 요소에서 React/Vue의 반응성(reactivity)을 트리거하는 가장 신뢰할 수 있는 방법으로 남아 있습니다. 합성 이벤트(synthetic event) 방식(new InputEvent('input', { data: text, bubbles: true }))은 프레임워크의 상태 업데이트를 일관되게 트리거하지 못합니다.
SPA에서의 페이지 탐색(Navigation) 처리
ChatGPT와 Claude는 모두 페이지 새로고침 없이 탐색합니다 (새 대화 = 전체 새로고침이 아닌 SPA 탐색). 감지 로직은 이를 처리할 수 있어야 합니다:
let currentPlatform = detectPlatform();
let lastUrl = location.href;
...
이것은 PromptStash의 탐지 레이어입니다. PromptStash는 다양한 AI 플랫폼에서 프롬프트 (Prompt)를 저장하고 재사용할 수 있게 해주는 Chrome 확장 프로그램입니다. 단축키 하나로 당신이 어디에 있든 저장된 프롬프트를 삽입할 수 있습니다.
여러분은 플랫폼별로 어떤 특이한 점들을 경험하셨나요? Gemini의 <rich-textarea> 커스텀 엘리먼트 (custom element)가 가장 놀라웠습니다. 이는 Shadow DOM 서브트리 (subtree) 내부에 Quill 에디터 (editor)를 감싸고 있어, 표준 querySelector 동작을 방해합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기