코드 위키: 리포지터리에서 자동 생성되는 문서, 그리고 그 대가

위 화면은 정산 리포의 한 패키지를 대상으로 “코드 위키” 빌드가 끝났다고 보고하는 출력이다. 별것 아닌 로그처럼 보이지만, 저 짧은 목록에는 코드베이스 문서화가 30년 동안 실패해 온 지점 세 개에 대한 대응이 각각 하나씩 들어 있다. 이 글은 저 화면을 한 줄씩 뜯어보면서, 코드 위키가 무엇을 해결하고 무엇을 새로 만들어내는지를 정리한다.
1. 왜 코드 위키가 필요한가
문제는 규모다. 이 글이 다루는 정산 리포(settlement)를 실제로 세어 보면 이렇다.
| 항목 | 수치 |
|---|---|
| 전체 Java 파일 (빌드 산출물 제외) | 4,582개 |
모듈 (*-service + shared-common) |
14개 |
settlement-service 모듈 Java 파일 |
1,084개 |
위키 생성 대상이었던 …/lemuel/settlement/ 패키지 |
185개 |
(2026-09-09 기준, 로컬 체크아웃에서 find로 직접 집계)
이 규모에서 “정산 생명주기가 어떻게 되나요”라는 질문에 답하는 방법은 세 가지뿐이다. ① 아는 사람에게 묻는다 ② 185개 파일을 읽는다 ③ 문서를 읽는다. ③이 되려면 문서가 있어야 하고, 최신이어야 한다.
그런데 코드 옆에 붙은 설명은 원래 잘 낡는다. Wen 등이 ICPC 2019에서 발표한 연구는 GitHub의 Java 프로젝트 1,500개, 커밋 3,323,198건에서 추출한 13억 건의 AST 수준 변경을 분석해 코드와 주석이 어떻게 함께(또는 따로) 진화하는지를 측정했다. 결론의 요지는 단순하다 — 코드 변경의 상당수는 관련 주석 갱신을 동반하지 않으며, 어떤 종류의 변경이 주석을 낡게 만드는지는 유형별로 다르다.1 사람의 성실성 문제가 아니라 구조적 문제라는 뜻이다.
그러니 “문서를 잘 쓰자”는 해법은 이미 실패한 해법이다. 남은 선택지는 문서를 코드에서 파생시키고, 낡음을 기계가 탐지하게 만드는 것이다. 위 화면의 세 줄은 정확히 그 시도다.
2. 화면의 세 줄을 뜯어보기
2-1. Parent pages 자동 생성 (orchestrate.py 부모 합성)
파일 하나당 위키 페이지 하나를 만드는 건 쉽다. 문제는 그렇게 만든 185개의 리프 페이지로는 “이 패키지는 뭐 하는 곳인가”에 답할 수 없다는 점이다. 개별 클래스 설명을 아무리 많이 쌓아도 도메인 설명이 되지는 않는다.
그래서 부모 페이지를 따로 합성한다. 자식 페이지들을 입력으로 받아 디렉터리 단위 요약을 만들고, 그 요약들이 다시 상위 디렉터리 요약의 입력이 된다. 트리를 따라 아래에서 위로 올라가는 계층적 요약이다. 리프는 정확하지만 시야가 좁고, 부모는 시야가 넓지만 세부가 없다 — 두 층이 다 있어야 “어디를 봐야 하나”와 “거기서 뭘 보나”에 각각 답할 수 있다.
2-2. state.json 업데이트 (content hash, ingested_sha)
두 개의 서로 다른 해시가 한 줄에 같이 적혀 있는데, 역할이 완전히 다르다.
content hash는 증분 재생성을 위한 것이다. 파일 내용의 해시를 저장해 두면, 다음 빌드에서 해시가 같은 파일은 건너뛸 수 있다. 4,582개 파일을 매번 LLM에 넣는 것과, 바뀐 30개만 넣는 것의 차이다. 이건 Git이 30년 가까이 써 온 발상 그대로다 — Git은 내용을 해시로 주소화하는 파일시스템이고, 같은 내용은 같은 객체 하나로 저장된다.2 콘텐츠 주소화는 캐시 무효화를 “언제 지울까”에서 “내용이 바뀌었나”로 바꿔 놓는다.
ingested_sha는 낡음을 탐지하기 위한 것이다. 이 위키가 어느 커밋을 읽고 만들어졌는지를 못 박는다. 이게 없으면 문서를 보면서 “이거 언제 기준이지?”라고 물었을 때 답할 방법이 없다. 있으면 ingested_sha와 HEAD를 비교해 그 사이 변경된 파일 목록을 뽑을 수 있고, 그건 곧 낡은 위키 페이지 목록이다. 주석이 낡는 걸 막지는 못하지만, 낡았다는 사실을 기계가 말할 수 있게 된다. 실무적으로 이 차이가 크다.
2-3. .code-wiki/log.md 빌드 이력 추가
언제 무엇을 대상으로 어떻게 생성했는지의 기록이다. 생성형 파이프라인에서 이건 사치가 아니라 필수다. 같은 입력에 같은 출력이 나오지 않는 시스템에서, 어떤 문장이 어느 빌드에서 나왔는지 역추적할 수 없으면 잘못된 설명을 발견해도 원인을 못 찾는다.
3. 활용 인터페이스 — 트리를 가로지르는 질문
화면의 아래쪽 절반이 사실 더 중요하다.
/code-wiki:query — 질문으로 settlement 도메인 조회
/code-wiki:topic — 횡단 주제 생성 (e.g. "정산 생명주기", "대사 패턴")
docs/architecture/ 지도와 연동
query는 예상 가능한 기능이다. 진짜 물건은 topic이다.
코드 트리는 패키지로 쪼개져 있지만, 사람의 질문은 패키지를 가로지른다. “정산 생명주기”는 settlement 패키지 안에서 끝나지 않는다. 주문이 들어오고(order), 원장에 기록되고(ledger), 대사가 돌고(recon/pgreconciliation), 지급이 나가고(payout), 마감이 된다(closing). 실제로 이 리포의 settlement-service 한 모듈 안에만 crypto, ledger, deposit, chargeback, recovery, tax, idempotency, integrity, auditconsole 같은 패키지가 나란히 있다. 디렉터리 구조를 그대로 따라간 문서는 이 질문에 절대 답하지 못한다. 디렉터리는 답이 흩어져 있는 방식 그 자체이기 때문이다.
횡단 주제 페이지는 그 흩어진 조각을 하나의 서사로 다시 꿰는 산출물이다. 그리고 이건 리프 페이지와 부모 페이지가 이미 있어야 만들 수 있다 — 원본 코드 185개 파일을 한 번에 컨텍스트에 넣어 서사를 쓰는 건 안 되지만, 요약된 페이지들 위에서는 된다. 2-1의 계층 합성이 여기서 값을 치른다.
4. 그냥 RAG를 붙이면 안 되나
“코드베이스에 임베딩 걸고 검색하면 되지 않나”는 당연한 반문이다. 실제로 상당 부분 된다. 다만 알려진 실패 모드가 하나 있다.
Anthropic이 2024년 9월에 공개한 Contextual Retrieval 실험은 이 문제를 정면으로 다룬다. 문서를 잘게 쪼개 임베딩하면 각 조각이 자기가 어느 문서의 어느 맥락에 속하는지를 잃어버린다. 대응책으로 각 조각 앞에 그 조각을 문서 전체 안에 위치시키는 50~100 토큰짜리 설명을 LLM으로 생성해 붙인 뒤 임베딩과 BM25 인덱스를 만들었더니, 상위 20개 검색 실패율이 5.7% → 3.7%(임베딩만, 35% 감소), 임베딩+BM25 조합으로 2.9%(49% 감소), 리랭킹까지 더하면 1.9%(67% 감소)로 떨어졌다.3 참고로 이 실험의 지식 베이스 자체가 코드베이스 9개였다.
여기서 얻을 교훈은 “청크에 문맥을 미리 붙여 두면 검색이 좋아진다”는 것이다. 코드 위키는 그 아이디어를 훨씬 굵은 입자로 밀어붙인 형태로 볼 수 있다 — 문맥을 붙인 산출물이 50~100 토큰짜리 기계용 접두사가 아니라, 사람이 읽고 검토할 수 있는 위키 페이지라는 점만 다르다. 그 차이가 실질적인 이득 두 개를 만든다.
- 감사 가능성. 임베딩 벡터 안의 문맥은 사람이 열어볼 수 없다. 위키 페이지는 틀렸으면 틀렸다고 지적할 수 있다.
- 재사용. 같은 산출물을 검색 인덱스에도, 온보딩 문서에도, 아키텍처 리뷰에도 쓴다.
물론 대가도 함께 온다. 아래에서 다룬다.
5. 선례 — DeepWiki
이 접근은 처음 나온 게 아니다. Cognition은 2025년 5월 5일 DeepWiki를 공개하면서, 사내 도구인 Devin Wiki/Devin Search의 공개 무료판이라고 밝혔다. GitHub URL의 github.com을 deepwiki.com으로 바꾸면 해당 리포의 위키가 열리는 방식이고, 공개 시점에 상위 공개 리포 50,000개 이상을 색인해 두었다고 했다.4 이건 벤더 1차 발표이므로 그대로 “벤더 주장”으로 읽는 게 맞다 — 색인 품질에 대한 중립 제3자 평가는 필자가 확인한 범위에 없다.
기술적으로 흥미로운 건 Devin 문서 쪽이다. 리포 루트에 .devin/wiki.json을 두면 기본 자동 생성 동작을 조종할 수 있고, pages를 명시하면 클러스터 기반 자동 계획을 우회해 지정한 페이지만 만든다. 대형 리포가 내장 한도에 걸려 중요한 부분이 빠지는 걸 막기 위한 장치라고 설명한다. 또 README에 배지를 달아 두면 위키를 자동 갱신한다고 한다.5
두 가지를 확인해 주는 대목이다. 첫째, 자동 생성만으로는 큰 리포를 못 덮는다 — 어디를 문서화할지에 대한 사람의 선언이 결국 필요하다. 둘째, 갱신 트리거가 설계의 일부다 — 배지든 ingested_sha든, 언제 다시 만들지를 정하지 않으면 위키는 그냥 낡는다.
직접 만든 로컬 파이프라인이 갖는 차이는 명확하다. 프라이빗 리포를 외부에 보내지 않고, 내 리포의 어휘(정산·대사·마감)와 아키텍처 문서(docs/architecture/)에 맞춰 페이지를 만들 수 있으며, 슬래시 명령으로 에이전트 워크플로에 직접 물릴 수 있다. 반대로 남의 서비스가 대신 해 주는 유지보수를 전부 직접 진다.
6. 새로 생긴 비용 — 정직하게
이런 파이프라인을 세우면 없던 문제가 네 개 생긴다.
① 위키도 낡는다. 이건 주석이 낡는 것과 정확히 같은 실패다. 달라진 건 ingested_sha 덕분에 낡음을 탐지 가능하게 만들었다는 것뿐이고, 탐지와 해소는 다른 일이다. 재생성을 돌리지 않으면 낡은 문서가 그냥 신뢰만 얻은 채로 남는다. 오히려 위험하다 — 사람이 쓴 주석은 의심하지만 “자동 생성”이라는 딱지는 근거 없는 신뢰를 준다.
② 생성 비용. 파일 수 × LLM 호출이다. 증분 재생성(content hash)이 이 비용을 관리 가능한 수준으로 낮추는 유일한 장치이고, 그래서 저 화면에서 state.json이 부수적 항목이 아니라 핵심이다. 콘텐츠 해시가 없으면 4,582개 파일 리포에서 위키는 한 번 만들고 방치되는 물건이 된다.
③ 진실 원본이 두 개가 된다. 코드와 위키가 어긋났을 때 무엇이 맞는가. 답은 항상 코드지만, 사람은 읽기 쉬운 쪽을 믿는다. 위키 페이지에 ingested_sha와 원본 파일 경로를 반드시 함께 박아 두어야 하는 이유다.
④ 검증 게이트가 아직 없다. 이게 가장 큰 구멍이다. 생성된 문장이 코드와 실제로 일치하는지를 재는 자동 검사가 없으면, 그럴듯한 오답이 위키에 그대로 굳는다. 이 화면에는 빌드가 “완료됐다”는 보고는 있지만 “맞다”는 증거는 없다.
7. 다음에 할 일
④를 메우는 가장 싼 방법부터 정리하면 이렇다.
- 드리프트 리포트: CI에서
ingested_sha와HEAD사이의git diff --name-only를 돌려, 위키 페이지가 있는데 원본이 바뀐 파일 수를 출력한다. 임계치를 넘으면 실패시킨다. 구현 난이도가 가장 낮고 효과가 즉시 나온다. - 참조 존재 검사: 위키 페이지가 언급하는 클래스·메서드·설정 키가 실제로 존재하는지 문자열 수준에서 확인한다. 의미 검증은 못 하지만, 없는 이름을 지어낸 경우는 전부 잡힌다.
- 횡단 주제 페이지의 인용 강제:
topic산출물의 모든 주장에 원본 파일 경로를 달게 하고, 경로 없는 문장 비율을 지표로 재는 것. 위키가 서사를 쓸수록 근거에서 멀어지므로 여기가 가장 위험하다.
정리하면, 코드 위키의 값은 “문서가 생긴다”가 아니다. 문서와 코드 사이의 거리를 숫자로 잴 수 있게 된다는 것이다. ingested_sha 한 줄이 그 전부다.
References
본문의 리포 통계(4,582 / 14 / 1,084 / 185)는 2026-09-09 로컬 체크아웃에서 직접 집계한 값이다. 화면의 코드 위키 빌드는 필자 환경의 실제 실행 출력이며, 생성된 위키 페이지의 정확도에 대한 정량 평가는 아직 수행하지 않았다 — 6절 ④가 그 이유다.
-
F. Wen, C. Nagy, G. Bavota, M. Lanza, “A Large-Scale Empirical Study on Code-Comment Inconsistencies,” 27th IEEE/ACM International Conference on Program Comprehension (ICPC 2019). 1,500개 Java 시스템, 3,323,198 커밋, 약 13억 건의 AST 수준 변경 분석. DOI 10.1109/ICPC.2019.00019 · 저자 PDF · 재현 패키지 ↩
-
Scott Chacon, Ben Straub, Pro Git (2nd ed.), “10.2 Git Internals — Git Objects”. Git이 콘텐츠 주소화 파일시스템이라는 설명. https://git-scm.com/book/en/v2/Git-Internals-Git-Objects ↩
-
Anthropic, “Introducing Contextual Retrieval,” 2024-09-19. 상위 20개 청크 검색 실패율 5.7% → 3.7%(Contextual Embeddings) → 2.9%(+Contextual BM25) → 1.9%(+리랭킹). 벤더 1차 실험 결과이며, 평가 데이터셋과 프롬프트는 공개 쿡북에 포함돼 있다. https://www.anthropic.com/engineering/contextual-retrieval · Claude Cookbook 가이드 ↩
-
The Cognition Team, “DeepWiki: AI docs for any repo,” 2025-05-05. 공개 시점 상위 공개 리포 50,000개 이상 색인 — 벤더 자체 발표. https://cognition.com/blog/deepwiki ↩
-
Devin Docs, “DeepWiki repository wikis” —
.devin/wiki.json을 통한 위키 생성 조종, 배지 기반 자동 갱신. https://docs.devin.ai/work-with-devin/deepwiki ↩