내 사이트가 AI 에이전트가 읽을 수 있는지 확인하도록 오픈 소스 SEO 도구를 학습시킨 방법
요약
오픈 소스 SEO 도구인 open-seo를 활용해 웹사이트가 AI 에이전트에게 최적화되어 있는지 확인하는 방법을 다룹니다. robots.txt 설정, llms.txt 도입, 마크다운 대안 제공 등 AI 크롤러 대응을 위한 실무적인 설계 경험과 교훈을 공유합니다.
핵심 포인트
- AI 에이전트의 접근성을 확인하는 구체적인 지표(robots.txt, llms.txt, Markdown) 제시
- 단순 차단 여부보다 학습용 크롤러와 실시간 사용자 요청 크롤러를 구분하는 것이 중요
- 모든 차단 신호를 알림으로 만들지 않고 유의미한 노이즈 필터링 설계 필요
- 신규 규약(llms.txt 등)의 부재를 무조건적인 오류로 처리하지 않는 설계의 중요성
대부분의 SEO 감사(audit) 도구들은 여전히 10년 전 검색 엔진들이 중요하게 여겼던 질문들을 던집니다. 제목 태그(title tag)의 길이가 적절한지, 이미지에 대체 텍스트(alt text)가 있는지, 사이트맵(sitemap)이 유효한지 같은 것들 말이죠. 이런 것들은 여전히 중요합니다. 하지만 이제 사람들이 콘텐츠를 찾는 방식 중 점점 더 큰 비중이 검색 결과 페이지 대신 AI 어시스턴트를 통해 이루어지고 있음에도 불구하고, 거의 어떤 감사 도구도 귀하의 사이트가 AI 에이전트에 의해 _도달 가능한지(reachable)_조차 확인하지 않습니다.
저는 이 격차를 줄이기 위해 open-seo (Semrush/Ahrefs의 오픈 소스 대안)의 열려 있는 이슈(open issue)를 하나 맡게 되었고, 이는 자신의 코드베이스가 아닌 타인의 코드베이스를 위해 기능을 설계(scoping)하는 것에 대한 좋은 교훈이 되었습니다.
실제 질문
"이 사이트는 AI가 읽을 수 있는가?"라는 질문은 구체적이고 확인 가능한 항목들로 나뉩니다:
robots.txt가 AI 검색 인덱스를 구축하고 사용자의 실시간 요청에 답변하는 크롤러(crawlers)를 차단하고 있는가?- 사이트에
llms.txt가 있는가? — 이는 AI 어시스턴트에게 중요한 페이지들의 깔끔한 지도를 제공하는 신흥 규약(convention)입니다. - 사이트가 페이지에 대한 Markdown 대안을 제공하고 있는가? 이를 통해 어시스턴트가 전체 HTML을 파싱하는 대신 깔끔한 콘텐츠를 읽을 수 있습니다.
말하기는 간단합니다. 흥미로운 부분은 노이즈(noise)를 걸러내는 것이었습니다.
교훈 1: 모든 robots.txt 차단이 신호는 아니다
모든 크롤러에 대해 Disallow: /를 설정한 사이트는 의도적으로 사이트 전체를 차단하는 선택을 한 것입니다. _모든 것_을 차단하는 사이트에 대해 "GPTBot이 차단되었습니다!"라고 보고하는 것은 유용한 정보가 아니라, 통찰력처럼 포장된 노이즈일 뿐입니다. 따라서 이 체크 기능은 AI 에이전트가 일반적인 규칙보다 더 나쁘게(worse than the generic rules) 취급될 때만 플래그를 표시합니다:
const rootUrl = `${origin}/`;
if ((robots.isAllowed(rootUrl, GENERIC_PROBE_AGENT) ?? true) === false) {
return []; // 사이트 전체가 폐쇄됨 — AI 전용 신호가 아님
...
또한 저는 봇마다 개별적인 이슈를 발생시키는 대신, 에이전트들이 _왜 방문하는지(why they visit)_에 따라 그룹화했습니다. GPTBot과 ClaudeBot은 학습용 크롤러(training crawlers)입니다. 이들을 차단하는 것은 종종 의도적인 콘텐츠 정책이며, 굳이 알림을 줄 가치도 거의 없습니다. 반면 ChatGPT-User와 Claude-User는 실제 사용자가 지금 바로 어시스턴트에게 질문했기 때문에 페이지를 실시간으로 가져옵니다. 이들을 차단하는 것은 훨씬 더 큰 문제입니다. 왜냐하면 이는 학습 데이터 세트에서 제외되는 것이 아니라, 진행 중인 요청을 끊어버리는 것이기 때문입니다. 동일한 신호(signal)이지만 심각도(severity)는 완전히 다르므로, 이들은 하나의 일반적인 "AI 크롤러 차단" 범주로 묶는 대신 별도의 기본 심각도를 가진 별개의 이슈 유형으로 분류했습니다.
레슨 2: 부재(absence)가 항상 오류인 것은 아니다
llms.txt와 마크다운 대체 형식(Markdown alternates)은 모두 도입된 지 얼마 되지 않아 아직 이를 갖춘 곳이 거의 없습니다. 만약 점검 도구가 감사(audit) 대상 사이트마다 "마크다운 대체 형식 누락"이라는 플래그를 표시했다면, 사용자들은 린터(linter)의 전형적인 실패 모드인 '양치기 소년'처럼 감사 도구의 경고를 완전히 무시하도록 학습되었을 것입니다. 두 점검 모두 "이것은 일반적인 현상이며 오류가 아닙"이라고 명시적으로 설명하는 info 심각도로 설정되었습니다. 진정으로 문제가 되는 상태, 즉 llms.txt를 제공하고는 있지만 유일한 필수 구조적 요구 사항(H1으로 시작해야 함)을 위반하는 사이트만이 warning으로 격상됩니다.
레슨 3: 자신의 직관이 아닌 코드베이스 자체의 제약 사항에 맞추기
"이 페이지에 마크다운 대체 형식이 있는가"에 대한 저의 첫 번째 직관은 크롤링된 페이지(crawled-pages) 테이블에 새로운 불리언(boolean) 컬럼을 추가하는 것이었습니다. 하지만 그 후 프로젝트의 크롤링 파이프라인(crawl pipeline)이 어떻게 작동하는지 실제로 살펴보았습니다. 전체 페이지 데이터는 데이터베이스 테이블에 저장되지만, 크롤링 단계 사이에서 지속 가능한 워크플로 상태(durable workflow state)로 유지되는 것은 오직 간략한 요약(slim summary)(제목, 상태 코드, 기타 몇몇 필드)뿐입니다. 10,000페이지 규모의 크롤링 시 메모리 사용량을 제한하기 위해 링크 목록과 그 외의 모든 것은 의도적으로 삭제됩니다.
지속성 있는 컬럼(persisted column)을 추가한다는 것은 스키마 마이그레이션(schema migration)을 의미하며, 이는 이 프로젝트가 병행 지원하는 SQLite와 Postgres 스키마 경로를 모두 수정해야 함을 뜻합니다. 이는 감사(audit) 실행 이후까지 유지될 필요가 없는 기능을 위해 훨씬 더 크고 위험한 차이(diff)를 만드는 일이었습니다. 따라서 대신에, 해당 플래그(flag)는 파이프라인이 단계 간에 이미 전달하고 있는 동일한 일시적 요약 객체(transient summary object)를 통해 전달되도록 했습니다. 그리고 페이지별 원시 플래그가 아닌, '발견 사항(finding)'만이 단일 사이트 수준의 이슈 행(issue row)으로 저장됩니다. 이는 프로젝트에서 이미 파일 기반의 llms.txt 확인을 위해 사용하던 것과 동일한 패턴이며, 저는 새로운 것을 발명하는 대신 이를 확장했을 뿐입니다.
레슨 4: 리뷰를 파편화하지 말고, 열려 있는 PR을 확장하라
저는 이미 robots.txt 및 llms.txt 확인을 위한 PR(Pull Request)을 올려둔 상태였습니다. 누군가 리뷰를 시작하기 전에, 저는 Markdown 대체(Markdown-alternates) 확인 기능도 구축했습니다. 그리고 세 번째 PR을 여는 대신, 동일한 브랜치에 두 번째 커밋을 푸시했습니다. 메인테이너(maintainer)가 아직 아무것도 검토하지 않았기 때문에 방해되는 진행 중인 리뷰도 없었으며, 원래 이슈의 4분의 3을 해결하는 하나의 PR이, 각각 4분의 1씩 해결하며 서로를 참조하는 세 개의 PR보다 리뷰하기 훨씬 쉽습니다.
결과
19개의 유닛 테스트(unit tests), 스키마 변경 없음, llms.txt를 위한 추가적인 fetch(가져오기) 한 번을 제외하고는 새로운 외부 API 호출 없음. 실제 차이(diff)를 확인하고 싶다면 PR은 여기에 열려 있습니다: every-app/open-seo#122.
코드 자체는 쉬운 부분이었습니다. 실제 작업은 단 한 줄의 코드를 쓰기 전에 다른 사람이 코드베이스에서 유사한 문제를 어떻게 이미 해결했는지 읽는 것이었습니다. "어떻게 하면 노이즈를 피할 수 있을까"와 "어떻게 하면 이 프로젝트의 기존 형태에 맞출 수 있을까"라는 질문이 크롤러 탐지 로직 자체보다 더 중요했습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기