Agent 친화적 웹사이트 구축 방법
Rspress가 llms.txt, SSG-MD, Accept: text/markdown 주입 등 기능으로 Agent가 문서를 이해·발견·읽도록 돕는 방법을 소개했다.
중국어 원문을 AI로 번역했습니다. 고유명사와 수치는 원문 표기를 우선하며, 중요한 판단에는 아래 출처 원문을 함께 확인하세요.
Claude Code, Cursor, Codex 등의 Agent는 이미 문서를 직접 열람하고, API를 호출하며, 코드를 작성한다. 개발자 도구 문서의 독자는 더 이상 사람만이 아니다.
하지만 사람에게 좋은 경험을 주는 웹사이트라도 Agent에게는 여전히 사용하기 어려울 수 있다:
- 사람은 내비게이션 바를 통해 콘텐츠를 탐색할 수 있지만, Agent는 어디서부터 시작해야 하는지 반드시 알지는 못한다;
- 사람이 보는 것은 완전히 렌더링된 페이지지만, Agent가 가져오는 것은 JavaScript 실행을 기다리는 빈 껍데기일 수 있다;
- 사람은 내비게이션, 버튼, 광고를 무시할 수 있지만, Agent는 방대한 HTML에서 본문을 먼저 추출해야 한다;
- 설령 웹사이트가 Markdown을 제공하더라도, Agent는 그것이 존재한다는 것을 반드시 알지는 못한다.
Rspress는 Rsbuild 기반의 정적 사이트 생성기로, 모든 문서가 Agent에 의해 더 잘 발견되고, 읽히고, 이해될 수 있도록 하는 데 힘쓰고 있다. 이 글에서는 Agent-friendly 측면에서의 Rspress 모범 사례를 소개한다.
llms.txt: Agent 시대의 sitemap
llms.txt 규격은 웹사이트 루트 디렉터리나 하위 경로에 두는 Markdown 색인 파일의 한 형태를 정의하며, 웹사이트 소개와 상세 내용을 포함한다.
sitemap.xml은 검색 엔진의 페이지 수집을 겨냥하고; llms.txt는 Agent의 콘텐츠 발견과 점진적 공개(progressive disclosure)를 겨냥한다: 먼저 간결한 문서 색인을 제공하고, 그다음 Agent가 필요에 따라 관련 페이지를 읽도록 하는 것이다.
예를 들어 Rspress 사이트의 llms.txt 파일은 rspress.rs/llms.txt 에 위치한다:
Rspress > Rspress is a static site generator based on Rspack. ## Docs - [Introduction](https://rspress.rs/guide/start/introduction.md 'Introduction'): Introduction to Rspress - [Quick Start](https://rspress.rs/guide/start/quick-start.md 'Quick Start'): Quick Start // ...
현재 Rspress의 doc_build 산출물은 세 부분으로 나눌 수 있다: 사람이 읽기 위한 HTML, Agent가 페이지 단위로 읽기 위한 Markdown, 그리고 콘텐츠 발견에 쓰이는 색인 파일이다.
doc_build/ ├── index.html # HTML 페이지, 사람이 읽기용 ├── index.md # Markdown 페이지, Agent가 읽기용 ├── guide/ │ └── start/ │ ├── introduction.html # HTML 페이지, 사람이 읽기용 │ └── introduction.md # Markdown 페이지, Agent가 읽기용 ├── sitemap.xml # HTML 문서 색인 ├── llms.txt # Markdown 간결 문서 색인 └── llms-full.txt # 전체 사이트 Markdown 콘텐츠
SSG-MD: SSG의 Markdown 버전
Rspress는 Static Site Generation to Markdown (SSG-MD) 기능을 제공하는데, 이는 완전히 새로운 기능이다. 그 이름 그대로 SSG-MD는 정적 사이트 생성(SSG) 과정과 유사하지만, 차이점은 페이지를 HTML 파일이 아니라 Markdown 파일로 렌더링하고, 대규모 모델이 기술 문서를 이해하고 사용하기 쉽도록 llms.txt 및 llms-full.txt 관련 파일을 생성한다는 것이다.
Rspress는 단순한 SSG 프레임워크가 아니라, Markdown 생성을 일급 능력으로 삼는다. 즉 SSG-MD: HTML for humans, Markdown for AI.
SSG-MD 개념을 이해하기 쉽도록, 아래는 SSG와 SSG-MD의 유추 표이다:
유추 항목 SSG SSG-MD 전체 명칭 Static Site Generation Static Site Generation to Markdown 최적화 목표 SEO(검색 엔진 최적화) GEO(생성형 엔진 최적화) 대상 검색 엔진 크롤러 대규모 언어 모델 / 벡터화 검색 시스템 색인 파일 sitemap.xml llms.txt 전체 콘텐츠 파일 - llms-full.txt 핵심 구현 renderToString renderToMarkdownString 접근 방식 /guide/start/introduction.html /guide/start/introduction.md
왜 SSG-MD가 필요한가?
React 동적 렌더링 기반의 프런트엔드 프레임워크에서는 정적 정보를 추출하기 어려운 문제가 흔히 존재한다. 이는 MDX에서도 마찬가지인데, .mdx 파일은 Markdown 콘텐츠를 포함하면서도 React 컴포넌트를 내장해 문서의 상호작용 능력을 강화할 수 있다. Rspress의 경우, Rspress는 사용자가 MDX 조각, React 컴포넌트, Hooks, 그리고 TSX 라우트 등의 동적 기능으로 문서 표현력을 강화할 수 있게 한다. 그러나 이러한 동적 콘텐츠를 Markdown 텍스트로 변환할 때는 다음과 같은 문제에 직면한다:
- MDX를 그대로 AI에 입력하면 대량의 코드 문법 노이즈가 포함되고 React 컴포넌트 내용이 손실된다
- HTML을 Markdown으로 변환하는 것은 흔히 효과가 좋지 않아 정보 품질을 보장하기 어렵다
정적 사이트 생성(SSG)은 크롤러가 수집할 수 있는 정적 HTML 파일을 생성해 SEO를 향상시킬 수 있다. SSG-MD 역시 이와 유사한 문제를 해결하여 GEO와 대규모 모델을 위한 정적 정보 품질을 높이기 위한 것이다. HTML을 Markdown으로 변환하는 것과 비교하면, 렌더링 기간의 React 가상 DOM이 더 나은 정보원을 가진다.
Why LLMs love Rspress SSG-MD?
SSG-MD는 어떻게 구현하는가?
- Rspress 내부에는 react-dom의 renderToString과 유사한 renderToMarkdownString 메서드가 구현되어 있어, React 컴포넌트를 Markdown 문자열로 렌더링한다:
import { renderToMarkdownString } from 'react-render-to-markdown' ; // HTML 요소는 대응하는 Markdown 문법으로 변환된다 renderToMarkdownString ( < div > < strong > foo </ strong > < span > bar </ span > </ div > , ); // 출력: '**foo**bar' // React 컴포넌트와 Hooks 지원 const Article = ( ) => { return ( <> < h1 > Hello World </ h1 > < p > This is a paragraph. </ p > </> ); }; renderToMarkdownString ( < Article /> ); // 출력: '# Hello World\n\nThis is a paragraph.\n'
이론적으로 이 API는 React로 구축된 모든 웹사이트에 적용할 수 있으며, 관심이 있다면 react-render-to-markdown 을 참고하기 바란다.
2. Rspress는 사용자 정의 remark 플러그인 remarkSplitMdx로 MDX 파일을 전처리한다. 이 플러그인은 MDX AST를 분할하여 순수 Markdown 콘텐츠와 JSX 컴포넌트를 분리한다: Markdown 텍스트는 문자열 리터럴로 직렬화되고, JSX 컴포넌트와 MDX 표현식(예: {variable})은 React 요소로 유지된다. 이를 통해 Markdown 콘텐츠는 React 렌더링 처리를 거치지 않고 원래대로 전달되며, 동적 컴포넌트는 renderToMarkdownString으로 렌더링된다.
예를 들어 다음 MDX:
Hello Some **bold** text. <PackageManagerTabs command="install rspress" /> {window.title}
는 다음과 같은 컴포넌트로 변환된다:
function _createMdxContent ( ) { return ( <> {'# Hello\n\nSome **bold** text.\n'} < PackageManagerTabs command = "install rspress" /> {window.title} </> ); }
3. import.meta.env.SSG_MD 환경 변수를 제공하여, 사용자가 React 컴포넌트에서 SSG-MD 렌더링과 브라우저 렌더링을 구분해 보다 유연하게 콘텐츠를 커스터마이즈할 수 있도록 한다:
export function Tab ( { label }: { label: string } ) { if ( import . meta . env . SSG_MD ) { return <> {`**Here is a Tab named ${label}**`} </> ; } return < div > {label} </ div > ; }
4. Rspress 내부 컴포넌트는 SSG-MD에 맞게 적응되어, SSG-MD 단계에서 합리적인 Markdown 콘텐츠가 렌더링되도록 보장한다. 예를 들어:
< PackageManagerTabs command= "create rspress@latest" />
는 다음과 같이 렌더링된다:
```sh [npm] npm create rspress@latest ``` ```sh [yarn] yarn create rspress ``` ```sh [pnpm] pnpm create rspress@latest ``` ```sh [bun] bun create rspress@latest ``` ```sh [deno] deno init --npm rspress@latest ```
Accept: text/markdown: 요청 헤더에 따라 Markdown 반환
2025년 11월, Claude Code 책임자 Boris Cherny는 X에 이렇게 썼다:
In the next version of Claude Code, Claude’s WebFetch tool automatically adds Accept: “text/markdown, *” to requests which helps docs sites provide token-efficient docs.
Cloudflare도 관찰했는데, Claude Code, OpenCode 등의 coding agent가 text/markdown을 포함한 Accept 요청 헤더를 전송한다는 것이다.
처음에 Accept: text/markdown이라는 관례는 Claude Code 등의 Agent 클라이언트와 Bun 등의 문서 사이트에서 먼저 채택되었다. HTML과 비교해 Markdown은 내비게이션, 스타일, 스크립트 등 페이지 외피를 포함하지 않아 컨텍스트를 덜 차지하고, HTML에서 본문을 추출하는 과정도 생략할 수 있어 Agent가 더 빠르게 사용 가능한 콘텐츠를 얻을 수 있다. 웹사이트에 이미 Markdown 산출물이 있다면 이 요청 헤더를 지원함으로써 Agent가 클라이언트가 스스로 HTML을 파싱하는 것에 의존하지 않고도 이러한 이점을 안정적으로 얻을 수 있다.
Rspress는 순수 정적 프레임워크로, 요청 헤더에 따라 HTML을 반환할지 Markdown을 반환할지 결정하는 deploy server가 없다. 그러나 이 요청 헤더를 처리하는 것은 간단하며, 호스팅 플랫폼에서 규칙을 설정하기만 하면 된다. Rspress 공식 사이트는 Cloudflare에 rewrite를 설정했다: 요청에 Accept: text/markdown이 포함되면 내부적으로 동일 경로에 대응하는 .md 파일로 rewrite하고; 일반 브라우저가 같은 URL에 접속하면 여전히 HTML을 반환한다.
curl https://rspress.rs/guide/start/introduction \ -H 'Accept: text/markdown'
응답은 Markdown이다:
> AI 에이전트용: 전체 문서 인덱스는 > https://rspress.rs/llms.txt 에서 확인할 수 있습니다. ... # Introduction Rspress는 Rsbuild를 기반으로 구축된 React 기반 정적 사이트 생성기입니다.
AFDocs: 검사와 점수 평가
AFDocs는 Agent-Friendly Documentation Spec의 부속 오픈소스 도구입니다. 검사 문서에는 7개 범주, 23개 검사 항목, 그리고 각 항목의 통과·경고·실패 조건이 나열되어 있습니다.
본문에서 소개한 여러 Rspress Agent-friendly 최적화는 AFDocs의 검사 규칙을 통해 검증할 수 있습니다.
Rspress의 기능 | AFDocs 검사 | 검사하는 문제 llms.txt | llms-txt-exists, llms-txt-valid | Agent가 문서 인덱스를 찾고 파싱할 수 있는가 SSG-MD | markdown-url-support | 각 페이지가 Markdown을 제공하는가 Accept: text/markdown | content-negotiation | 해당 요청 헤더를 포함했을 때 Markdown을 얻을 수 있는가 LlmsHint | llms-txt-directive-html, llms-txt-directive-md | Agent가 단일 페이지에서 문서 인덱스를 발견할 수 있는가 이중 산출물 | markdown-content-parity | HTML과 Markdown이 같은 내용을 표현하는가
이 외에도 Rspress는 AFDocs의 다른 검사도 참고하여 HTML 용량 등의 지표를 추가로 최적화했습니다.
Rspress의 afdocs 점수는 100/100 (A+)
다음 명령을 실행하여 공개 문서 사이트를 검사할 수 있습니다:
npx afdocs check https://docs.example.com --format scorecard
보고서에는 각 검사 항목의 결과, 수정 제안, 점수가 포함되며, 개조 전후의 결과를 비교하는 데 사용할 수 있습니다.
Mintlify의 Agent Score 역시 이 규격을 기반으로 하며, 23개 기본 검사 외에 완전한 콘텐츠, Agent Skills, MCP Server의 발견 가능성 검사를 추가했습니다.
주목할 점은, AFDocs는 글의 품질이나 Agent 응답의 정확도를 평가하지 않고, 검증 가능한 일련의 규칙만 검사한다는 것입니다.
injectLlmsHint: llms.txt 주소 노출
AFDocs의 Content Discoverability 검사에는 두 가지 관련 규칙이 있습니다:
- llms-txt-directive-html: Agent가 특정 HTML 페이지에 직접 접속할 때, 사이트에 llms.txt가 존재하고 현재 페이지의 Markdown이 있음을 아는가;
- llms-txt-directive-md: Agent가 Markdown 한 편을 받은 후, 전체 문서 사이트를 어디에서 찾아야 하는지 아는가.
사이트가 이미 Markdown을 제공하더라도, Agent가 깊은 문서에서 진입할 때는 현재 페이지의 Markdown 버전과 /llms.txt의 존재를 반드시 알지는 못합니다. 따라서 모든 페이지가 이러한 진입점을 능동적으로 노출해야 합니다.
Rspress는 기본적으로 injectLlmsHint 설정을 활성화합니다. SSG-MD를 활성화한 상태에서 생성된 HTML은 본문 앞쪽 위치에 시각적으로 숨겨진 순수 텍스트를 주입합니다:
< div class = "rp-llms-hint" style = "position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0" > For AI agents: the complete documentation index is available at https://example.com/llms.txt, the full documentation bundle is available at https://example.com/llms-full.txt, and this page is available as Markdown at https://example.com/guide/index.md. </ div >
여기에서는 display: none, hidden 또는 aria-hidden을 사용하지 않았고, URL도 링크 요소에 중첩하지 않고 순수 텍스트에 직접 적었습니다. 이렇게 하면 Hint는 독자에게 보이지 않지만 DOM에는 남아 있으며, HTML 파싱이나 HTML-to-Markdown 변환으로 페이지를 읽는 Agent도 이 directive를 보존하여 읽을 수 있습니다.
AFDocs의 llms-txt-directive-html 규칙도 이 점을 강조합니다: directive는 clip-rect나 sr-only 등의 방식으로 시각적으로 숨길 수 있지만, 반드시 DOM에 남아 있어야 하고, HTML-to-Markdown 변환을 거쳐도 계속 존재할 수 있어야 합니다.
생성된 Markdown 산출물에도 다음 내용이 포함됩니다:
> For AI agents: the complete documentation index is available at > https://example.com/llms.txt, the full documentation bundle is available at > https://example.com/llms-full.txt.
두 가지 출력은 각각 llms-txt-directive-html과 llms-txt-directive-md에 대응합니다.
참고 자료
- The /llms.txt file
- Rspress: llms.txt (SSG-MD)
- Boris Cherny: Claude Code WebFetch의 Accept 요청 헤더
- Cloudflare: Introducing Markdown for Agents
- Agent-Friendly Documentation Spec
- AFDocs: Checks Reference
- Mintlify: Is your documentation agent-ready?