
llms.txt 다음의 URL.md 패턴 — 동일 URL에 HTML과 Markdown을 병설하는 AI 검색 최적화
요약
AI 크롤러의 효율적인 정보 취득을 위해 HTML과 병행하여 Markdown 파일을 제공하는 'URL.md 패턴'을 소개합니다. llms.txt가 사이트 목차 역할을 한다면, URL.md는 개별 페이지의 순수 콘텐츠를 제공하여 AI의 파싱 비용과 토큰 소모를 줄이는 것을 목표로 합니다.
핵심 포인트
- URL.md 패턴은 HTML 파싱 비용과 토큰 소모를 근본적으로 절감함
- llms.txt는 사이트 목차를, URL.md는 개별 페이지 본문을 제공하는 보완 관계
- Astro를 활용하여 동일 데이터 소스로 HTML과 Markdown을 동시 생성 가능
- AI 에이전트가 별도의 파싱 없이 즉시 컨텍스트로 사용할 수 있는 환경 구축
llms.txt를 사이트에 배치했지만, AI 크롤러가 정말로 방문하고 있는지 불안해하는 웹 담당자분이 계시지 않나요?
저는 2025년부터 LLMO(LLM Optimization)를 운영하고 있는 입장입니다만, llms.txt는 '사이트의 목차'를 제공할 뿐, 개별 페이지의 내용(Content)을 AI에게 전달하는 역할은 별도로 필요하다는 것을 최근 깨달았습니다. 그것이 이번에 소개할 URL.md 패턴입니다.
본 기사에서는 Astro를 이용한 구현을 중심으로, URL.md 패턴의 설계 사상, 구현 코드, AI 크롤러의 동작까지 정리합니다.
짧게 말하자면, 이것뿐입니다.
/company → HTML(인간용)
/company.md → Markdown(AI용)
AI 에이전트가 웹상의 정보를 취득할 때, HTML에서 내비게이션(Nav), 장식(Decoration), 스크립트(Script)를 제외하고 순수 콘텐츠를 추출하는 처리는 무겁고 실패하기 쉽습니다. 처음부터 Markdown으로 반환하면, AI는 파싱(Parsing)이 불필요하며 그대로 컨텍스트(Context)에 사용할 수 있습니다.
llms.txt가 사이트 전체의 개요 맵을 제공하는 것에 반해, URL.md는 각 페이지의 내용을 제공합니다. 양자는 보완 관계입니다.
llms.txt는 Answer.AI 공동 창립자인 Jeremy Howard 씨가 2024년 9월에 제창한 사양입니다. 저도 즉시 자사 사이트에 설치했고, Qiita에도 llms.txt와 robots.txt를 15분 만에 설정하는 글을 썼습니다.
하지만 1년간 운용하며 깨달은 것이 있습니다.
llms.txt는 목차를 제공한다 → 어떤 페이지에 무엇이 있는지 알 수 있다
llms.txt는 본문을 제공하지 않는다 → 결국 AI가 HTML을 가져가러 간다
그리고 문제는 그 다음입니다. AI 에이전트가 HTML을 가져가러 가면, Tailwind로 빽빽하게 장식된 페이지에서 본문을 추출하기 위해 1페이지당 수천 토큰을 소비합니다. 중요한 헤딩(Heading) 구조가 <div>의 바다에 파묻혀 있다면 추출 정밀도도 떨어집니다.
SERanking사의 30만 도메인 조사(2025년 11월)에 따르면, llms.txt의 유무는 AI 인용률에 통계적으로 유의미한 차이를 만들어내지 못하고 있습니다. "LLM이 /llms.txt를 가장 먼저 가져오러 온다"라는 전제는 실측치로는 성립하지 않는 듯합니다. 저도 이 데이터를 보기 전까지는 "설치한 시점에서 절반은 이겼다" 정도의 기분이었기에, 조금 반성했습니다.
URL.md 패턴은 "llms.txt를 보완한다"기보다 "HTML 파싱 비용을 근본적으로 삭감한다"는 접근 방식입니다.
제가 운영하는 사이트는 Astro로 구동되고 있으므로, Astro의 예시로 작성하겠습니다.
src/pages/company.md.ts를 만듭니다 (.ts 확장자 + 앞단의 .md가 Astro의 동적 루트 규약).
import type { APIRoute } from 'astro';
export const GET: APIRoute = () => {
const content = `# 株式会社サンプルテック — 会社情報
...
포인트는 세 가지입니다.
Content-Type: text/markdown — AI에게 "이것은 Markdown이다"라고 명시
charset=utf-8 — 일본어 페이지에서는 필수
API 루트 — 빌드 시 정적 파일로 쓰여진다 (SSG에서도 동작)
HTML 페이지와 Markdown 엔드포인트(Endpoint)에서 서로 다른 내용을 반환하면 AI가 혼란을 겪습니다. src/data/company.json과 같은 단일 데이터 소스로 양쪽을 렌더링하는 것이 기본입니다.
// src/data/company.json
{
"name": "株式会社サンプルテック",
...
이렇게 하면 src/pages/company.astro (HTML)와 src/pages/company.md.ts (MD)가 동일한 company.json에서 파생되어 일관성이 유지됩니다.
llms.txt의 "상세 정보" 섹션에서 각 .md 엔드포인트를 안내합니다.
# Sample Tech Inc.
## 요약 (Summary)
엔지니어의 가처분 시간을 되찾아주는 스타트업.
...
AI 에이전트는 llms.txt를 기점으로 필요한 .md 엔드포인트에 자율적으로 도달할 수 있게 됩니다.
다른 프레임워크에서도 동일하게 할 수 있습니다.
// app/company.md/route.ts
export async function GET() {
return new Response(`# 株式会社サンプルテック...`, {
...
// server/api/company.md.get.ts
export default defineEventHandler((event) => {
setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8');
...
// src/routes/company.md/+server.ts
export const GET = () => {
return new Response(`# 株式会社サンプルテック...`, {
...
어떤 프레임워크에서도 수십 줄의 코드로 구현 가능합니다. public/ 디렉토리에 직접 .md 파일을 두는 방법도 있지만, 호스팅 측에서 Content-Type을 어떻게 반환할지 불확실하기 때문에 저는 API 루트 (API Route) 방식을 선호합니다.
구현을 하고 나면 "그래서 AI 크롤러가 정말로 .md를 가져오긴 하는 걸까?"라는 의문이 생깁니다.
제 사이트의 액세스 로그(Access Log)에서 발췌하면, 주요 AI 유저 에이전트 (User-Agent)는 다음과 같습니다.
| User-Agent | 주체 | 목적 |
|---|---|---|
ChatGPT-User/1.0 | OpenAI | ChatGPT 세션 중 실시간 취득 |
OAI-SearchBot/1.0 | OpenAI | ChatGPT Search용 인덱싱 |
GPTBot/1.0 | OpenAI | 학습 데이터 수집 |
ClaudeBot/1.0 | Anthropic | 학습 데이터 수집 |
Claude-User/1.0 | Anthropic | Claude 세션 중 취득 |
Claude-SearchBot/1.0 | Anthropic | Claude 검색용 |
PerplexityBot/1.0 | Perplexity | Perplexity 웹 검색 |
Claude-SearchBot는 ClaudeBot과 독립적으로 제어할 수 있는 사양으로 되어 있어, "학습 데이터 수집은 차단하되 검색을 위한 취득은 허용"과 같은 세밀한 운영이 가능합니다 (2026년 5월 기준).
제 사이트에서는 .md 엔드포인트에 대한 액세스 로그를 별도로 기록하고 있는데, 위의 봇(Bot)들이 .md를 직접 페치 (fetch)한 흔적이 있습니다. 비율은 전체 액세스의 몇 % 수준이지만, 제로가 아님을 확인할 수 있었습니다.
URL.md를 배치한다면 robots.txt도 정리해 두어야 합니다.
# robots.txt
User-Agent: GPTBot
Disallow: /
...
"학습 데이터 수집은 거부 / 실시간 인용과 AI 검색은 허용"이라는 흔한 방침은 이 방법으로 실현할 수 있습니다. Disallow:는 엄격하게 준수되는 것은 아니므로 (무시하는 봇도 있음), 정말로 차단하고 싶은 트래픽은 에지 (Edge)에서 차단해야 하지만, 규약상의 의사 표시로서는 유효합니다.
구현 프레임워크인 llmoframework.com이 이러한 User-Agent × 정책 패턴을 공개하고 있으므로, 설계의 기점으로 삼으면 빠릅니다.
URL.md를 배치했다면 효과를 측정하고 싶어질 것입니다. 저는 다음 메트릭 (Metrics)을 살펴보고 있습니다.
# nginx 액세스 로그에서 하루 동안의 AI 크롤러 유입을 집계
grep -E "ChatGPT-User|ClaudeBot|PerplexityBot|GPTBot" access.log \
| awk '{print $7}' \
...
- 어떤 페이지가 AI에 읽히고 있는가:
.md엔드포인트별 히트 수 - User-Agent별 비율: ChatGPT vs Claude vs Perplexity의 점유율
- HTML과 MD의 fetch 비율: AI가
.md를 우선시하고 있는가
제 사이트의 경우 2026년 Q1 기준으로 "ChatGPT-User 계열 41% / Claude 계열 28% / Perplexity 15% / 기타 16%"였습니다. 사이트 내용에 따라 비율은 크게 달라지므로, 직접 측정해 보시는 것을 권장합니다.
- URL.md 패턴은 「llms.txt의 다음 단계」 — 사이트 목차에서 개별 페이지 본문으로
- 동일한 데이터 소스에서 생성할 것 — HTML과 내용이 어긋나면 AI가 혼란을 느낌
- Content-Type: text/markdown이 핵심 — text/plain으로는 구조를 살릴 수 없음
- AI 크롤러(Crawler)는 확실히 오고 있음 — 로그를 통해 실측해야 함
- llms.txt / robots.txt / URL.md를 3종 세트로 — 각각의 역할이 다름
당신의 사이트는 URL.md에 대응하고 있습니까? AI 크롤러(ChatGPT-User / ClaudeBot 등)의 액세스 수 변화를 측정한 데이터가 있다면 꼭 댓글로 공유해 주세요. 저 또한 아직 「.md를 배치한 후 실제로 인용이 늘었는지」를 6개월 단위로 측정하는 중이며, 대조 데이터가 모이면 판단 근거가 될 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기