에이전트를 위해 준비한 문서, 코딩 에이전트는 실제로 어떻게 읽었을까
올해 상반기부터 회사 제품 문서를 AI 에이전트가 쓰기 좋은 형태로 바꾸는 작업을 진행했습니다. 저는 DRM, 포렌식 워터마킹, 모바일 앱 보안 같은 보안 솔루션을 제공하는 도브러너(DoveRunner)에서 개발자 경험을 맡고 있으며, 개발자들은 저희 문서 사이트에서 제품 연동 방법을 익힙니다. 저 역시 연동 작업 자체를 코딩 에이전트에게 맡기는 일이 많아졌고 저희 문서를 읽는 개발자들도 마찬가지일 것이기 때문에, 앞으로는 에이전트가 개발 문서를 찾아 읽기 쉽게 하는 것이 점점 더 중요해질 것입니다.
여름이 끝날 무렵에는 작업이 다 끝난 것처럼 보였습니다. 모든 페이지에 깔끔한 마크다운 버전이 생겼고, 사이트는 에이전트에게 그 위치를 알려 주고 있었으며, 에이전트 준비도를 평가하는 스캐너들의 점수도 향상되었습니다.
하지만 코딩 에이전트에게 실제 제품 연동 작업을 시켜보니, 에이전트는 저희가 만든 마크다운 페이지를 한 번도 직접 요청하지 않았고, 정확한 사양이 필요할 때는 결국 HTML 문서를 내려받아 값을 뽑아내고 있었습니다. 이 글에서는 에이전트의 세션 기록에서 무엇을 발견했는지, 왜 어떤 스캐너도 이를 잡아내지 못했는지, 마크다운을 만든 수고가 애초에 의미가 있었는지, 그리고 무엇을 고쳤고 같은 작업을 하는 분들은 무엇을 확인하면 좋을지에 대해 알아보겠습니다.
개요: 에이전트를 위한 문서란
문서를 AI 에이전트가 잘 읽고 활용할 수 있는 상태를 **‘에이전트 준비도’(Agent Readiness)**라고 부르며, 이 글을 읽는 데 필요한 개념은 세 가지입니다.
- 페이지의 마크다운 버전: 문서 페이지의 HTML은 대부분 내비게이션, 스크립트, 스타일이고 에이전트에게 필요한 것은 본문 텍스트뿐입니다. 그래서 각 페이지를 마크다운으로도 공개하며, 보통 같은 주소 끝에
.md를 붙입니다. (예:/guide/setup/과/guide/setup.md) llms.txt: 사이트 루트에 두는 에이전트용 목차로, 사이트의 페이지와 그 마크다운 버전을 나열합니다.- ‘콘텐츠 협상’(Content Negotiation): 에이전트가
Accept: text/markdown헤더로 마크다운을 선호한다고 알리면, 서버는 일반 페이지 주소에도 HTML 대신 마크다운으로 응답할 수 있습니다. 덕분에 에이전트는.md주소를 몰라도 마크다운을 받게 됩니다.
이런 항목을 기준으로 사이트를 채점하는 무료 스캐너도 여럿 있습니다. 저희는 isitagentready.com, Vercel의 is-agentic.com, 그리고 오픈소스 문서 검사 도구인 afdocs와 이를 웹에서 실행해 주는 Fern Agent Score를 사용했으며, 작업을 진행할수록 점수도 꾸준히 올라갔습니다.
실제 업무에서 이루어진 검증
9월과 10월에 저는 Anthropic의 코딩 에이전트인 Claude Code로 저희 문서에 크게 의존하는 두 가지 프로젝트를 진행했습니다. 하나는 개발자 사이트인 DoveRunner Lab의 실습 과정 제작으로, DRM 라이선스 토큰 생성, 워터마킹 세션 API 호출, 영상 패키징처럼 실제로 제품을 연동해야 했습니다. 다른 하나는 이 과정에 들어가는 튜토리얼 영상을 HyperFrames라는 도구로 코드로 만드는 작업으로, 화면의 모든 단계와 내레이션이 실제 API와 정확히 일치해야 했습니다.
두 프로젝트의 목적은 과정과 영상을 만드는 것이었습니다. 하지만 3주 동안 수십 번의 세션에서 에이전트가 정확한 기술 정보를 얻기 위해 저희 문서를 읽었기 때문에, 제가 따로 설계하려던 실험보다 나은 검증이 되었습니다. 그래서 작업이 끝난 뒤 세션 기록을 하나씩 살펴보았습니다.
에이전트가 실제로 한 일
에이전트는 저희 문서 페이지를 47번 가져왔는데, 모두 일반 HTML 페이지 주소였고 .md 주소는 한 번도 요청하지 않았습니다.
| 가져온 방법 | 횟수 | 요청한 주소 |
|---|---|---|
| Claude Code의 WebFetch 도구 | 40 | 일반 HTML 페이지 주소 |
| 명령줄 도구 curl | 7 | 일반 HTML 페이지 주소 |
.md 주소 | 0 |
WebFetch로 가져온 40번은 사실 마크다운으로 응답을 받았습니다. 페이지 주소에 대해서는 서버의 콘텐츠 협상이 정상 동작했기 때문입니다. 다만 WebFetch는 받아 온 내용을 작은 모델로 요약해 에이전트에게 넘기기 때문에, 에이전트가 실제로 읽은 것은 마크다운 원문 대신 그 요약이었습니다.
에이전트는 나름대로 올바른 길을 찾아갔습니다. 한 세션에서 에이전트는 /llms.txt에서 출발해 콘텐츠 보안 제품의 목차로, 다시 사이트의 모든 URL을 담은 기계 판독용 목록으로 이동한 뒤 그곳에서 페이지 주소를 가져왔습니다. 그리고 라이선스 토큰의 필드, 해시 형식, 암호화 키 같은 값처럼 틀리면 안 되는 사양은 주소를 직접 요청해 내용을 내려받는 명령줄 도구인 curl로 원본 HTML을 내려받아 글자 패턴으로 원하는 부분을 찾아내는 정규 표현식으로 값을 뽑아냈습니다. 에이전트가 남긴 메모에 따르면 요약이 아니라 정확한 원문이 필요했기 때문입니다.
즉, 에이전트는 저희가 마크다운으로 제공하려던 바로 그것을 원했고, 그 위치를 알려 주는 목차까지 찾아갔지만, 결국 250KB짜리 HTML 페이지를 긁어 와야 했습니다. 작업은 끝났지만 저희가 설계한 방식대로 진행되지는 않았습니다.
마크다운 원문에 닿지 못한 이유
원인은 언제 마크다운으로 응답할지를 결정하는 작은 서버 코드에 있었습니다. Claude Code의 WebFetch는 Accept: text/markdown, text/html, */* 헤더를 보내는데, 저희 서버는 이 헤더가 붙은 모든 요청을 문서 페이지 요청으로 간주하고 요청받은 주소 뒤에 무조건 .md를 붙여 찾았습니다. /guide/setup/처럼 슬래시로 끝나는 페이지 주소는 문제가 없었지만, llms.txt나 setup.md처럼 확장자로 끝나는 파일 주소에서는 동작이 깨졌습니다. 예를 들어 setup.md를 요청하면 존재하지 않는 setup.md.md를 찾게 된 것입니다.
| 에이전트가 마크다운을 선호하며 요청한 것 | 실제로 받은 것 |
|---|---|
콘텐츠 보안 목차 (/content-security/llms.txt) | 콘텐츠 보안 랜딩 페이지로 리다이렉트 (다른 주소로 자동으로 넘겨 보냄) |
전체 목차 (/llms-full.txt) | “페이지를 찾을 수 없음” |
모든 .md 페이지 | “페이지를 찾을 수 없음” (서버가 page.md.md를 찾음) |
| 존재하지 않는 주소 | 가장 가까운 섹션 페이지로 리다이렉트되어 정상처럼 보임 |
결국 저희가 이 모든 것을 만들어 준 대상인 바로 그 클라이언트는 마크다운 주소를 담은 목차에 접근할 수 없었고, 마크다운 주소 자체도 오류를 돌려주었습니다. 루트의 /llms.txt만 정상 동작했는데, 이마저도 다른 이유로 서버 코드를 거치지 않도록 설정되어 있었던 덕분이었습니다.
에이전트가 끝내 HTML로 돌아간 데에는 이유가 하나 더 있었습니다. .md 규칙을 설명하는 llms.txt의 한 줄이 WebFetch의 요약에 한 번도 포함되지 않았기 때문에, 에이전트는 .md 주소가 있다는 사실을 모른 채 curl을 썼고, curl은 마크다운을 요청하지 않으니 HTML을 받았습니다.
스캐너가 이를 잡아내지 못한 이유
가장 마음에 걸렸던 것은 이 문제를 스캐너가 잡아내지 못했다는 점입니다. afdocs의 검사 코드를 확인해 보니, 검사들은 한 번에 한 가지 조합만 확인하고 있었습니다. 페이지 주소에 마크다운을 선호하는 요청을 보내는 검사와, 마크다운을 요청하지 않고 .md 주소나 llms.txt의 링크를 가져오는 검사는 있었으며, 두 경우 모두 정상 동작했습니다. 문제가 된 조합, 즉 페이지가 아닌 주소에 마크다운을 선호하는 요청을 보내는 경우는 실제 에이전트가 보내는 요청 그대로였는데, 이를 보내는 검사는 하나도 없었습니다. is-agentic.com은 검사가 서버에서 실행되어 내용을 볼 수 없지만, 버그가 살아 있던 9월에 저희 문서를 채점하고도 이 문제를 지적하지 않았습니다. isitagentready.com으로 채점한 것은 버그가 생기기 전인 6월이었습니다.
심지어 버그가 있으면 오히려 통과하게 되어 있는 검사도 있었습니다. 이 검사는 마크다운을 선호하며 콘텐츠 보안 목차를 요청했고, 리다이렉트를 따라 랜딩 페이지에 도착한 뒤 마크다운 응답을 받자 통과로 처리했습니다.
스캐너는 사양에 비추어 URL을 하나씩 검사하지만, 이런 종류의 문제는 에이전트가 실제 헤더를 보내며 문서가 안내하는 경로를 따라갈 때만 드러납니다. 루트 목차에서 섹션 목차로, 다시 .md 페이지로 이어지는 경로입니다.
마크다운은 정말 필요했을까
에이전트가 .md 페이지를 한 번도 직접 열지 않고도 작업을 끝냈으니, 마크다운을 만든 수고가 의미 있었는지 묻는 것은 당연합니다. 우선 WebFetch로 가져온 페이지는 이미 마크다운으로 응답을 받은 것이었고, 요약을 거치더라도 에이전트가 페이지의 요지를 무리 없이 파악하는 데 한몫했습니다.
나머지 답은 마크다운이 없을 때 에이전트가 무엇을 받는지 측정해 보면 알 수 있습니다. 저희 사양 페이지 세 개를 afdocs 같은 검사 도구도 쓰는 turndown이라는 변환기로 HTML에서 마크다운으로 바꾼 뒤, 저희가 만든 .md 버전과 비교해 보았습니다.
| 페이지 | 원본 HTML | HTML 변환 결과 | 저희 .md | 코드 블록 유지 (변환 결과 / .md) |
|---|---|---|---|---|
| License Token | 247KB | 54KB | 36KB | 13개 중 0개 |
| Session Manager | 189KB | 44KB | 37KB | 15개 중 0개 |
| HTML5 Player | 148KB | 30KB | 14KB | 3개 중 0개 |
변환된 페이지는 AI 모델이 읽고 비용을 매기는 단위인 토큰을 1.2~2.2배 더 소모하지만, 더 큰 문제는 변환 과정에서 내용이 달라진다는 점입니다. 모든 코드 블록이 한 줄로 합쳐지기 때문에 라이선스 토큰 템플릿은 줄바꿈 없이 { "drm_type": "<drm type string>", "site_id": … 형태가 됩니다. JSON이라면 읽기 불편한 정도로 끝나지만, Python이나 YAML 설정이라면 더 이상 동작하지 않습니다. 정확한 값도 바뀝니다. 문서에 대소문자를 구분한다고 명시한 DRM 타입 이름이 둥근 따옴표로 감싸져 나오는데, 코드에서 "Widevine"과 “Widevine”은 서로 다른 값이기 때문에 그대로 옮기면 동작하지 않습니다.
결론적으로 작업의 종류에 따라 다릅니다. 페이지의 요지만 필요하다면 형식은 거의 상관이 없으며, 그래서 모든 것이 잘 돌아가는 것처럼 보였습니다. 하지만 정확한 필드 이름, 수식, 실행 가능한 코드가 필요하다면 마크다운 버전은 사양을 그대로 복사하느냐, 짐작해서 재구성하느냐의 차이를 만듭니다. 이번 에이전트가 사양을 정확히 옮길 수 있었던 것도 마크다운 덕분은 아니었으며, 앞에서 본 것처럼 HTML을 직접 내려받아 수고와 토큰을 들인 결과였습니다. 모든 에이전트와 도구가 이렇게 집요하게 원문을 찾아가지는 않으며, HTML을 받아 변환해 읽는 도구라면 한 줄로 합쳐진 코드와 바뀐 따옴표가 곧바로 작업 결과에 섞여 들어가게 됩니다.
무엇을 고쳤나
문서 사이트에서는 이미 파일 확장자가 있는 주소를 그대로 제공하도록 서버를 고쳐, 목차와 .md 페이지가 모든 클라이언트에 전달되도록 했습니다. 존재하지 않는 주소는 이제 실제로 “찾을 수 없음”을 돌려줍니다. 같은 시기에 afdocs 새 버전이 찾아낸 마크다운 변환 문제도 함께 고쳐, afdocs 점수는 실패 항목 3개가 있던 94점에서 100점이 되었습니다.
제가 만든 도구도 고쳐야 했습니다. siteready는 이 작업에서 떼어 내 만든 오픈소스 스캐너 겸 자동 수정 도구인데, 다른 사이트에도 같은 서버 코드를 생성해 주고 있었기 때문에 같은 버그가 그대로 배포되고 있었습니다. 이제 siteready에는 llms.txt에 있는 모든 종류의 주소를 실제 에이전트 헤더로, 리다이렉트를 따라가지 않고 요청하는 검사가 들어 있습니다. 이 검사를 예전 서버 코드에 돌리면 100점 만점에 50점이 나옵니다. 기존 스캐너의 검사 방식으로는 잡히지 않는 문제였습니다.
다만 저희가 만든 것들의 쓸모는 서로 달랐습니다. 단순한 마크다운 페이지와 llms.txt는 만드는 비용이 적었고 제 역할을 했습니다. 하지만 잘못된 주소를 추측해 리다이렉트하는 것처럼 영리한 부분은 대부분 스캐너 점수를 올리려고 추가한 것이었고, 이 글에 나온 버그는 모두 그 부분에 있었습니다.
같은 작업을 하는 분들께
-
에이전트가 요청하는 방식대로 직접 요청해 보시기 바랍니다. 아래 두 줄로 이 글의 핵심 문제를 확인할 수 있습니다.
curl -sI -H "Accept: text/markdown" https://your-docs.example/llms.txt curl -sI -H "Accept: text/markdown" https://your-docs.example/some/page.md-I옵션은 내용 없이 응답 상태만 확인합니다. 두 요청 모두 정상 응답인200을 돌려주어야 하며, 리다이렉트되거나 찾을 수 없다는 뜻의404가 나온다면 마크다운을 선호하는 에이전트는 해당 파일에 접근할 수 없습니다. -
스캐너 점수는 최소 기준으로 보는 것이 좋습니다. 최고 등급은 사이트가 그 검사들을 통과했다는 뜻일 뿐, 에이전트가 문서를 잘 쓸 수 있는지는 따로 확인해야 합니다.
-
실제 에이전트의 작업 기록을 남기고, 정확한 답을 기준으로 평가하시기 바랍니다. 에이전트가 어떤 주소를 어떤 헤더로 요청했는지, 마크다운에 한 번이라도 도달했는지를 남기고, 답을 냈는지뿐 아니라 필드 이름, 수식, 코드가 정확한지를 확인해야 합니다. 이 기록이 없었다면 작업이 성공한 것만 보고 아무것도 배우지 못했을 것입니다.
마치며
에이전트는 결국 연동 작업을 성공적으로 해냈지만, 저희 문서 사이트의 에이전트 준비도는 의도했던 대로 동작하지 않고 있었습니다. 스캐너의 검사 결과만 믿고 실제 에이전트 작업을 시도해보지 않았다면 이러한 문제를 발견하지 못했을 것입니다.
이번 일로 얻은 교훈은 스캐너는 자신이 실행하는 검사에 대해서만 알려 준다는 것입니다. 저희가 만든 경로가 실제로 에이전트가 지나는 경로인지는 에이전트가 일하는 모습을 지켜봐야 알 수 있습니다. 이 교훈을 바탕으로, 다음에는 ChatGPT나 Claude 같은 AI 어시스턴트가 회사 콘텐츠에 연결되는 표준 방식인 MCP 서버를 통해 저희 문서를 직접 검색할 수 있게 하는 작업을 계획하고 있습니다.
이상으로 에이전트 준비도를 갖춘 문서를 실제 코딩 에이전트가 어떻게 읽었는지에 대해 알아보았습니다. 자신의 문서에서도 비슷한 문제를 발견하셨거나 siteready를 사용해 보신 분이 있다면 의견을 나누어 주시기 바랍니다.
새 글을 이메일로 받아보세요
AI 기반 지식 관리와 에이전트 친화적인 기술 문서에 관한 글을 2주에 한 번쯤 보내드립니다.
스팸은 보내지 않습니다. 언제든 구독을 해지할 수 있습니다.