AI 가시성을 위한 30초 Raw-HTML 스모크 테스트 (Smoke Test)
요약
AI 크롤러가 웹페이지의 콘텐츠를 제대로 읽을 수 있는지 확인하기 위한 'Raw-HTML 스모크 테스트' 방법을 소개합니다. 브라우저 렌더링 결과와 실제 서버가 전달하는 HTML 사이의 격차를 확인하여 AI 가시성을 확보하는 법을 다룹니다.
핵심 포인트
- AI 크롤러는 대부분 JS 실행 전의 Raw-HTML을 읽음
- DevTools Elements 패널은 실제 크롤러가 보는 데이터가 아님
- View Source(소스 보기)를 통해 서버 전달 문서를 확인해야 함
- JavaScript 비활성화 또는 curl 명령어로 간편하게 테스트 가능
AI 가시성을 위한 30초 Raw-HTML 스모크 테스트 (Smoke Test)
브라우저에서는 페이지가 완벽해 보입니다. Lighthouse 점수는 초록색이고, 분석(Analytics) 데이터도 정상적으로 보입니다.
하지만 그 중 어느 것도 AI 어시스턴트가 당신의 페이지에서 실제로 무엇을 읽을 수 있는지는 알려주지 않습니다.
이 아이디어는 제 지난 포스트의 댓글에서 @alexshev와 나눈 진정성 있는 대화에서 나왔으며, 그분은 제가 생각했던 것보다 더 명확하게 틀을 잡아주었습니다. 바로 Raw-HTML 확인을 스모크 테스트 (Smoke Test)로 취급하라는 것입니다. 이것이 당신의 사이트가 AI 리더(AI readers)에게 훌륭하다는 것을 증명하지는 않습니다. 다만, 왜 AI 답변에 당신의 사이트가 전혀 나타나지 않는지 한 달 동안 고민하기 전에, 당혹스러운 실패 사례들을 조기에 잡아낼 수 있게 해줍니다.
브라우저가 당신에게 거짓말을 하는 이유
페이지를 로드할 때, 브라우저는 JavaScript를 실행하고, fetch를 기다리며, 프레임워크를 하이드레이션 (Hydrate) 하고, 최종 결과를 렌더링 (Render) 합니다. 당신이 보는 것은 최종 상태입니다.
대부분의 AI 크롤러 (Crawlers)는 그렇게 하지 않습니다. GPTBot, ClaudeBot, 그리고 PerplexityBot은 일반적으로 Raw-HTML 문서를 가져와서 그곳에 있는 내용을 읽습니다. 렌더링 대기열도 없고, 당신의 useEffect가 해결되기를 기다리지도 않습니다. Google은 주목할 만한 예외입니다. Googlebot은 JavaScript를 렌더링하며, AI Overviews는 그 인덱스를 기반으로 구축되지만, 그 외의 모든 AI 시스템은 초기 응답만을 읽는다고 가정하십시오.
따라서 인간이 보는 것과 기계가 안정적으로 추출할 수 있는 것 사이에는 격차가 존재합니다. 그리고 모든 것이 정상적으로 보이기 때문에 그 격차는 눈에 보이지 않습니다.
대부분의 개발자가 실수하는 한 가지
DevTools Inspector는 Raw-HTML이 아닙니다.
Elements 패널은 JavaScript가 실행된 후의 라이브 DOM (Document Object Model)을 보여줍니다. 이는 어떤 AI 크롤러도 절대 볼 수 없는 콘텐츠를 기꺼이 보여줄 것입니다.
당신이 원하는 것은 소스 보기 (View Source, Cmd/Ctrl + U)입니다. 그것이 서버가 보낸 실제 문서입니다.
만약 콘텐츠가 Inspector에는 있지만 View Source에는 없다면, 그것은 클라이언트 사이드 렌더링 (Client-rendered)된 것입니다. 이것이 바로 실패 모드(Failure mode)를 한 문장으로 요약한 것입니다.
테스트 방법 자체
원하는 방식을 선택하세요. 세 가지 모두 1분도 걸리지 않습니다.
옵션 1 — 소스 보기 (View Source). Cmd/Ctrl + U를 누른 다음, Cmd/Ctrl + F를 눌러 당신의 헤드라인, 가격, 주소를 검색해 보세요. 거기에 없다면? 그것이 바로 답입니다.
옵션 2 — JavaScript 비활성화 (Disable JavaScript). Chrome DevTools에서: Cmd/Ctrl + Shift + P → "Disable JavaScript" 입력 → 새로고침. 이제 당신은 렌더링되지 않는 크롤러(crawler)가 보는 것과 거의 유사한 화면을 보게 됩니다.
옵션 3 — curl. 직관적인 확인을 위한 가장 빠른 방법:
curl -sL https://yoursite.com | grep -i "your headline here"
크롤러가 추출할 텍스트를 실제로 읽어보고 싶으신가요? 다음 명령은 태그를 제거하고 실제 콘텐츠의 처음 2000자만 보여줍니다:
curl -sL https://yoursite.com | python3 -c "
import sys, re
h = sys.stdin.read()
h = re.sub(r'<(script|style)[^>]*>', ' ', h, flags=re.S)
print(re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', ' ', h))[:2000])
"
만약 출력 결과가 대부분 내비게이션(navigation)이나 불필요한 코드(boilerplate)뿐이거나, 거의 비어 있다면 문제의 원인을 찾은 것입니다.
확인해야 할 네 가지 실패 모드 (Failure Modes)
Alex의 리스트는 실제 페이지에 적용해 볼 수 있을 만큼 구체적이므로 매우 유용합니다:
- 빈 HTML (Empty HTML). 전형적인 CSR (Client-Side Rendering) 기반의 싱글 페이지 애플리케이션 (SPA) 형태입니다. 당신의 콘텐츠는 [IMG:1]과 같이 비어 있으며, 나머지 모든 것은 JS를 통해 전달됩니다. 크롤러는 빈 껍데기(shell)만 받게 됩니다.
- 비즈니스 정보 누락 (Missing business facts). 제목, 제안(offer), 위치, 제품 상세 정보 등입니다. 이것들은 AI가 당신을 설명하거나 추천하는 데 필요한 정보들입니다. 만약 이 정보들이 초기 응답(initial response)에 포함되어 있지 않다면, 당신은 모델에게 추측을 요구하고 있는 셈입니다.
- 보이지 않는 제안 (Invisible offers). 마운트(mount) 시점에 API로부터 가격 정보를 불러오는 경우입니다. 매우 흔하면서도 치명적인 문제입니다. "X의 가격은 얼마인가요?"는 사람들이 어시스턴트에게 던지는 전형적인 질문입니다.
- 클라이언트 전용 콘텐츠 (Client-only content). 탭(tabs), 아코디언(accordions), "더 보기(load more)", 지연 마운트(lazy-mounted) 섹션 등입니다. 콘텐츠가 존재하기는 하지만, 크롤러는 절대 수행하지 않을 상호작용(interaction)이 있어야만 나타나는 콘텐츠들입니다.
이 네 가지 문제에 대한 해결책은 모두 같은 방향을 가리킵니다: 핵심 콘텐츠를 초기 HTML에 포함시키세요. SSR (Server-Side Rendering), SSG (Static Site Generation), ISR (Incremental Static Regeneration) 또는 프리렌더링 (prerendering) 등 당신의 스택이 지원하는 방식을 사용하면 됩니다. React를 포기할 필요는 없습니다. Next.js, Nuxt, Remix, SvelteKit은 모두 기본적으로 이 문제를 해결합니다. 문제는 거의 항상 순수 CSR을 사용하거나, 의도적으로 콘텐츠를 클라이언트로 미루는 데서 발생합니다.
두 번째 계층: 보험으로서의 구조화된 데이터 (Structured Data)
스모크 테스트 (Smoke Test)에 제가 덧붙이고 싶은 미묘한 차이는, 테스트를 통과하는 것이 결코 최종 목적지는 아니라는 점입니다.
콘텐츠가 Raw-HTML에 포함되어 있더라도, 구조가 모호하면 에이전트 (Agent)가 이를 평면화 (Flatten) 해버릴 수 있습니다. 단어들은 가져오지만 관계를 놓치게 됩니다. 즉, 어떤 숫자가 가격인지, 어떤 줄이 주소인지, 무엇이 제품이고 무엇이 관련 항목인지를 구분하지 못하게 됩니다.
이 지점에서 JSON-LD가 제 역할을 합니다:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Acme Widget",
"offers": {
"@type": "Offer",
"price": "49.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
}
}
이것은 초기 HTML에 바로 위치하며, 여러분의 핵심 사실 (Hard facts)을 모호함 없이 기계가 읽을 수 있는 (Machine-readable) 형태로 제공합니다. 이는 얕은 훑어보기 (Shallow skim) 과정에서도 살아남습니다.
따라서 경험 법칙 (Rule of thumb)은 결국 두 개의 계층으로 귀결됩니다:
- 초기 HTML 내의 핵심 콘텐츠 — 최소한 읽힐 수 있도록 하기 위함.
- 구조화된 데이터 (Structured data)에 반영된 핵심 사실 — 잘못 읽히지 않도록 하기 위함.
이 테스트가 증명하지 못하는 것들
스모크 테스트를 가짜 안도감이 아닌 유용한 도구로 만드는 것은 그 한계를 솔직하게 인정하는 것입니다:
- 이 테스트를 통과했다고 해서 콘텐츠의 품질이 좋다는 뜻은 아니며, 누군가가 이를 인용할 것이라는 보장도 없습니다.
- AI 크롤러 (AI crawlers)의 접속이 실제로 허용되는지는 알려주지 않습니다. 그것은 robots.txt의 영역이며, 완전히 별개의 확인 사항입니다.
- 특정 제품의 검색 (Retrieval) 및 순위 결정 (Ranking) 파이프라인을 재현하지 않습니다. 해당 기업 외부의 누구도 이를 할 수 없습니다.
이 테스트는 단 하나의 구체적이고, 흔하며, 비용이 많이 드는 실패 사례를 잡아냅니다. 바로 중요한 콘텐츠가 클라이언트 측 타이밍 (Client-side timing) 뒤에 숨겨지는 문제입니다. 그것이 전부입니다. 하지만 그것만으로도 30초를 투자할 가치는 충분합니다.
오늘 바로 실행해 보세요
여러분의 사이트에서 직접 시도해 보세요. 그다음 경쟁사의 사이트에서도 시도해 보세요. 그 과정은 진정으로 교육적이며, 예상보다 훨씬 빠르게 문제가 있는 사이트들을 찾아내게 될 것입니다.
수동으로 작업하고 싶지 않다면, 제가 이를 위한 무료 도구 세트를 운영하고 있습니다. 별도의 가입 없이도 크롤러(Crawler)가 하는 방식 그대로 서버 렌더링된 HTML (server-rendered HTML)을 가져와 실제로 읽기 가능한 정도를 점수화합니다. 하지만 위에서 언급한 curl 한 줄 명령어도 비용이 전혀 들지 않으며 대부분의 문제를 잡아낼 수 있습니다.
이 방식의 핵심은 비용이 거의 들지 않는다는 점입니다.
스모크 테스트 (smoke-test)라는 프레임워크와 네 가지 실패 모드 (failure modes)를 제시해 준 @alexshev에게 감사드립니다. 이 포스트는 기본적으로 우리의 댓글 스레드를 깔끔하게 정리한 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기