
당신의 Claude Code 프로젝트는 설정된 것이 아니라, 단지 설정된 것처럼 보일 뿐입니다.
요약
Claude Code의 /init 명령어는 단순한 설정 생성기가 아니라, 기존 CLAUDE.md 파일과 실제 코드베이스 간의 불일치를 찾아내는 '감사 모드(audit mode)'로 동작합니다. 문서와 실제 구현 사이의 간극인 '문서 드리프트'를 식별하여 프로젝트의 정확성을 높이는 것이 핵심입니다.
핵심 포인트
- /init 명령어는 기존 파일 존재 시 감사 모드로 전환됨
- 문서와 코드 간의 불일치인 '문서 드리프트'를 탐지함
- 단순 생성이 아닌 코드베이스와의 교차 참조를 수행함
- 아키텍처적 사실과 실제 구현의 차이(delta)를 드러냄
Claude Code에 제공하는 좋은 지침(instructions)은 실제로 출시되는 앱과 아무도 디버깅하고 싶어 하지 않는 버그투성이의 엉망진창인 앱 사이의 차이를 만듭니다. 새로운 프로젝트 온보딩(onboarding) 흐름은 그 작업을 인터뷰 형식으로 매우 단순화했습니다. 이는 명확하지 않은 부분을 추출하고, 훅(hooks)과 기술(skills)을 설정하며, 당신이 답변하는 것을 잊어버릴 만한 질문들을 던집니다. 하지만 이미 존재하는 CLAUDE.md 파일을 발견했을 때 어떤 일이 일어나는지는 여전히 알고 있어야 합니다.
그리고 일어나는 일은 대부분의 게시물에서 설명한 것과 다릅니다.
요약(TLDR): /init은 개선된 생성기(generator)가 아닙니다. 기존에 CLAUDE.md가 있는 저장소(repo)에서, 이는 **감사 모드(audit mode)**로 전환됩니다. 파일을 읽고, 락파일(lockfiles) 및 설정(configs)과 교차 참조하며, 코드가 진행되는 동안 일치하지 않게 된 부분을 드러냅니다. 4번째 저장소(64줄 분량)에서 저는 5개의 조용한 거짓말을 찾아냈습니다. 생성하는 것과 수정하는 것의 차이가 이 글의 핵심입니다.
새로운 저장소에서는 그렇습니다, 생성합니다. 커뮤니티는 합리적으로 그 사실을 바탕으로 움직였습니다. "설정이 10배 더 쉬워졌다",
각각의 결과물에서 제가 기대했던 것은 다음과 같습니다: 스택 섹션 (stack section), 명령어 블록 (commands block), 그리고 몇 가지 컨벤션 (conventions)이 포함된 CLAUDE.md 파일입니다. 제가 수동으로 정리해야 할 보일러플레이트 (Boilerplate) 말이죠. 즉, 시작점은 제공해주되 실제로 유용하게 쓰이려면 사람의 손길이 필요한 수준의 결과물을 기대했습니다.
하지만 대신 제가 마주한 것은 이랬습니다: 4개의 저장소 중 3개에서 /init 명령어가 기존 파일을 감지하자마자 동작 방식이 즉시 바뀌었습니다. 빈 저장소에서는 처음부터 생성합니다. 하지만 이미 파일이 존재하는 경우에는 접근 방식이 다릅니다. 현재의 CLAUDE.md를 읽고, 코드베이스 (codebase)를 읽은 뒤, 당신이 작성한 내용이 실제 코드와 더 이상 일치하지 않는 간극을 찾아냅니다.
**문서 드리프트 (Documentation drift)**는 스스로를 알리지 않습니다. 빌드 에러 (build error)나 테스트 실패 (failing test)와는 다릅니다. 그것은 마치 세이브 파일의 손상과 같습니다. 게임은 계속 실행되고 수치도 정상적으로 보이지만, 보스전이 시작되었을 때 스탯이 약속된 것과 맞지 않는 상황과 같습니다. 당신이 이를 알아차릴 때쯤이면, 이미 한동안 잘못된 가정 위에서 플레이를 해온 상태가 됩니다.
Claude Code의 문서에는 새로운 /init이 "Claude가 스스로 추론할 수 없는 내용만 포함한다"는 원칙을 따른다고 설명되어 있습니다. 기존 저장소의 경우 이는 다음과 같은 의미입니다: 먼저 코드가 실제로 무엇인지 파악한 다음, 이를 문서화된 내용과 비교하고, 그 **차이점 (delta)**을 드러내는 것입니다. 중요한 것은 바로 그 차이점 (delta)입니다.
이미 스스로를 알고 있었던 2개의 저장소
첫 번째 저장소는 저의 메인 제품 대시보드였습니다. 중심이 되는 프로젝트로, 약 150줄 정도의 CLAUDE.md가 초기에 작성되어 가끔 업데이트되었습니다. /init을 실행하자 11개의 새로운 라인이 반환되었습니다.
추가된 내용들은 파일에 없었던 아키텍처적 사실들이었습니다: 전체 시스템이 회전하는 중심이 되는 중앙 데이터 엔티티 (central data entity), 모든 헬퍼 로직을 특정 모듈로 모으는 라우팅 규칙 (routing rule), 2단계 인증 (2-level auth) (최종 사용자 대 머신 투 머신), 그리고 일반적인 요청 사이클 외부에서 실행되는 격리된 예약 작업 (scheduled task) 등이었습니다. 문서화된 것은 아무것도 없었습니다. 하지만 코드를 깊이 읽고 임포트 (imports)를 교차 참조한다면 모두 추론 가능한 내용들이었습니다.
그러더니 단 하나의 거짓을 찾아냈습니다. CLAUDE.md에는 npm install과 localhost:3000이 언급되어 있었습니다. 하지만 루트 (root)에는 bun.lock이 있었습니다. 프로젝트는 이미 몇 달 동안 다른 포트에서 실행되고 있었습니다. 두 사항 모두 어느 시점에는 사실이었지만, 둘 다 조용히 사실이 아니게 되었음에도 아무것도 망가지지 않았습니다. npm 명령어는 bun 프로젝트에서도 대부분 여전히 실행됩니다. 잘못된 포트는 그저 한 번 잘못된 URL을 시도했다가, 올바른 URL을 찾은 뒤 잊어버리게 만들 뿐입니다. 놓치기 쉽고, 방치하기 쉽습니다.
두 번째 리포지토리 (Repo 2)는 다른 종류의 문제였습니다. 매번 중요한 변경 사항이 있을 때마다 업데이트되는, 약 700줄에 달하는 세심하게 관리된 CLAUDE.md가 있는 프로젝트였습니다. /init은 전체 내용을 읽고 코드베이스 (codebase)를 스캔한 뒤, 단 3줄의 결과만을 내놓았습니다. 최근에 bun test 러너 (runner)가 추가되었으나 문서화되지 않았던 것입니다. 코드 재작성도, 구조적 변경도 없었으며, 단지 누락된 명령어뿐이었습니다.
그리고 다음과 같은 판결이 내려졌습니다: "당신의 CLAUDE.md는 이미 이곳에서 /init이 생성할 수 있는 수준을 훨씬 넘어섰습니다."
(저는 이 내용과는 전혀 상관없는, 완전히 별개의 프로젝트에 대해 500줄 분량의 내부 문서를 작성하고 있습니다. 글을 쓰는 과정이 생각을 명확하게 해주기 때문에 2년 동안 업데이트해 오고 있습니다. 한 번은 제가 섹션 헤더 (section header)를 다시 쓰는 동안 딸이 들어오더니, 왜 내 코드에 관한 책을 쓰고 있느냐고 묻더군요. 제대로 된 대답을 하지 못했습니다. 어쨌든 말이죠.)
언제 멈춰야 할지를 아는 도구는, 언제 시작해야 할지를 아는 도구만큼이나 희귀합니다.
지도가 없는 리포지토리
세 번째 리포지토리: **멀티 프론트엔드 카탈로그 배송 시스템 (multi-frontend catalog delivery system)**입니다. 여러 개의 Astro 프론트엔드, Hono 백엔드, SQLite로 구성되어 있었으며, CLAUDE.md도 없고 루트 (root)에 README도 없었습니다. 구조가 명확했기에 빠르게 시작했고, 문서화는 나중에 하기로 했으며, 그 '나중'은 계속해서 뒤로 밀려났습니다.
/init은 **4개의 병렬 도구 패스 (parallel tool passes)**를 통해 완전한 아키텍처 지도 (architecture map)를 반환했습니다. 독립적인 읽기 작업들이 동시에 수행됩니다: 루트 설정 (root config), Hono 라우트 (routes), Astro 설정 (configs), 빌드 스크립트 (build scripts), 배포 노트 (deploy notes). 호출들이 서로를 기다리지 않기 때문에 재구성 (reconstruction) 속도가 매우 빠릅니다.
이 원리는 Claude Code가 기존 코드베이스로부터 프로젝트 컨텍스트를 구축하는 방법에서와 마찬가지로 동일하게 적용됩니다. 구조화되고 정확한 지도는 이후의 세션들이 엉뚱한 방향으로 흐르는 것을 막아주는 거의 유일한 작업입니다. 지도가 존재하고 정확하거나, 그렇지 않으면 모델이 즉흥적으로 판단(improvising)하게 되는데, 잘못된 그림을 바탕으로 즉흥적인 판단을 내리는 것은 빠르게 문제를 악화시킵니다.
제가 따로 기록하지 않았을 수도 있었던, 드러난 핵심 불변량(invariant)은 다음과 같습니다: Astro는 Node 환경에서 실행되며, 그 외 모든 것은 Bun 환경에서 실행된다는 점입니다. 이 세부 사항은 배포 스크립트의 주석과 빌드 스크립트의 fnm use 22 호출 속에 파묻혀 있었습니다. Astro의 빌드 프로세스가 작동하는 방식에 특화된 아키텍처 제약 사항이었지만, 문서화는 전혀 되어 있지 않았습니다. 이 저장소(repo)에서의 모든 Claude Code 세션은 이 사실을 모른 채 작동해 왔습니다.
4번의 패스(passes)를 통해 구체적으로 조립된 결과물은 다음과 같습니다: SQLite 스키마, 콘텐츠를 사이트별 JSON 번들로 직렬화하는 내보내기 파이프라인(export pipeline), Node 22 환경에서의 Astro 빌드 순서(Astro의 SSG 렌더러가 이를 요구했기 때문에 Bun이 아닌 Node 사용), 이미 정의된 재작성 규칙(rewrite rules)과 함께 nginx로 전달되는 rsync 단계, 그리고 전체 시스템 앞단에서 에지 라우팅(edge routing)을 처리하는 Cloudflare Worker입니다. 가공되지 않은 SQLite 행(row)부터 Cloudflare 뒤편의 렌더링된 정적 파일에 이르기까지의 전체 전달 체인(delivery chain)이 단 한 번의 패스로 문서화되었습니다. 디렉토리 트리나 일반적인 프로젝트 조언이 아닙니다. 질문할 필요 없이 탐색할 수 있을 만큼 충분히 구체적입니다.
64줄 속에 담긴 5가지 거짓말

