들어가며
21차시에서는 CLAUDE.md, 22차시에서는 Auto memory를 다뤘다. CLAUDE.md는 매 세션에 전체가 로드되고, Auto memory는 머신-로컬에 쌓인다.
그런데 프로젝트가 커질수록 CLAUDE.md 한 파일로는 부족해진다. TypeScript 작업할 때만 필요한 규칙, API 라우트 만질 때만 필요한 컨벤션, 테스트 파일에만 적용되는 패턴이 한 파일에 다 들어가면 — 매 세션마다 200줄, 300줄을 그대로 컨텍스트에 싣게 된다. 공식 문서가 200줄을 권장하는 이유다. 길수록 토큰을 더 먹고 준수율은 더 떨어진다.
이번 차시는 CLAUDE.md를 쪼개는 두 가지 메커니즘을 다룬다.
.claude/rules/— 토픽별로 파일을 쪼개고,paths:프론트매터로 특정 파일을 만질 때만 로드되게 한다@멘션 import —CLAUDE.md나 rules 파일에서 다른 마크다운을 끌어와 합친다
그리고 이 모든 걸 .claude/ 디렉토리째로 Git에 커밋하면 팀원이 클론만 해도 같은 규칙이 자동으로 적용된다.
CLAUDE.md만으로 부족해지는 순간
CLAUDE.md는 매 세션 시작 시 컨텍스트에 들어간다. 컨텍스트는 토큰이고 토큰은 비용이다. 그런데 길이만 문제가 아니라 준수율이 문제다.
공식 문서가 명시하는 권장 한도는 파일당 200줄이다. 그 이상이면 준수율이 떨어진다고 못박혀 있다. 또 CLAUDE.md는 시스템 프롬프트가 아니라 시스템 프롬프트 다음의 사용자 메시지로 주입된다. 강제 설정이 아니라 컨텍스트라는 뜻이다. 짧고 구체적일수록 따라온다.
길어지는 패턴은 대개 비슷하다.
- TypeScript 컨벤션 (any 금지, strict 모드…)
- React 컴포넌트 규칙 (함수형, props interface…)
- 테스트 작성 규칙 (Given-When-Then, 커버리지…)
- API 라우트 규칙 (입력 검증, 에러 포맷…)
- 보안 규칙 (자격 증명 처리, 로그…)
이 모두가 한 CLAUDE.md에 쌓이면 어떻게 될까. 사용자가 README만 손보는 세션에서도 React 규칙과 테스트 규칙이 컨텍스트에 다 들어간다. 토큰 낭비도 낭비지만, Claude가 어떤 규칙을 적용할지 판단할 때 노이즈가 늘어나서 정작 중요한 지시를 흐리게 한다.
해결은 두 단계다.
- 토픽별로 파일을 쪼갠다 →
.claude/rules/ - 특정 파일을 만질 때만 로드되게 한다 →
paths:프론트매터
.claude/rules/ 디렉토리
.claude/rules/는 CLAUDE.md를 토픽별로 쪼개기 위한 디렉토리다. 한 파일당 한 토픽, 파일명은 내용을 드러내게 짓는다.
your-project/
├── .claude/
│ ├── CLAUDE.md # 핵심 지시
│ └── rules/
│ ├── code-style.md # 코드 스타일
│ ├── testing.md # 테스트 컨벤션
│ └── security.md # 보안 규칙
└── src/
.md 파일은 재귀적으로 발견된다. 서브디렉토리를 만들어서 분야별로 묶어도 된다.
.claude/rules/
├── frontend/
│ ├── react.md
│ └── styling.md
├── backend/
│ ├── api.md
│ └── database.md
└── security.md
기본 동작은 단순하다. paths: 프론트매터가 없으면 매 세션 시작 시 무조건 로드된다. 우선순위는 .claude/CLAUDE.md와 같다. 즉 무조건 로드되는 rules 파일은 CLAUDE.md를 토픽별로 쪼갠 것에 지나지 않는다.
진짜 힘은 다음 절의 path-scoped rules에 있다.
path-scoped rules: 매칭 파일을 만질 때만 로드
paths: 프론트매터를 쓰면 특정 파일을 Claude가 읽을 때만 그 규칙이 로드된다. 매 세션 시작 시 로드되는 게 아니라, 매칭 파일을 읽는 순간 컨텍스트에 추가된다.
---
paths:
- "src/api/**/*.ts"
---
# API 개발 규칙
- 모든 API 엔드포인트는 입력 검증 필수
- 에러 응답은 표준 포맷 사용
- OpenAPI 주석 포함
이 파일은 src/api/ 아래 .ts 파일을 Claude가 읽을 때만 로드된다. README만 손보는 세션에서는 컨텍스트에 들어가지 않는다. 트리거는 매 도구 호출이 아니라 매칭 파일을 읽는 시점이라는 점이 중요하다. Claude가 파일을 한 번 읽고 나면 그 세션 안에서는 규칙이 활성화된 상태로 유지된다.
글롭 패턴
paths:에는 글롭 패턴을 쓴다.
| 패턴 | 매칭 |
|---|---|
**/*.ts | 모든 디렉토리의 TypeScript 파일 |
src/**/* | src/ 아래 모든 파일 |
*.md | 프로젝트 루트의 마크다운 |
src/components/*.tsx | 특정 폴더의 React 컴포넌트 |
여러 패턴을 나열하거나 브레이스 확장으로 한 번에 묶을 수도 있다.
---
paths:
- "src/**/*.{ts,tsx}"
- "lib/**/*.ts"
- "tests/**/*.test.ts"
---
무조건 로드 vs path-scoped 정리
| 종류 | 프론트매터 | 로드 시점 | 우선순위 |
|---|---|---|---|
| 무조건 | paths: 없음 | 매 세션 시작 | .claude/CLAUDE.md와 동등 |
| path-scoped | paths: 있음 | 매칭 파일을 읽을 때 | path-scoped 활성화 후에는 동등 |
경험칙: 매 세션에 필요한 핵심 규칙은 CLAUDE.md 또는 paths: 없는 rules에. 특정 영역을 만질 때만 필요한 규칙은 paths:가 있는 rules에.
실전 예시: TypeScript 규칙
---
paths:
- "**/*.ts"
- "**/*.tsx"
---
# TypeScript 규칙
- `any` 금지. 정말 모를 땐 `unknown`을 쓰고 좁혀라
- 모든 public 함수에 반환 타입 명시
- 제네릭 파라미터 이름은 의미 있게 (`T` 대신 `TData`)
## 예시
\`\`\`typescript
// 좋은 예
function fetchData<TData>(url: string): Promise<TData> {
return fetch(url).then(res => res.json());
}
// 나쁜 예
function fetchData(url: string): Promise<any> {
return fetch(url).then(res => res.json());
}
\`\`\`
.ts나 .tsx를 Claude가 읽을 때만 이 규칙이 활성화된다. README, 마크다운 문서, 설정 JSON 작업에는 들어가지 않는다.
@ 멘션으로 외부 문서 import
CLAUDE.md와 rules 파일은 @path/to/file 문법으로 다른 파일을 import할 수 있다. import된 파일은 launch 시점에 펼쳐져서 컨텍스트에 같이 로드된다.
# CLAUDE.md
프로젝트 개요는 @README.md를 참조하고,
사용 가능한 npm 명령은 @package.json을 보라.
## 추가 지침
- git 워크플로 @docs/git-instructions.md
- 아키텍처 @docs/architecture.md
상대 경로(파일 기준)와 절대 경로 모두 허용된다. 상대 경로는 working directory 기준이 아니라 import한 파일 기준이라는 점에 주의한다.
재귀 import
import된 파일이 또 import할 수 있다. 최대 5단계까지 재귀가 허용된다.
CLAUDE.md
→ @docs/architecture.md
→ @docs/database-schema.md
→ @docs/migrations.md
→ @docs/migration-conventions.md
→ @docs/legacy-notes.md ← 여기까지 (5 hops)
첫 외부 import 시 승인 다이얼로그
처음 외부 파일을 import할 때 Claude Code는 승인 다이얼로그를 띄운다. 어떤 파일들이 로드되는지 보여준다. 거부하면 import는 비활성화되고 다이얼로그도 다시 안 나온다. 나중에 마음을 바꾸려면 별도로 처리해야 한다.
worktree 간 개인 설정 공유
Git worktree를 여러 개 쓰면 CLAUDE.local.md는 worktree마다 따로 존재한다(gitignore되어 있어서). 모든 worktree에서 같은 개인 설정을 쓰고 싶으면 홈 디렉토리에서 import하면 된다.
# 개인 선호
- @~/.claude/my-project-instructions.md
@~/.claude/...는 home-relative 절대 경로다. 어느 worktree에 있든 같은 파일이 펼쳐진다.
import vs path-scoped: 헷갈리지 말 것
@import는 launch 시점에 무조건 펼쳐진다. 컨텍스트에 항상 들어간다.
paths: rules는 매칭 파일을 읽을 때만 들어간다. 컨텍스트를 아낄 수 있다.
긴 파일을 import로 합치면 단순히 한 파일로 합친 것과 같다. 컨텍스트는 줄지 않는다. 컨텍스트를 줄이고 싶으면 path-scoped rules를 쓰는 게 맞다.
AGENTS.md 호환
다른 AI 코딩 에이전트(예: Codex, Cursor)는 AGENTS.md를 읽는다. Claude Code는 AGENTS.md를 직접 읽지 않는다. 한 저장소가 여러 도구를 같이 쓰고 있다면 둘 다 만족시키는 패턴이 있다.
<!-- CLAUDE.md -->
@AGENTS.md
## Claude Code 전용
`src/billing/` 아래 변경은 plan mode를 써라.
AGENTS.md를 import해서 공통 지침은 한 곳에 두고, Claude 전용 지침은 import 아래에 덧붙인다. 양쪽 도구가 같은 베이스를 보면서 도구별 추가 지침만 따로 두는 구조다.
사용자 전역 rules: ~/.claude/rules/
머신 전체에 적용되는 개인 rules는 ~/.claude/rules/에 둔다. 프로젝트와 무관한 본인 선호 — 코딩 스타일, 자주 쓰는 워크플로 — 같은 것들이다.
~/.claude/rules/
├── preferences.md # 개인 코딩 선호
└── workflows.md # 자주 쓰는 워크플로
여기서 우선순위 규칙을 알아두자. 공식 문서는 다음과 같이 명시한다.
User-level rules are loaded before project rules, giving project rules higher priority.
User rules가 먼저 로드되고 project rules가 나중에 로드된다. 충돌하는 지시가 있을 때 Claude는 나중에 본 것을 더 가까운 컨텍스트로 인식하기 쉽기 때문에, project rules가 사실상 더 높은 우선순위를 가진다. 팀 표준이 개인 선호를 덮어쓴다고 보면 된다.
같은 토픽에서 두 rules가 정반대 지시를 담고 있으면 Claude가 임의로 하나를 고를 수 있다. 충돌은 정기적으로 정리한다.
팀과 규칙 공유하기
.claude/ 디렉토리는 Git에 커밋해도 안전하다. CLAUDE.md, .claude/CLAUDE.md, .claude/rules/까지 묶어서 커밋하면 팀원이 클론한 순간 같은 규칙이 자동 적용된다.
git add .claude/
git commit -m "docs: 팀 코딩 규칙 추가"
git push
팀원이 한 일은 git pull이 전부다.
git pull
claude
# .claude/rules/ 자동 로드
다만 두 가지를 분리해서 생각한다.
- 공유해야 할 것: CLAUDE.md,
.claude/CLAUDE.md,.claude/rules/*.md - 공유하면 안 되는 것:
CLAUDE.local.md(개인),.claude/settings.local.json(개인)
.gitignore에 CLAUDE.local.md와 .claude/settings.local.json을 미리 넣어둔다. /init로 CLAUDE.md를 생성할 때 이 작업을 자동으로 해준다.
심볼릭 링크로 여러 프로젝트에 규칙 공유
같은 회사의 여러 저장소에 동일한 보안 규칙, 회사 표준을 적용하고 싶을 때 매번 복사하는 건 유지보수가 힘들다. .claude/rules/는 심볼릭 링크를 지원한다.
# 회사 표준 디렉토리를 통째로 심볼릭 링크
ln -s ~/shared-claude-rules .claude/rules/shared
# 개별 파일만 링크
ln -s ~/company-standards/security.md .claude/rules/security.md
심볼릭 링크는 정상적으로 풀어서 로드된다. 순환 심볼릭 링크는 감지해서 처리한다. 한 곳에서 표준을 업데이트하면 모든 프로젝트가 자동으로 따라간다.
심볼릭 링크된 디렉토리/파일은 git에 커밋되면 링크 자체가 커밋된다. 팀원이 같은 경로(
~/shared-claude-rules)를 가지고 있어야 동작한다. 팀이 이걸 자동화하고 싶으면 setup 스크립트를 PR에 포함하는 패턴이 흔하다.
추가 디렉토리에서 rules 로드하기
--add-dir 플래그로 working directory 외부 폴더를 같이 보여줄 수 있다(파일 접근 권한). 그런데 기본적으로는 추가 디렉토리의 CLAUDE.md와 rules는 로드되지 않는다. 별도 환경 변수로 켠다.
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 claude --add-dir ../shared-config
이렇게 하면 추가 디렉토리의 CLAUDE.md, .claude/CLAUDE.md, .claude/rules/*.md, CLAUDE.local.md가 로드된다(--setting-sources에서 local을 빼면 CLAUDE.local.md는 제외).
여러 저장소가 공통 설정을 공유하는 모노레포-옆-공통-디렉토리 구조에서 유용하다.
모노레포에서 다른 팀 CLAUDE.md 제외하기
모노레포에서 여러 팀이 각자 CLAUDE.md를 두고 있을 때 — Claude Code가 디렉토리 트리를 위로 거슬러 올라가면서 발견하는 모든 CLAUDE.md를 합쳐 로드한다. 다른 팀의 지시가 내 작업에 노이즈로 들어온다.
이럴 땐 claudeMdExcludes 설정으로 특정 경로를 건너뛴다.
{
"claudeMdExcludes": [
"**/monorepo/CLAUDE.md",
"/home/user/monorepo/other-team/.claude/rules/**"
]
}
패턴은 절대 경로에 글롭으로 매칭된다. user, project, local, managed policy 어느 레이어에서든 설정할 수 있고 여러 레이어의 배열은 합쳐진다. 이 설정은 머신-로컬에 두는 게 자연스러우니 보통 .claude/settings.local.json에 넣는다.
Managed policy CLAUDE.md(조직이 IT/DevOps로 배포하는)는
claudeMdExcludes로도 제외할 수 없다. 조직 단위 표준은 무조건 적용되도록 설계됐다.
/memory로 어떤 규칙이 로드됐는지 확인
22차시에서 /memory를 다뤘다. 다시 떠올려보면 /memory는 다음을 보여준다.
- 현재 세션에 로드된 모든 CLAUDE.md, CLAUDE.local.md, rules 파일 목록
- Auto memory 토글
- Auto memory 폴더 열기 링크
- 파일 선택 시 에디터로 열기
.claude/rules/를 도입한 직후에는 /memory로 의도한 파일이 실제로 로드되는지 확인한다. 보이지 않는 파일이 있다면 Claude가 못 보는 거다.
InstructionsLoaded 훅으로 디버깅
path-scoped rules는 매칭 파일을 읽는 시점에 동적으로 로드되기 때문에, “왜 이 규칙이 안 먹지?” 같은 의문이 생기기 쉽다. InstructionsLoaded 훅이 이런 디버깅에 쓰는 도구다.
이 훅은 CLAUDE.md나 .claude/rules/*.md가 컨텍스트에 로드될 때마다 발화한다. 차단하거나 결정에 개입하지는 못하고 — 관찰 전용이다.
{
"hooks": {
"InstructionsLoaded": [
{
"matcher": "session_start|path_glob_match",
"hooks": [
{
"type": "command",
"command": "echo 'Loaded: ${file_path}' >> ~/instruction-audit.log"
}
]
}
]
}
}
매처 값은 다음과 같다.
| 매처 | 의미 |
|---|---|
session_start | 세션 시작 시 로드된 파일 |
nested_traversal | 서브디렉토리 진입으로 lazy-load된 nested CLAUDE.md |
path_glob_match | paths: 패턴 매치로 lazy-load된 path-scoped rule |
include | @import로 끌려온 파일 |
compact | /compact 후 재로드 |
훅에 전달되는 입력 필드에는 file_path, memory_type(User/Project/Local/Managed), load_reason, globs(path_glob_match일 때 패턴), trigger_file_path(어떤 파일을 읽다가 트리거됐는지)가 있다.
규칙이 안 먹는 것 같으면 이 훅으로 로그를 남기고, 어떤 파일을 어떤 이유로 어느 시점에 로드했는지 추적한다. 28~31차시에서 hooks를 다룰 때 이 패턴을 다시 본다.
트러블슈팅
규칙이 적용되지 않는 것 같다
순서대로 점검한다.
/memory로 로드 여부 확인 — 목록에 파일이 보이는가paths:패턴 확인 — 만지는 파일이 패턴에 정말 매치되는가. 패턴은 절대 경로가 아니라 working directory 기준 상대 경로로 매치된다paths:매치 트리거 확인 — Claude가 매칭 파일을 한 번이라도 읽었는가. 패턴이 매치만 된다고 로드되지 않고, 실제 Read가 일어나야 활성화된다InstructionsLoaded훅으로 audit log 남기기 — 위 단계로 안 풀리면 훅으로 정확히 어떤 파일이 언제 로드됐는지 본다- 충돌 규칙 점검 — 다른 rules나 CLAUDE.md에서 정반대 지시를 하고 있지 않은지
무엇이 로드됐는지 모르겠다
/memory가 1차 도구다. 더 정밀하게 보고 싶으면 InstructionsLoaded 훅을 audit log로 쓴다.
CLAUDE.md가 너무 커졌다
200줄 넘기면 — 두 가지 처치다.
- path-scoped rules로 옮긴다 — 특정 영역에만 필요한 지시는
.claude/rules/로 빼고paths:를 단다. 컨텍스트에서 영구적으로 빠진다 @import로 분리한다 — 단순히 가독성 목적이면 import. 다만 import된 파일은 launch 시점에 펼쳐지므로 컨텍스트는 줄지 않는다. 토큰을 줄이려면 1번을 쓴다
모노레포에서 다른 팀의 CLAUDE.md가 들어온다
claudeMdExcludes로 경로를 제외한다. 머신-로컬이라 .claude/settings.local.json에 두는 게 자연스럽다.
path-scoped rule이 자꾸 활성화 안 된다
path-scoped rule의 트리거는 Claude가 매칭 파일을 Read하는 순간이다. Bash로 ls만 해서는 활성화 안 된다. 한 번 매치되면 세션 동안 유지된다. InstructionsLoaded 훅으로 path_glob_match 매처를 걸어두면 정확한 발화 시점을 본다.
정리
핵심 요점
-
CLAUDE.md 200줄 한도: 길어지면 준수율 하락. 토픽별로 쪼개거나 path-scoped로 옮긴다
-
.claude/rules/디렉토리: 토픽별 마크다운, 재귀 발견. 서브디렉토리(frontend/,backend/)도 가능 -
무조건 로드 vs path-scoped:
paths:없으면 매 세션 시작 시 무조건 로드(CLAUDE.md와 동등). 있으면 매칭 파일을 Read할 때 lazy-load -
글롭 패턴:
**/*.ts,src/**/*, 브레이스 확장(*.{ts,tsx}) 지원 -
@import문법: 상대(파일 기준) 또는 절대 경로. 최대 5단계 재귀. 첫 외부 import 시 승인 다이얼로그. import는 컨텍스트를 줄이지 않는다(launch 시점에 펼쳐짐) -
AGENTS.md 호환: CLAUDE.md에서
@AGENTS.md로 import 후 Claude 전용 지침 추가 -
User vs Project rules: User rules(
~/.claude/rules/)가 먼저, project rules(.claude/rules/)가 나중. 사실상 project가 우선 -
팀 공유:
.claude/를 git에 커밋.CLAUDE.local.md와settings.local.json은 .gitignore. 심볼릭 링크로 여러 프로젝트에 공통 규칙 적용 -
모노레포 제외:
claudeMdExcludes로 다른 팀의 CLAUDE.md/rules 경로를 글롭으로 제외 (managed policy는 제외 불가) -
추가 디렉토리에서 로드:
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 claude --add-dir ... -
디버깅:
/memory로 로드된 파일 목록 확인.InstructionsLoaded훅으로 정확한 로드 시점/이유 audit log
확인해볼 링크
- 공식 문서: How Claude remembers your project (Memory)
- 공식 문서: Extend Claude Code (Build your setup over time)
- 공식 문서: Hooks reference (InstructionsLoaded)
- 공식 문서: CLI reference (—add-dir, —setting-sources)
다음 단계
24차시부터는 섹션 5 — Custom Skills로 들어간다. 24차시에서는 Skills의 개념을 다룬다. CLAUDE.md와 rules가 “매 세션에 로드되는 컨텍스트”라면, Skills는 on-demand로 로드되는 워크플로/지식이다. /<name> 슬래시 명령으로 직접 호출하거나 Claude가 작업과 관련 있다고 판단했을 때만 본문이 들어온다. 매 세션 컨텍스트를 늘리지 않으면서 재사용 가능한 작업을 패키징하는 방법을 살펴본다.