본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 21. 11:41

Next.js를 위한 동적 llms.txt를 만들었지만, Google은 신경 쓰지 말라고 했습니다.

요약

Next.js 16 환경에서 AI 어시스턴트의 효율적인 콘텐츠 인덱싱을 위해 동적 llms.txt 및 llms-full.txt를 구현하는 방법을 소개합니다. Google은 이러한 특별한 마크업을 권장하지 않지만, 저자는 비용 대비 편익과 소규모 AI 엔진 및 인덱서들의 활용 가능성을 근거로 이 방식의 유지 가치를 주장합니다.

핵심 포인트

  • llms.txt는 AI 모델이 사이트 전체를 크롤링하는 대신 큐레이션된 인덱스를 통해 콘텐츠를 빠르게 파악하도록 돕는 사양입니다.
  • Google은 AI 검색 최적화 가이드에서 llms.txt와 같은 특별한 마크업 생성을 권장하지 않는다고 명시했습니다.
  • Anthropic, OpenAI, Perplexity 등 주요 기업의 공식 지원은 없으나, 다양한 소규모 AI 엔진과 개발자 중심 인덱서들이 이를 활용합니다.
  • Next.js App Router를 사용하여 약 90줄의 코드로 CMS 데이터를 기반으로 한 동적 llms.txt 생성이 가능합니다.

2주 전, 저는 제가 운영하는 Next.js 16 사이트를 위해 동적 llms.txt 및 llms-full.txt를 배포했습니다. 시간 단위의 재검증 (revalidation), 사이트맵 (sitemap)과 동일한 콘텐츠 소스로부터의 데이터 추출, URL 패턴에 따른 자동 분류, 적절한 text/plain 반환 기능을 갖추고 있습니다. 구축하는 데 약 40분이 걸렸습니다. 지난주 Google은 AI 검색 최적화 가이드를 발표했습니다. 관련 문구는 다음과 같습니다: llms.txt 파일이나 기타 "특별한" 마크업을 만들지 마십시오. 그래서 저는 제가 가장 신경 쓰는 검색 엔진이 명시적으로 신경 쓰지 말라고 말하는 동적 llms.txt를 가지게 되었습니다. 그럼에도 불구하고 저는 이를 유지할 것입니다. 그 이유와 여러분도 똑같이 하고 싶을 경우를 대비한 코드를 소개합니다.

llms.txt가 수행해야 하는 역할
llms.txt 사양은 2024년 (Answer.AI의 Jeremy Howard가 제안)에 사이트가 AI 어시스턴트에게 사이트 콘텐츠의 요약된 기계 판독 가능 (machine-readable) 요약본을 제공하는 방법으로 제안되었습니다. 두 개의 파일이 있습니다: /llms.txt — 짧은 목차 스타일. 최상위 페이지와 간략한 설명. /llms-full.txt — 긴 형식. AI 인제스션 (ingestion)을 위한 실제 콘텐츠. 핵심 논리는 다음과 같습니다: 사용자 질문에 답하기 위해 사이트를 크롤링하는 LLM (Large Language Models)이 모든 페이지를 크롤링할 필요 없이 큐레이션된 인덱스를 얻게 된다는 것입니다. 실제로 이것은 진정한 표준이 되지 못했습니다. Anthropic, OpenAI, 그리고 Perplexity는 이러한 파일을 읽겠다고 공식적으로 약속한 적이 없습니다. Google 또한 이제 공개적으로 읽지 않겠다고 말했습니다. 하지만 여러 작은 AI 엔진과 개발자 중심의 인덱서들은 llms.txt가 존재할 때 이를 소비합니다. 그리고 제대로 설계된 파일을 유지하는 비용은 거의 제로에 가깝습니다. 따라서 비용 대비 편익 계산이 흥미롭습니다.

Next.js 16 App Router에서의 구현
세 개의 파일. 총 약 90줄.