문서 드리프트 탐지 보드: 5가지 숨겨진 코드의 거짓말
네 번째 저장소는 저의 제품 처리 파이프라인입니다. 풀스택 구성이며, Convex 백엔드, Vite/Express 프론트엔드, 그리고 Infisical을 통해 관리되는 비밀값(secrets)을 사용합니다. CLAUDE.md 파일은 3개월 전에 작성되었으며, 당시에는 정확했던 64줄의 내용으로 구성되어 있었습니다.
/init 명령은 문서가 조용히 허구로 변해버린 5곳을 찾아냈습니다.
1. 패키지 매니저 (The package manager)
CLAUDE.md는 npm install, npm run dev를 지시하고 있었습니다. 하지만 루트 디렉토리에는 bun.lock 파일이 있었습니다. 어느 시점에 bun으로의 마이그레이션(migration)이 이루어졌으나 문서는 이를 따라가지 못했던 것입니다. Claude Code는 bun 프로젝트에서 npm 명령어를 실행하고 있었으며, 대부분의 npm 명령어가 여전히 실행되기 때문에 오류 없이 조용히 작동했습니다. 이러한 불일치는 의존성 해결(dependency resolution) 방식이 달라지고, 왜 그런지 파악하느라 오후 시간을 통째로 허비할 때까지 숨겨져 있습니다.
2. 누락된 워크플로우 상태 (The missing workflow states)
CLAUDE.md는 Convex 스키마(schema)에 있는 3가지 주문 상태 코드를 기록했습니다: draft, published, archived. 하지만 실제 스키마에는 7개가 있었습니다. 누락된 4개는 processing, failed, review, scheduled와 같이 예외적인 상황을 처리하는 상태들이었습니다. 이는 사소하지 않은 모든 작업에서 매우 중요한 상태들입니다. 모든 Claude Code 세션은 4개의 상태가 부족한 시스템 모델을 바탕으로 로직을 작성해 오고 있었습니다.
3. Convex 폴더 (The Convex folder)
CLAUDE.md는 convex/를 주요 처리 계층(processing layer)으로 설명했습니다. 초기에는 그것이 사실이었습니다. 하지만 3개월에 걸쳐 주요 작업들이 server.js로 마이그레이션되었습니다: 제품 시트를 위한 PDF 생성, 이미지 크기 조정, 파트너 통합을 위한 웹훅(webhook) 발송 등 말입니다. Convex 함수들은 얇은 라우팅(routing) 역할만 하게 되었습니다. 실제 처리는 문서에서 언급하기를 멈춘 파일에서 일어나고 있었습니다.
4. 문서화되지 않은 모드 전환 (The undocumented mode switch)
루트에 있는 쉘 스크립트(shell script)는 배포 대상에 따라 스택을 관리형 Convex Cloud와 자체 호스팅되는 Convex 인스턴스 사이에서 전환했습니다. CLAUDE.md에는 이에 대한 언급이 전혀 없었습니다. 모든 Claude Code 세션은 정적인 백엔드 연결을 가정하고 있었지만, 실제로는 데이터가 저장되는 위치에 실질적인 영향을 미치는 수동 전환 단계가 존재했습니다.
5. 찾으려면 파일 3개가 필요한 포트 (The port that takes 3 files to find)
CLAUDE.md는 개발 서버가 3150 포트에서 실행된다고 명시했습니다. 실제 포트는 3002였습니다. Vite가 3002 포트의 Express로 프록시(proxy)를 설정했고, PORT는 Infisical에 의해 주입되었으며, vite.config.ts가 이 세 가지 사이의 가교 역할을 하고 있었습니다. 실제 포트를 찾으려면 3개의 파일을 교차 참조(cross-reference)해야 했습니다. /init은 그 교차 참조를 수행했지만, 저는 3개월 동안 하지 않았습니다.
누구도 의도적으로 거짓말을 한 것은 아니었습니다. CLAUDE.md는 정확했습니다. 코드는 항상 그렇듯 움직였습니다. 극적인 재작성(rewrites)이 아니라, 매주 10번씩 이루어지는 결정들을 통해 움직였습니다. 각 결정은 문서 업데이트가 필요하다고 느낄 만큼 크지 않았습니다. 3개월이 지난 후, 그 결정들 중 5개는 기록된 내용에서 벗어나 있었습니다.
막간: 지저분한 git 트리 (dirty git tree)
이 저장소(repo)에는 커밋되지 않은 상태(uncommitted state)가 있었습니다. 삭제된 package-lock.json, 루트 디렉토리에 놓인 추적되지 않는(untracked) 백업 파일 등이 있었습니다. /init은 저장소를 건너뛰지 않았고, 트리를 강제하지 않았으며, 작업 디렉토리(working directory)를 덮어쓰지도 않았습니다.
/init은 5단계의 git 단계를 순차적으로 실행했습니다: 전용 브랜치에 **워크트리 (worktree)**를 생성하고, 격리된 상태에서 수정을 가한 뒤, 관습적인 커밋 메시지(conventional commit message)와 함께 커밋했습니다. 그 후 main 브랜치로 fast-forward 머지(merge)를 수행하고, 워크트리를 정리한 뒤 브랜치를 삭제했습니다. 작업 중인 내용(WIP)은 건드리지 않았고, git 히스토리는 깔끔했습니다.
이 작업이 깔끔하게 작동한 이유는 이 워크플로우를 강제하는 버전 관리된 git 훅 (versioned git hooks) 덕분이었습니다. 모델이 신중함을 선택한 것이 아닙니다. 모델은 스스로 우회할 수 없는 프로젝트 제약 조건 내에서 작동한 것입니다. 이는 모델의 선의(goodwill)에 의존하는 것과는 다른 차원의 보장이며, 어느 쪽에 얼마나 많은 신뢰를 둘지 결정하기 전에 모델의 선의와 실제 도구의 규율 사이의 차이를 이해할 가치가 있습니다.
/init이 할 수 없는 것
/init은 추적 가능한 것들을 감사(audit)합니다. 읽을 수 있는 파일, 교차 참조(cross-reference)할 수 있는 설정, 문서와 비교할 수 있는 스키마(schema) 등이 대상입니다. 이는 결과물이 단순해 보인다는 이유로 과소평가되어 있지만, 실질적인 능력입니다. 포트(port)에 대한 거짓말 하나만으로도 저는 원치 않았던 디버깅 세션을 피할 수 있었습니다.
하지만 /init이 손댈 수 없는 지식의 범주가 존재합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기