CLAUDE.md를 헌법처럼 작성하라. 금지 목록, 행동 지침, 기본 결정. 5일 92 커밋, 이탈 없음.
5일 동안 92개 커밋을 쌓아 제품을 프로덕션에 올렸다(github.com/defi-io/smarts). 중간에 사고도, 롤백도, 큰 리팩터링도 없었다. 그게 가능했던 건 Claude가 천재라서가 아니다. 프로젝트 루트에 있는 565줄짜리 CLAUDE.md 덕분이다.
이 글은 헌법으로 작동하는 CLAUDE.md를 어떻게 써야 하는지, 그리고 다 쓴 다음 무슨 일이 일어나는지에 대한 이야기다.
많은 사람이 CLAUDE.md를 README처럼 쓴다 — 프로젝트가 무엇이고 어떻게 실행하는지. 그건 낭비다.
README는 사람이 읽는다. CLAUDE.md는 Claude가 읽는다. Claude는 session이 시작될 때마다 이 파일을 자동으로 로드한다. 즉 거기 적은 한 줄 한 줄이 이후 모든 결정의 전제 조건이 된다.
README식으로 쓰면 이렇게 된다: Claude는 프로젝트가 대략 무엇인지, 테스트를 어떻게 돌리는지는 알지만, 기술 결정은 매번 백지에서 내린다.
헌법식으로 쓰면 이렇게 된다: Claude는 어느 길이 막혀 있는지, 어느 규약이 필수인지, 갈림길에서 어느 쪽으로 기본 선택하는지, 어떤 상황에서 멈추고 물어봐야 하는지를 안다.
차이는 이거다: 전자는 "겸사겸사" 새 의존성을 끌어들이고, 추상화 계층을 하나 얹고, Gemfile을 손댄다. 후자는 그러지 않는다.
최근 만들고 있는 프로젝트는 smarts.md — 스마트 컨트랙트의 live docs와 MCP server를 생성하는 플랫폼이다. CLAUDE.md는 565줄, 이렇게 나뉜다:
1. 프로젝트 정체성 (5줄)
2. 제품 핵심 (한 줄 정의 + 경쟁사와의 구조적 차이)
3. 기술 스택 (강한 제약, YAML 형식)
4. 금지 목록 (명시적인 ❌ 항목)
5. 지원 범위 (MVP의 강한 경계)
6. 아키텍처 (ASCII 다이어그램 포함)
7. 디렉터리 구조 (파일별 책임 명시)
8. Ruby 코딩 규약 (좋은 예/나쁜 예 스니펫 포함)
9. 캐시 전략 (표)
10. 테스트 요구사항
11. 배포 세부사항
12. Git 워크플로
13. Claude Code 사용 규약 (행동 지침)
14. 90일 마일스톤
15. 핵심 원칙
어느 하나 장식이 아니다. 아래에서 몇 개를 골라 왜 이렇게 쓰는지 설명한다.
내 금지 목록은 이렇게 생겼다:
❌ TypeScript / Node.js 서비스 도입 금지
❌ ethers.js / web3.js 도입 금지
❌ Sidekiq 도입 금지 (Solid Queue 사용)
❌ React / Vue / Svelte 도입 금지 (Hotwire 사용)
❌ Redis 도입 금지 (Solid Cache / Solid Queue / Solid Cable 로 전부 커버)
❌ 미검증 컨트랙트 처리 금지 (Etherscan 검증된 것만)
❌ Solana / Bitcoin / Move 계열 처리 금지 (EVM 만)
이건 Web3 프로젝트다. Web3 바닥의 기본은 TypeScript + ethers.js + Redis + React. 나는 그 반대로 간다. 이유는 내가 갖고 있지만 Claude는 모른다. 금지 목록을 안 쓰면 Claude는 학습 데이터에 깔린 "업계 상식"에 따라 제안한다 — 거의 확실하게 TypeScript 노선이다.
금지 목록을 쓴 뒤에는 Claude가 "web3.js 돌릴 Node 서비스 하나 추가할까요?"라고 묻지 않는다. 그 길은 기본값으로 막혀 있다.
핵심: 각 항목은 명시적이고 절대적이어야 한다. "TypeScript는 가급적 피한다"가 아니라 "TypeScript 도입 금지". 모호하면 Claude는 협상 가능한 것으로 읽는다.
CLAUDE.md의 마지막 큰 블록은 "Claude Code 사용 규약"이다, 이렇게:
### 내가 "X 시작해" 라고 하면
1. CLAUDE.md 의 제약이 이 접근을 허용하는지 먼저 확인
2. 기존 코드에 관련 구현이 있는지 확인 (바퀴 재발명 금지)
3. 코드 작성 전 새 브랜치 생성
4. 구현 완료 후 테스트 실행
5. 자동 commit 금지 — 코드 다 짜고 테스트 통과 후 멈춰서 보고,
내가 명시적으로 "commit" 이라고 할 때까지 대기
### 내가 "아이디어가 있다" 고 하면
즉시 코드 쓰지 말 것. 먼저:
1. 이해한 내용 확인 (다시 말로 풀어서)
2. CLAUDE.md 제약과 대조 평가
3. 잠재적 문제나 더 나은 대안 제시
4. 내 확인 후 움직일 것
### 기술 선택 갈림길을 만나면
CLAUDE.md 에 명시가 없을 때:
1. "더 Rails 네이티브한" 옵션을 기본으로
2. "의존성이 더 적은" 옵션을 기본으로
3. "1인 개발자가 더 빨리 돌릴 수 있는" 옵션을 기본으로
4. 확신 없으면 멈추고 묻기
이 부분이 "snake_case로 명명"보다 열 배 중요하다.
코드 스타일은 Claude가 코드를 보면 익힌다. 하지만 "언제 멈추고 물어야 하는가", "'아이디어가 있다'를 들으면 먼저 다시 풀어서 확인하고 곧장 코드를 쓰지 마라" — 이런 건 행동 패턴이라, 코드만 봐서는 못 배운다.
가장 핵심 한 줄: 갈림길에서 어느 쪽을 기본으로 잡을지. 이 한 줄이 당신이 자리에 없을 때(예: "X 시작해"만 던지고 자리를 떠났을 때) Claude가 무엇을 할지를 결정한다. 이걸 명문화하면 안심하고 일을 맡길 수 있다.
CLAUDE.md 첫머리에 이런 줄이 있다:
이 문서는 프로젝트의 "제1 출처"다. Claude Code session 시작 시 자동으로 로드된다. 기술 결정을 바꿀 때는 먼저 여기를 고치고, 코드는 그다음에 따라가게 한다.
워크플로 약속이다. 기술 선택을 바꾸기로 했을 때 — 예를 들어 메인 AI 모델을 gpt-5-mini에서 gpt-5로 올린다 — 첫 단계는 initializer를 고치는 게 아니다. 첫 단계는 CLAUDE.md의 이 부분을 고치는 거다:
ai:
fast: gpt-5-mini # 이전
fast: gpt-5 # 변경 후
그다음에 Claude가 CLAUDE.md를 읽고 코드를 맞추게 한다.
왜? 코드의 어떤 값을 바꿨는데 CLAUDE.md가 못 따라오면, 다음번 Claude가 CLAUDE.md를 읽을 때 옛 값을 보고 "친절하게" 당신 코드를 되돌린다. 충돌의 뿌리는 단일 실패점 — CLAUDE.md가 뒤처진 것이다. 헌법이 현실보다 늦으면 내전이 난다.
CLAUDE.md를 헌법으로 다룬다는 말의 핵심: 항상 코드보다 앞서 달린다.
smarts.md, 5일, 92 커밋:
PR 평균 수명 30분. 큰 리팩터링도, "방향이 틀렸으니 롤백"도 없었다.
Claude가 마법을 부려서가 아니다. 매 session마다 565줄짜리 레일 위를 달렸기 때문이다. TypeScript를 들이지 말 것, Solid Queue가 기본 큐인 것, view 함수가 60초 캐시되는 것, 각 service는 call 클래스 메서드를 구현해야 하는 것, commit 전에 내 확인을 기다려야 하는 것 — 다 알고 있었다.
이 제약들 중 절반만 떼어내면 이렇게 된다: 매일 30분 "왜 X를 안 쓰는가"를 설명하고, 5분 동안 "겸사겸사" 들어온 의존성을 리뷰하고, Gemfile이 서서히 무너진다.
빈 파일에서 시작하지 마라. 가장 최근에 아팠던 결정에서 시작하라.
요즘 Claude가 "자꾸 새 의존성을 들인다"라면, 금지 목록을 써라.
"걸핏하면 아키텍처를 바꾼다"라면, 아키텍처 다이어그램 + "마이크로서비스 추가, DB 교체 등은 확인 필요".
"테스트를 안 쓴다"라면, "단위 테스트 필수 범위".
"커밋이 너무 공격적이다"라면, "자동 git commit 금지, 명시 지시 대기".
아플 때마다 한 줄 추가하라. 그 외에는 당분간 쓰지 마라.
3개월 뒤에는 500줄짜리 CLAUDE.md가 생긴다. 모든 줄 뒤에는 구체적인 "이 짓 다시는 시키지 말 것"이 있다. 이 문서는 어떤 템플릿보다 강하다. 당신의 프로젝트, 당신의 워크플로, 당신의 팀(혼자라도) 의 실제 제약에 정확히 맞춰져 있기 때문이다.
헌법은 한 번에 쓰이지 않는다. 구체적 충돌이 하나씩 결정화되어 만들어진다.