1단계: CMS에서 콘텐츠를 가져오는 렌더러
저는 이를 lib/llms.ts에 보관합니다. Sanity에서 읽어오지만, Markdown 파일, MDX, 데이터베이스, JSON 설정 등 무엇이든 동일한 패턴으로 작동합니다.

// lib/llms.ts
import { getAllBlogPosts , getAllResources } from ' @/lib/sanity '
const SITE_URL = ' https://example.com '

function resourceCategory ( slug : string ): ' permit ' | ' comparison ' | ' glossary ' | ' general ' {
if ( slug .

startsWith( ' building-permit-guide- ' )) return ' permit ' if ( slug.includes( ' -vs- ' )) return ' comparison ' if ( slug.startsWith( ' what-is- ' )) return ' glossary ' return ' general ' } export async function renderLlmsTxt(): Promise<string> { const posts = await getAllBlogPosts() const resources = await getAllResources() const lines: string[] = [] lines.push( ' # Example Company ' ) lines.push( '' ) lines.push( ' > Toronto-based contractor specializing in framing, drywall, and insulation. ' ) lines.push( '' ) lines.push( ' ## 핵심 페이지 (Core Pages) ' ) lines.push( ' - ' ) lines.push( ' - 서비스 ' ) lines.push( ' - 회사 소개 ' ) lines.push( ' - 문의하기 ' ) lines.push( '' ) const permits = resources.filter(r => resourceCategory(r.slug.current) === ' permit ' ) const comparisons = resources.filter(r => resourceCategory(r.slug.current) === ' comparison ' ) const glossary = resources.filter(r => resourceCategory(r.slug.current) === ' glossary ' ) if ( permits.length ) { lines.push( ' ## 건축 허가 가이드 (Building Permit Guides) ' ) for ( const r of permits ) { lines.push( - [ ${ r.title } ]( ${ SITE_URL } /resources/ ${ r.slug.current } ) ) } lines.push( '' ) } if ( comparisons.length ) { lines.push( ' ## 비교 페이지 (Comparison Pages) ' ) for ( const r of comparisons ) { lines.push( - [ ${ r.title } ]( ${ SITE_URL } /resources/ ${ r.slug.current } ) ) } lines.push( '' ) } if ( glossary.length ) { lines.push( ' ## 용어집 (Glossary) ' ) for ( const r of glossary ) { lines.push( - [ ${ r.title } ]( ${ SITE_URL } /resources/ ${ r.slug.current } ) ) } lines.push( '' ) } if ( posts.length ) { lines.push( ' ## 블로그 (Blog) ' ) for ( const p of posts ) { lines.push( - [ ${ p.title } ]( ${ SITE_URL } /blog/ ${ p.slug.current } ) ) } } return lines.

) } export async function renderLlmsFullTxt (): Promise < string > { // renderLlmsTxt와 동일한 형태이지만, 각 // resource/post에 따라 실제 본문 내용을 인라인으로 포함합니다. // CMS에서 본문을 가져와 평문(plaintext)으로 변환한 뒤, // 각 섹션 헤더 아래에 추가합니다. // 간결함을 위해 생략됨 } 카테고리 분류는 제 개인적인 선호입니다. 명세(spec)는 느슨합니다. 중요한 것은 파일을 읽는 기계가 사이트에 대한 깔끔한 계층적 구조(hierarchical view)를 파악할 수 있느냐 하는 것입니다.

2단계: 라우트 핸들러 (route handlers)
Next.js 16 App Router는 /app 내부의 파일들을 라우트(routes)로 취급합니다. URL 경로로 명명된 폴더 안에 route.ts를 넣으세요:

// app/llms.txt/route.ts
import { renderLlmsTxt } from ' @/lib/llms '
export const revalidate = 3600 // 1시간 캐시
export async function GET () {
const body = await renderLlmsTxt ()
return new Response ( body , { headers : { ' content-type ' : ' text/plain; charset=utf-8 ' }, })
}

// app/llms-full.txt/route.ts
import { renderLlmsFullTxt } from ' @/lib/llms '
export const revalidate = 3600
export async function GET () {
const body = await renderLlmsFullTxt ()
return new Response ( body , { headers : { ' content-type ' : ' text/plain; charset=utf-8 ' }, })
}

revalidate = 3600 라인이 핵심입니다. 이는 Vercel/Next.js에 한 시간 동안 캐시된 버전을 제공하고, 그다음 요청 시 재생성하도록 지시합니다. 하루에 10개의 블로그 포스트를 게시한다면, 파일은 60분 이내에 이를 반영합니다. 일주일에 한 번 게시한다면, 게시 후 한 시간 이내에 반영됩니다. 별도의 빌드 단계(build step)가 필요하지 않습니다.

3단계: 배포 및 확인
curl https://example.com/llms.txt
몇 초 안에 생성된 마크다운 형식(markdown-flavored)의 텍스트가 반환되어야 합니다. 끝입니다. 총 코드량은 약 90줄입니다. Next.js를 사용해 본 적이 있다면 총 소요 시간은 40분입니다.

그다음 Google은 신경 쓰지 말라고 했습니다.
Google의 AI 최적화 가이드(이번 달 발행)는 명시적입니다: llms.txt 파일이나 기타 "특별한" 마크업을 만들지 마세요. Google의 설명에 따르면, 그들의 AI 기능(AI Overviews, AI Mode)은 인간이 보는 것과 동일한 콘텐츠를 수집(ingest)합니다. 그들은 페이지를 크롤링(crawl)하고, 렌더링된 HTML을 파싱(parse)하며, 이를 합성(synthesize)합니다.

그들은 llms.txt를 읽지 않습니다. 그것을 만드는 것은 "불필요한 노력"입니다. 이것이 솔직한 입장입니다. Google은 llms.txt를 가지고 있다고 해서 당신에게 불이익을 주지 않습니다. 그들은 그들의 관점에서 그것이 낭비되는 작업이라고 말하고 있을 뿐입니다. 하지만 Google이 유일한 AI 엔진은 아닙니다. 그럼에도 불구하고 내가 이것을 계속 유지하는 네 가지 이유입니다.

  1. 유지보수 비용이 제로입니다. 이 파일은 동적(dynamic)이며 사이트의 나머지 부분과 동일한 콘텐츠 소스에서 데이터를 가져오기 때문에 스스로 업데이트됩니다. 유지보수해야 할 별도의 워크플로우도, 매달 해야 하는 번거로운 일도, 데이터가 오래될 위험도 없습니다. 코드는 아무런 주의를 기울이지 않아도 2주 동안 리포지토리(repo)에 그대로 있었습니다. 다음에 블로그 포스트나 리소스 페이지를 추가하면, 파일은 한 시간 이내에 이를 반영합니다. 만약 유지보수 비용을 한 달당 소요되는 분(minute) 단위로 측정한다면, llms.txt는 0에 수렴할 것입니다. 포기해야 할 것이 아무것도 없습니다.

  2. 모든 AI 엔진이 입장을 밝힌 것은 아닙니다. Perplexity, ChatGPT, Claude, 그리고 여러 소규모 AI 검색 제품들은 llms.txt를 읽는지 여부를 공식적으로 밝히지 않았습니다. 일부 오픈 소스 RAG 인덱서(indexer)들은 읽습니다. 일부 에이전트 프레임워크(agent frameworks)는 기본적으로 이를 확인하도록 설정되어 있습니다. 만약 이러한 도구 중 단 하나라도 나의 llms.txt를 읽고 인간 사용자가 클릭할 만한 인용(citation)을 생성한다면, 이 파일은 제값을 하는 것입니다. 이것을 유지하는 데 드는 기회비용은 해당 엔드포인트(endpoint)에 드물게 발생하는 요청에 대해 Vercel이 제공하는 바이트(bytes) 정도에 불과합니다.

  3. 구조화된 폴백(fallback) 역할을 합니다. llms.txt는 어디에 존재하는 것보다도 내 사이트의 내용을 가장 깔끔하고 사람이 읽기 쉬운 요약본 중 하나입니다. 누군가 나에게 내 사이트 콘텐츠의 개요를 물어본다면, 나는 llms.txt를 채팅창에 붙여넣어 즉각적인 브리핑을 얻을 수 있습니다. 이것은 파일을 구축함으로써 얻는 무료 부수 효과입니다.

  4. 구축하는 과정 자체가 유용한 연습이었습니다. 모든 페이지를 분류하고, 무엇이 중요한지 결정하며, 각 섹션에 깔끔한 요약을 제공하는 과정을 거치는 것은 대부분의 사이트가 어쨌든 수행해야 할 콘텐츠 감사(content audit)와 정확히 일치합니다. 결과물(llms.txt 파일)은 산출물일 뿐입니다. 진짜 가치는 그 과정에서의 사고(thinking)에 있습니다.

언제 건너뛰어야 할까
만약 당신의 사이트가 순수하게 커머스(Commerce) 중심이거나, 콘텐츠가 매우 동적(Dynamic)이거나(수백만 개의 상품 페이지), 또는 콘텐츠의 대부분이 로그인 뒤에 숨겨져 있다면, llms.txt의 가치는 낮습니다. AI 엔진은 평문 텍스트 파일(Flat text file)로부터 당신의 상품 카탈로그를 가져가지 않을 것입니다. 하지만 당신이 콘텐츠 사이트, 문서(Docs) 사이트, 포트폴리오, 편집 출판물, 또는 페이지 수준의 콘텐츠가 중요하고 URL 구조가 의미를 갖는 곳이라면, llms.txt를 만드는 데 드는 40분의 시간은 충분한 가치가 있습니다.

작동 예시
다음에서 실제 출력물을 확인할 수 있습니다:
https://konstruction.ca/llms.txt
https://konstruction.ca/llms-full.txt

두 파일 모두 매시간 재생성됩니다. 두 파일 모두 사이트의 나머지 부분을 구동하는 동일한 Sanity CMS에서 데이터를 가져옵니다. 총 실행 시간 메모리 비용(Total runtime memory cost)은 0과 구분이 불가능할 정도로 낮습니다. 페이지뷰(Pageviews)는 낮지만 0은 아닙니다.

더 중요한 점
"X를 신경 쓰지 마세요"라고 말하는 Google의 게시 가이드라인(Publishing guidance)은 Google이 X를 패널티(Penalizing)로 처리한다는 것과 동일하지 않습니다. 때때로 Google은 당신에게 노력의 최적 배분(Optimal allocation)을 알려주는 것입니다. 때로는 그 말에 동의해야 합니다. 하지만 때로는 X를 계속해야 할 때도 있는데, 왜냐하면 비용은 0이고, 이득은 작을지언정 0은 아니기 때문입니다. llms.txt는 두 번째 경우에 해당합니다.

한 번 구축해 두고, 실행되게 둔 다음, 6개월 동안 그 파일을 무시하며 어떤 일이 일어나는지 지켜보세요. 만약 18개월 후에 Google이 "이제 우리는 AI Overviews에 정보를 제공하기 위해 llms.txt를 사용합니다"라고 발표한다면, 당신은 그것을 계속 실행해 두길 잘했다고 생각할 것입니다. 만약 발표하지 않는다면, 그 파일은 당신의 40분과 거의 0에 가까운 지속적인 주의력만을 소모했을 뿐입니다. 비용이 낮고 확신이 낮은 베팅(Low-cost, low-confidence bets)을 할 때 최적의 전략은, 많이 베팅하고 결과가 스스로 결정되도록 두는 것입니다. llms.txt를 만드세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0