김현우
  • [Python/AEO] llms.txt와 자동 생성 주제 색인으로 220개 항목 저장소를 LLM이 읽는 문서로 바꾸기
    2026년 11월 14일 19시 59분 51초에 업로드 된 글입니다.
    작성자: kugorang

    TL;DR

    1. 지식 저장소 kugorang/ai-dev-knowledge에는 항목 220개가 월별 파일 3개(합계 163,946바이트)에 들어 있습니다. 가장 큰 파일은 2026년 11월 파일 하나로 185개 항목 144,118바이트이고, 항목 하나를 찾으려면 그 파일 144,118바이트를 통째로 읽어야 했습니다.
    2. 2026-11-14에 llms.txt(3,718바이트)와 주제 색인 페이지 9개·색인 목차 1개(합계 168,644바이트), 그리고 색인을 만드는 scripts/build-index.py(280줄, 표준 라이브러리만 사용)를 추가했습니다.
    3. python3 scripts/build-index.py --check는 색인이 낡았으면 종료 코드 1을 냅니다. 실측으로 색인 링크 473개가 모두 실제 항목 헤딩 앵커와 일치했고(broken 0), 실행 시간은 5회 중앙값 72.4ms였습니다.

    1. 문제: 항목 185개가 한 파일에 144,118바이트로 들어 있었다

    kugorang/ai-dev-knowledge는 AI 코딩 도구(Claude Code, Codex, Cursor, ChatGPT·OpenAI, Grok Bot 등)의 팁을 한국어 2~3문장 요약으로 모은 저장소입니다. 항목은 월별 파일 entries/YYYY-MM.md에 쌓입니다.

    2026-11-14 19시(KST)에 저장소를 내려받아 측정한 값입니다.

    파일 항목 수 바이트 헤딩(##) 수
    entries/2026-10.md 7 3,471 7
    entries/2026-11.md 185 144,118 185
    entries/bookmarks.md 28 16,357 28
    합계 220 163,946 220

    2026년 11월 파일 하나가 185개 항목, 144,118바이트입니다. 항목 하나의 평균 크기는 144,118 ÷ 185 = 779.0바이트입니다.

    이 구조에서는 항목 하나를 찾는 데 144,118바이트를 읽어야 합니다. 사람이 브라우저에서 Ctrl+F로 찾으면 되지만, 문서를 통째로 받아 가는 검색 크롤러와 LLM은 항목 하나를 확인하려고도 144,118바이트를 받습니다. 저장소 소개 문서인 README도 1,204바이트짜리 짧은 설명뿐이어서, 어떤 파일에 무엇이 들었는지 미리 알려 주는 문서가 없었습니다.

    2026-11-14에 이 다섯 가지를 커밋했습니다.

    커밋 시각(UTC) 내용 변경
    98969df 03:47 색인 생성기와 생성된 주제 페이지 추가 11개 파일, 861줄 추가
    1d2ae33 03:48 README를 대상 독자·주제 색인·FAQ·재생성 안내로 다시 작성 71줄 추가, 2줄 삭제
    a3b17e3 03:48 LLM용 저장소 설명 파일 llms.txt 추가 39줄 추가
    ecde7ba 05:22 Grok Bot 사용 팁 문서 추가, README·llms.txt에서 연결 162줄 추가
    20593d5 05:22 생성된 Grok Bot 주제 페이지에서 팁 문서를 링크 11줄 추가, 3줄 삭제

    합계는 14 files changed, 1,141 insertions(+), 2 deletions(-)입니다.


    2. llms.txt: 3,718바이트로 저장소 전체 구조를 먼저 알리는 파일

    llms.txt는 저장소 최상위에 두는 텍스트 파일입니다. LLM이 이 저장소를 읽을 때 어떤 파일을 어떤 순서로 열면 되는지 링크로만 적어 둡니다.

    지금 파일은 43줄, 3,718바이트이고 구조는 다음과 같습니다.

    # AI Dev Knowledge
    
    > A Korean-language knowledge base of practical tips, updates and workflows ...
    
    - Language: entries and summaries are in Korean; index pages have Korean and English intros.
    - Audience: developers (client and server), artists, game designers and QA who build apps, web and games with AI.
    - Entry format: a `## title` heading followed by `- 도구:` (tool), `- 날짜:` (date), ...
    - Updates: new entries are added to the monthly file about twice a day (around 00:00 and 12:00 KST).
    - Topic pages under topics/ are generated by scripts/build-index.py ...
    
    ## Docs
    - [README](https://raw.githubusercontent.com/.../README.md): Overview, audience, entry format, FAQ and how to regenerate the indexes
    ...
    ## Topics by tool
    - [Claude Code](https://raw.githubusercontent.com/.../topics/claude-code.md): Entries about Anthropic's Claude Code
    ...

    첫 줄은 # 제목 한 개, 두 번째 단락은 > 인용 한 줄, 그 뒤로 ## 묶음과 - [이름](주소): 설명 목록이 이어집니다. 이 형태는 llms.txt를 쓰는 프로젝트들이 공통으로 쓰는 모양입니다.

    링크 주소를 GitHub 저장소 화면(github.com/...)이 아니라 파일 원본 주소(raw.githubusercontent.com/...)로 적은 이유가 있습니다. 저장소 화면은 HTML과 자바스크립트로 만들어져 문서 내용 외에 메뉴 코드가 함께 옵니다. 원본 주소는 파일 본문만 text/plain으로 보냅니다. 터미널에서 확인한 실제 응답입니다.

    $ curl -sSI https://raw.githubusercontent.com/kugorang/ai-dev-knowledge/main/llms.txt
    HTTP/2 200
    content-type: text/plain; charset=utf-8
    etag: "70f390a334bd2d319f0fc69f7e50fe9efb1f82ec54a5c4789782d665a32c21c1"
    
    $ curl -sS -o /dev/null -w "%{http_code} %{size_download} %{time_total}\n" <같은 주소>
    200 3718 0.034118

    응답 코드 200, 본문 3,718바이트, 소요 0.034초입니다. 저장소 전체 파일 크기 합계는 367,719바이트이므로, llms.txt는 저장소 전체의 1.0% 크기로 목차 역할을 합니다.

    주의할 점이 하나 있습니다. llms.txt는 아직 공식 표준이 아니라 제안 규격(llmstxt.org)으로 시작된 관행입니다. 파일을 두었다고 해서 특정 검색엔진이나 LLM이 반드시 요청한다는 보장은 없습니다. 지금까지 확인한 사실은 "원본 주소로 요청하면 200과 text/plain으로 3,718바이트를 준다"는 것까지입니다. 실제로 어떤 봇이 이 파일을 요청했는지는 서버 접근 로그로 확인해야 하고, 이 저장소는 GitHub가 서버라 그 로그를 받을 수 없습니다.


    3. 색인 생성기: 텍스트 파싱 → 앵커 계산 → 정렬 → 분류 → 렌더

    scripts/build-index.py는 280줄, 10,485바이트이고 외부 패키지를 쓰지 않습니다. Python 3 표준 라이브러리 re, sys, unicodedata, pathlib 네 개만 가져옵니다. 실행 순서는 다섯 단계입니다.

    1. entries/에서 월별 파일(YYYY-MM.md)과 bookmarks.md를 찾습니다. 월별 파일의 항목은 ## 헤딩, bookmarks.md의 항목은 ### 헤딩입니다. 파일마다 항목 레벨이 달라서 entry_files()가 (파일, 레벨) 쌍으로 돌려줍니다.
    2. 각 파일을 한 줄씩 읽어 헤딩과 항목 필드를 뽑습니다.
    3. 헤딩마다 GitHub이 만드는 것과 같은 규칙으로 앵커 문자열을 계산합니다.
    4. 날짜 필드로 정렬합니다.
    5. 도구·주제별로 묶어 마크다운 페이지를 만들어 topics/에 씁니다.

    3.1 한 줄씩 읽으며 헤딩과 필드를 뽑는 부분

    HEADING_RE = re.compile(r"^(#{1,6})[ \t]+(.*?)(?:[ \t]+#+)?[ \t]*$")
    FIELD_RE = re.compile(r"^- (도구|날짜|요약|출처):\s*(.*)$")
    
    def parse_file(path, level):
        slugger = Slugger()
        entries = []
        current = None
        in_fence = False
        for line in path.read_text(encoding="utf-8").splitlines():
            if line.lstrip().startswith(("```", "~~~")):
                in_fence = not in_fence
                continue
            if in_fence:
                continue
            heading = HEADING_RE.match(line)
            if heading:
                anchor = slugger.slug(plain_heading(heading.group(2)))
                current = None
                if len(heading.group(1)) == level:
                    current = {"title": heading.group(2).strip(), "anchor": anchor,
                               "file": path.name}
                    entries.append(current)
                continue
            field = FIELD_RE.match(line)
            if field and current is not None:
                current.setdefault(field.group(1), field.group(2).strip())
        return [e for e in entries if "도구" in e and "요약" in e and "날짜" in e]

    HEADING_RE는 줄이 # 1~6개와 공백으로 시작하는지 검사합니다. (#{1,6})이 # 개수를 잡고 len(heading.group(1))로 레벨을 비교하므로, 항목 헤딩이 아닌 # 제목이나 #### 소제목은 목록에 들어가지 않습니다.

    in_fence는 코드 블록 안쪽 줄을 건너뛰는 상태값입니다. 줄이 ``` 또는 ~~~로 시작하면 상태가 뒤집히고, 코드 블록 안에서는 헤딩과 필드를 읽지 않습니다. README에 예시로 넣은 마크다운 조각이 항목으로 잘못 잡히는 것을 막는 장치입니다.

    setdefault는 같은 필드가 두 번 나와도 첫 값만 남깁니다. 항목 하나에 - 요약:이 두 줄 적힌 경우 뒤 문장이 앞 문장을 덮어쓰지 않습니다.

    마지막 줄이 필터입니다. 도구, 요약, 날짜 세 필드가 모두 있는 헤딩만 항목으로 인정합니다. 2026-11-14 측정에서 헤딩 220개가 들어가 220개가 남았습니다. 버려진 헤딩은 0개입니다.

    3.2 앵커: GitHub이 만드는 것과 같은 문자열

    링크 [제목](../entries/2026-11.md#앵커)가 정확한 위치로 이동하려면 앵커가 GitHub 화면의 헤딩 ID와 같아야 합니다. 그래서 GitHub이 쓰는 규칙을 그대로 옮겼습니다.

    class Slugger:
        """GitHub-compatible heading anchors (same rules as github-slugger)."""
    
        def __init__(self):
            self.seen = {}
    
        @staticmethod
        def base(text):
            kept = []
            for ch in text.lower():
                cat = unicodedata.category(ch)
                if ch in " -" or cat[0] in "LMN" or cat == "Pc":
                    kept.append(ch)
            return "".join(kept).replace(" ", "-")
    
        def slug(self, text):
            original = self.base(text)
            result = original
            while result in self.seen:
                self.seen[original] += 1
                result = f"{original}-{self.seen[original]}"
            self.seen[result] = 0
            return result

    unicodedata.category(ch)는 유니코드 문자의 종류를 두 글자로 돌려줍니다. 첫 글자가 L이면 글자, N이면 숫자, M이면 결합 문자이고 Pc는 밑줄 같은 연결 문자입니다. 그 밖의 문자(마침표, 쉼표, 물음표, 낫표 등)는 버립니다. 소문자로 바꾼 뒤 공백을 하이픈으로 바꿉니다.

    한글은 유니코드에서 L(글자)로 분류되므로 버려지지 않습니다. 실제로 계산한 예입니다.

    • 헤딩: Grok Bot 하네스 최적화로 같은 요금제에서 사용량 여유 증가
    • 앵커: grok-bot-하네스-최적화로-같은-요금제에서-사용량-여유-증가
    • 앵커 길이: 36자

    한글 앵커는 주소에 그대로 넣을 수 있지만, 프로그램이 URL로 요청할 때는 percent-encoding을 거칩니다. 같은 앵커를 인코딩하면 204자가 됩니다. 링크가 길어지고 도구에 따라 인코딩 처리가 다르므로, 링크를 만든 뒤에는 실제로 열리는지 확인해야 합니다. 이 검증은 아래 5절에 있습니다.

    seen 사전은 같은 파일 안에 같은 제목이 두 번 나올 때 뒤쪽에 -1, -2를 붙이는 부분입니다. 2026-11-14 측정에서 세 파일의 중복 제목은 0건이어서 이 분기는 실행되지 않았습니다. 항목이 늘면 같은 제목이 다시 나올 수 있으므로 코드는 남겨 두었습니다.

    3.3 도구 분류: 콤마로 나누고 Codex를 따로 센다

    def tool_slugs(raw):
        tokens = [t.strip().lower() for t in raw.split(",")]
        has_codex = any("codex" in t for t in tokens)
        found = set()
        for t in tokens:
            if "claude code" in t:
                found.add("claude-code")
            if "codex" in t:
                found.add("codex")
            if "cursor" in t:
                found.add("cursor")
            if "grok" in t:
                found.add("grok-bot")
            if "chatgpt" in t or (("openai" in t or "gpt" in t) and not has_codex):
                found.add("chatgpt")
        return found

    - 도구: 필드는 Claude Code, Codex처럼 콤마로 여러 도구를 적습니다. 실측으로 서로 다른 조합이 56가지였습니다.

    has_codex가 필요한 이유는 Codex가 OpenAI 제품이기 때문입니다. 이 값이 없으면 Codex, OpenAI 항목이 Codex 페이지와 ChatGPT 페이지에 동시에 들어가면서 ChatGPT 쪽 개수가 부풀려집니다. 그래서 같은 필드에 Codex가 하나라도 있으면 openai·gpt 문자열로는 ChatGPT에 넣지 않습니다. Codex만 적힌 항목은 Codex 페이지에만 들어갑니다.

    3.4 주제 분류: 제목과 요약을 정규식으로 검사

    def theme_slugs(entry):
        haystack = f"{entry['title']} {entry['요약']}".lower()
        return {th["slug"] for th in THEMES
                if any(re.search(k, haystack) for k in th["keywords"])}

    주제 키워드는 정규식입니다. 예를 들어 아트·영상 주제의 키워드 하나는 픽셀(?! 좌표)입니다. (?! 좌표)는 "바로 뒤에 좌표가 오지 않는"이라는 조건이라, 픽셀 좌표계처럼 UI 좌표를 말하는 항목은 픽셀아트 주제로 잡히지 않습니다. 같은 목록에 영상 ?(생성|제작|편집|광고|지시|만들|파이프라인)도 있어서 "영상생성"과 "영상 생성"을 모두 잡습니다.

    3.5 정렬 키: 날짜와 원래 순서

    DATE_RE = re.compile(r"(\d{4}-\d{2}-\d{2})(?:\s+(\d{2}:\d{2}))?")
    SENTENCE_RE = re.compile(r"(.+?[.!?][\"'”’)]*)(?=\s|$)")
    
    def date_key(raw):
        found = DATE_RE.findall(raw)
        if not found:
            return ("", "")
        return max((d, t) for d, t in found)

    - 날짜: 필드에는 2026-11-11처럼 날짜만 적기도 하고 2026-11-11 22:15처럼 시각까지 적기도 합니다. findall은 줄 안의 모든 날짜를 (날짜, 시각) 쌍으로 돌려주고, max가 가장 늦은 쌍을 고릅니다. 날짜가 없으면 빈 문자열 두 개가 되어 목록 맨 뒤로 갑니다.

    정렬은 entries.sort(key=lambda e: (e["sort"], e["order"]), reverse=True)입니다. order는 파일을 읽은 순서이고, reverse=True에서도 같은 날짜끼리는 원래 순서가 유지됩니다. Python의 sort는 안정 정렬이라 이 조합이 성립합니다.

    페이지에 붙는 요약은 first_sentence()가 문장 첫 하나만 잘라 씁니다. (.+?[.!?][\"'”’)]*)(?=\s|$)는 마침표·물음표·느낌표 뒤에 공백이나 줄 끝이 오는 첫 지점까지를 잡고, 닫는 따옴표가 붙은 경우도 함께 포함합니다.


    4. 같은 입력에 같은 출력: --check로 색인 낡음을 잡는다

    생성기는 결과 문자열을 만들어 기존 파일과 비교합니다. 같으면 아무것도 하지 않고, 다르면 다시 씁니다. --check를 붙이면 쓰지 않고 종료 코드만 냅니다.

    def main():
        check = "--check" in sys.argv[1:]
        stale = []
        for name, content in build().items():
            path = TOPICS_DIR / name
            if path.exists() and path.read_text(encoding="utf-8") == content:
                continue
            stale.append(name)
            if not check:
                TOPICS_DIR.mkdir(exist_ok=True)
                path.write_text(content, encoding="utf-8")
        verb = "out of date" if check else "updated"
        print(f"{verb}: {', '.join(stale) if stale else 'nothing'}")
        return 1 if check and stale else 0

    생성된 페이지 첫 줄에는 <!-- generated by scripts/build-index.py; do not edit --> 주석이 들어갑니다. 손으로 고친 흔적과 생성 결과가 갈라지는 것을 사람에게 먼저 알리는 표시입니다.

    터미널에서 직접 확인한 결과입니다.

    $ python3 scripts/build-index.py --check
    out of date: nothing
    $ echo $?
    0
    
    # 낡음 감지 시험: topics/mcp.md 끝에 한 줄을 덧붙인 뒤
    $ printf '\nstale\n' >> topics/mcp.md
    $ python3 scripts/build-index.py --check
    out of date: mcp.md
    $ echo $?
    1
    
    # 원래 내용으로 되돌린 뒤
    $ python3 scripts/build-index.py --check
    out of date: nothing
    $ echo $?
    0

    세 번째 결과가 핵심입니다. 시험용으로 고친 파일을 되돌리면 다시 0으로 돌아옵니다. 생성기가 파일을 읽어 문자열을 만들 뿐이고, 실행할 때마다 결과가 달라지는 값(현재 시각, 난수, 환경 변수)을 쓰지 않는다는 뜻입니다.

    실행 시간은 5회 측정에 중앙값 72.4ms, 최소 72.1ms, 최대 76.8ms였습니다. 항목 220개, 출력 파일 10개를 매번 다시 만드는 비용입니다. CI에 넣어도 부담이 없는 시간이고, 색인 갱신을 잊은 커밋은 이 명령이 종료 코드 1로 막습니다.


    5. 결과: 파일 10개, 링크 473개, 깨진 링크 0개

    2026-11-14 생성 결과를 실측한 표입니다.

    항목 값
    입력 항목 수 220개 (2026-10 파일 7, 2026-11 파일 185, bookmarks 28)
    입력 바이트 163,946바이트
    출력 파일 10개 (도구 5, 주제 4, 색인 목차 1)
    출력 바이트 168,644바이트
    색인 링크 줄 수 473개 (항목 1개당 평균 2.15회)
    깨진 앵커 0개
    생성기 280줄, 10,485바이트
    실행 시간 중앙값 72.4ms
    llms.txt 43줄, 3,718바이트

    분류별 항목 수입니다.

    도구 페이지 항목 주제 페이지 항목
    Claude Code 101 에이전트·스킬 158
    Codex 46 MCP 36
    Cursor 36 아트·영상 32
    ChatGPT / OpenAI 15 게임·Unity 27
    Grok Bot 22    

    깨진 링크 0개는 눈으로 본 것이 아니라 검사로 확인했습니다. 생성기가 만든 473개 링크에서 파일 이름과 앵커를 뽑아, 그 파일의 헤딩 앵커 집합에 실제로 있는지 대조했습니다. 검사 스크립트는 생성기의 Slugger와 HEADING_RE를 그대로 가져다 썼기 때문에, 앵커를 만드는 규칙과 링크를 검사하는 규칙이 같습니다.

    출력 페이지 하나의 실제 모양입니다.

    <!-- generated by scripts/build-index.py; do not edit -->
    
    # Grok Bot
    
    Grok Bot 운영·설정·사용량 관련 항목 모음입니다.
    
    Entries about running, configuring and budgeting Grok Bot.
    
    관련 가이드 / Related guides: [Grok Bot 사용 팁](../guides/grok-bot-tips.md)
    
    항목 22개, 최신순 / 22 entries, newest first.
    
    - [Grok Bot 하네스 최적화로 같은 요금제에서 사용량 여유 증가](../entries/2026-11.md#grok-bot-하네스-최적화로-같은-요금제에서-사용량-여유-증가) (2026-11-14): Grok Bot 팀이 이번 주 하네스 최적화를 몇 가지 배포해, 같은 요금제에서 쓸 수 있는 양이 평균 약 7% 늘고 루틴 실행은 최대 약 15%까지 늘어난다고 밝혔다.

    관련 가이드 줄은 20593d5 커밋에서 붙인 부분입니다. 도구 정의에 guides 목록을 두면 그 페이지만 가이드 링크를 출력합니다. Grok Bot 페이지에만 Grok Bot 사용 팁 문서(11가지 팁)가 연결됩니다.


    6. 남은 문제 세 가지

    6.1 에이전트·스킬 페이지가 220개 중 158개를 잡는다 (71.8%)

    키워드에 에이전트, agent, 스킬, skill, 하네스, harness가 들어 있어서, 요약에 이 단어가 한 번이라도 나오면 그 항목이 에이전트·스킬 페이지에 들어갑니다. 158 ÷ 220 = 71.8%입니다. 색인으로 쓰기에는 너무 넓습니다.

    개선 방향은 세 가지입니다. 첫째, 키워드마다 가중치를 주고 일정 점수 이상만 넣습니다. 둘째, - 태그: 필드를 항목에 직접 적고 생성기는 그 필드만 읽습니다. 셋째, 도구 필드처럼 정해진 값 목록에서 고르게 합니다. 어느 쪽이든 항목 작성 규칙이 바뀌므로 기존 220개 항목을 다시 손봐야 합니다. 지금은 문제의 크기만 재 두었습니다.

    6.2 항목 1개가 평균 2.15번 링크된다

    473 ÷ 220 = 2.15입니다. Claude Code, Codex 항목은 두 도구 페이지와 에이전트·스킬 페이지에 함께 나옵니다. 검색 결과에 같은 항목이 여러 번 보일 수 있습니다. 대표 페이지 하나를 정하고 나머지 페이지에는 링크 없이 제목만 남기는 방식으로 줄일 수 있습니다.

    6.3 주제 페이지가 계속 커진다

    지금 가장 큰 페이지는 에이전트·스킬 55,399바이트이고, 그다음이 Claude Code 35,789바이트입니다. 항목이 하루 2회, 회당 3~5개씩 쌓이면 한 달에 200개 안팎이 늘어납니다. 월 단위로 페이지를 나누거나(topics/claude-code/2026-11.md), 최근 30일 항목만 페이지에 싣고 나머지는 월별 파일로 넘기는 방식이 필요합니다. llms.txt가 3,718바이트로 작게 유지되는 것은 이 문제와 별개입니다. llms.txt는 목차만 담당하고 본문은 주제 페이지가 담당하기 때문입니다.


    7. 트러블슈팅 FAQ

    Q1. llms.txt를 두면 검색엔진과 LLM이 실제로 읽나요?
    파일이 있다는 것과 읽힌다는 것은 다른 문제입니다. llms.txt는 공식 표준이 아니라 제안 규격으로 퍼진 관행이고, 어떤 서비스가 이 경로를 요청할지는 그 서비스가 정합니다. 이 저장소에서 확인한 사실은 원본 주소로 요청했을 때 HTTP 200, content-type: text/plain, 3,718바이트가 온다는 것까지입니다. 실제 요청 기록은 서버 로그가 있어야 확인할 수 있고, GitHub가 호스팅하는 저장소에서는 그 로그를 받을 수 없습니다. 읽혔는지 확인하려면 직접 서버를 두고 접근 로그를 보거나, 검색 유입을 다른 방식으로 재야 합니다.

    Q2. 색인 페이지를 손으로 쓰면 안 되나요?
    손으로 쓰면 항목을 추가할 때마다 두 곳(항목 파일과 색인)을 고쳐야 하고, 한쪽만 고치면 두 문서가 서로 다른 말을 합니다. 생성기는 entries/만 고치면 되고, --check가 색인이 낡았는지 알려 줍니다. 대신 분류 규칙이 코드(TOOLS, THEMES)에 있으므로 규칙을 바꾸면 220개 항목의 분류가 한 번에 바뀝니다. 규칙 변경은 커밋 기록과 --check 결과로 확인해야 합니다.

    Q3. 게임 프로젝트에도 같은 방식을 쓸 수 있나요?
    같은 방식이 통합니다. 이 저장소의 게임·Unity 주제 페이지에는 27개 항목이 있고, 판정 기준은 도구 필드와 제목·요약의 키워드입니다. 엔진 표기를 Unity 6 (Unity 6.3 등 마이너 버전)처럼 마이너 버전까지 적어 두면 요약 문장 안에 버전 정보가 남아 검색에서 걸립니다. 다만 생성기는 텍스트만 읽으므로 버전 번호 자체로 페이지를 나누지는 않습니다. 버전별 페이지가 필요하면 도구 필드에 Unity 6.3처럼 버전을 적고 TOOLS 목록에 페이지를 추가해야 합니다.


    8. 엔지니어링 헌법: 코드 수정 ≠ 모의 시험 ≠ 실기기 검증 ≠ 운영 반영

    이번 작업에도 같은 기준을 적용했습니다.

    • 코드 수정: scripts/build-index.py를 쓰고 색인 페이지 10개를 생성한 단계입니다.
    • 모의 시험: --check가 out of date: nothing으로 끝나고 종료 코드 0을 내는 것까지입니다. 여기서 생성기가 만든 문자열과 저장된 파일이 같은지만 확인됩니다.
    • 실기기 검증: 473개 링크의 앵커를 실제 헤딩 집합과 대조해 깨진 링크 0개를 확인하고, 원본 주소 llms.txt를 HTTP로 요청해 200과 3,718바이트를 받은 단계입니다. 사람이 브라우저에서 링크를 눌러 본 것은 아니므로 여기까지가 자동 검증의 한계입니다.
    • 운영 반영: GitHub 저장소에 커밋과 푸시로 반영된 상태입니다. 다만 이것은 저장소 공개 반영이고, 별도 웹사이트에 이 문서를 게시하는 작업은 아직 하지 않았습니다.

    이 글의 수치는 2026-11-14 KST에 저장소를 내려받아 직접 측정한 값입니다. 서버 운영 지표나 트래픽 수치는 포함하지 않았습니다.


    작성: 쿠고랑 (kugorang) — 2026-11-14

    728x90
    728x90
    댓글