
GitHub 저장소: claude-orchestration-template
bash <(curl -s https://raw.githubusercontent.com/jaejeonglee/claude-orchestration-template/main/scripts/init.sh)프로젝트 루트에서 한 줄 실행하면 Claude Code 오케스트레이션 환경이 세팅된다.
목적 및 동기
Claude Code를 쓰다 보면 금방 느끼는 문제가 몇 가지 있다.
- 컨텍스트가 압축(compact)되면 직전에 뭘 하고 있었는지를 잊는다
- 확인 안 된 기획을 바로 코드로 옮겨놓고 나중에 엎는다
- 공식 문서 확인, 로직 검증, 코드 작성을 한 세션이 다 하니까 결과가 들쭉날쭉
- 문서와 코드가 따로 놀아서 프로젝트의 현재 상태를 빠르게 파악하기 어렵다
결국 “Claude한테 일을 잘 시키는 규칙”이 프로젝트마다 필요한데, 이걸 매번 수작업으로 세팅하는 건 비효율적이다. 그래서 설치 한 줄로 끝나는 템플릿을 만들었다.
핵심 설계: 3-Agent 오케스트레이션
한 에이전트가 기획, 검증, 구현을 다 하면 결과가 일정하지 않다. 사람 팀도 기획자, 리뷰어, 개발자가 나뉘어 있는 데는 이유가 있다. 같은 원칙을 Claude Code에 적용했다.
| 에이전트 | 역할 |
|---|---|
| Claude | 오케스트레이터 — 코드 작성, 전체 흐름 조율, 결과 통합 |
| codex-reasoner | 심층 추론 — 로직 검증, 보안 분석, 버그 근본원인 분석 |
| gemini-researcher | 리서치 — 공식 문서 확인, 최신 API 스펙, 기획 초안 작성 |
왜 Gemini?
공식 문서 확인이나 최신 API 조회처럼 외부 정보가 필요한 작업은 Gemini의 긴 컨텍스트와 웹 검색이 유리하다. .claude/scripts/call-gemini.sh로 Gemini API를 호출하는 서브에이전트를 만들어서 Claude가 필요할 때 위임하도록 했다.
왜 Codex?
복잡한 비즈니스 로직, 보안 취약점 분석, race condition 같은 단계별 깊은 추론이 필요한 작업은 별도 세션으로 분리해야 결과가 깔끔하다. 메인 Claude는 오케스트레이터로서 “검증이 필요한 부분”을 codex-reasoner에 넘기고 받아온 분석 결과로 구현 방향을 정한다.
기능 개발 워크플로우
gemini-researcher → 기획 초안 작성 (.claude/docs/specs/*.draft.md)
codex-reasoner → 현재 코드 기준 리뷰
사람 → 확정
Claude → 구현 → 문서 동기화 → 초안 삭제
핵심 원칙은 “확정되지 않은 초안은 바로 코드로 옮기지 않는다”. 기획 단계와 구현 단계 사이에 사람의 확정 게이트를 강제로 두는 이유는, Claude가 “그럴듯한 초안”을 그대로 구현해버리는 사고를 막기 위해서다.
구조
.claude/
├── agents/ # 서브 에이전트 정의
│ ├── codex-reasoner.md
│ └── gemini-researcher.md
├── docs/
│ ├── architecture.md # 시스템 구조
│ ├── conventions.md # 코딩 규칙
│ └── specs/ # 기획 초안 (임시)
├── scripts/
│ └── call-gemini.sh # Gemini API 호출
├── settings.json # Hooks + 권한
├── skills/
│ ├── new-spec/ # /new-spec 커맨드
│ └── update-task/ # /update-task 커맨드
├── CURRENT_TASK.md # 세션 핸드오프 노트
└── PROGRESS.md # 전체 진행 기록
CLAUDE.md # 세션 시작 시 자동 로드
.claude/와 CLAUDE.md는 설치 시 .gitignore에 자동 추가되니 프로젝트 설정은 로컬에서만 유지된다.
Hooks로 강제되는 자동화
Claude에게 “문서도 업데이트해줘”라고 말로 시키면 까먹는다. 훅으로 강제해야 한다.
| 시점 | 동작 |
|---|---|
| 세션 시작 | CURRENT_TASK.md + 최근 커밋 자동 출력 |
| 파일 수정 (Edit/Write) | JS/TS 파일 ESLint auto-fix |
| 작업 완료 (Stop) | 문서 동기화 누락 검증, 에러 핸들링 체크 |
SessionStart 훅이 컨텍스트 리셋을 해결한다
Claude Code는 컨텍스트가 길어지면 자동 압축을 한다. 압축 후에는 이전 작업 내용을 대부분 잃는다.
이걸 해결하려면 “지금 뭐 하고 있었는지”가 어디엔가 저장되어 있어야 하고, 새 세션이 시작될 때 자동으로 주입되어야 한다.
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '=== 현재 작업 ===' && cat .claude/CURRENT_TASK.md 2>/dev/null && echo '---' && git log --oneline -5 2>/dev/null"
}
]
}
]
이 훅 덕분에 claude 실행만 해도 이전 작업 상태와 최근 커밋 내역을 자동으로 컨텍스트에 로드한다.
Stop 훅으로 문서 동기화 강제
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "이번 대화에서 Edit/Write 도구로 파일을 수정한 적이 있는지 확인하세요. ... 문서 동기화 누락, CURRENT_TASK.md 업데이트 누락 중 해당하는 것이 있으면 {\"ok\": false, ...}"
}
]
}
]
작업이 끝날 때마다 Claude 본인이 스스로를 검증한다. 누락이 있으면 {"ok": false}를 반환하고 보완 작업이 이어진다.
보안
민감 파일 접근은 텍스트 지시가 아닌 도구 레벨에서 차단해야 한다. settings.json의 permissions.deny로 처리했다.
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/*.pem)",
"Read(./**/*.key)",
"Read(./**/credentials*)",
"Read(./**/*secret*)"
]
}
Claude가 실수로도 .env를 읽으려 하면 도구 호출 자체가 실패한다.
실제 사용
최초 실행
claude
> 이 프로젝트 파악해줘
Claude가 코드를 읽고 architecture.md와 conventions.md를 자동으로 채운다. 기존 프로젝트에 도입할 때도 동일하게 동작한다.
일상 개발
평소처럼 자연어로 요청하면 된다. Claude가 작업 성격에 맞는 에이전트에 자동 위임한다.
| 요청 | 처리 |
|---|---|
결제 API 추가해줘 | Claude가 직접 구현 |
이 인증 로직 보안 문제 있어? | codex-reasoner가 분석 |
Fastify v5 마이그레이션 가이드 확인해줘 | gemini-researcher가 확인 |
슬래시 커맨드
/new-spec payment → 기획 초안 템플릿 생성
/update-task → CURRENT_TASK.md 갱신
이슈
.ai/와 .claude/ 이중 구조 문제
초기 설계에서는 AI용 문서를 .ai/에, Claude Code 설정을 .claude/에 분리했다. 문서가 10개나 됐다.
.ai/README.md(문서 맵).ai/agents.md(에이전트 역할).ai/architecture.md.ai/conventions.md.ai/context-reset.md.ai/docs/PROJECT.md.ai/docs/TODO.md.ai/docs/API_DOCS.mdCLAUDE.mdCURRENT_TASK.md
세션 시작할 때마다 5개 파일을 순서대로 읽으라고 하니까 컨텍스트 낭비가 심했다. 그리고 대부분 역할이 겹쳤다. README.md는 “이 문서들 읽는 순서”만 적혀 있었고, 이건 CLAUDE.md에 이미 있는 내용이었다.
결국 .ai/를 없애고 .claude/docs/로 통합했다. 문서도 architecture.md, conventions.md 두 개만 남기고 나머지는 삭제하거나 CLAUDE.md에 흡수했다.
claude 실행해도 아무 일도 안 일어난다
초기 설계에서는 “claude 실행하면 자동으로 프로젝트 분석해줌”이라고 생각했다. 실제로는 아무 일도 안 일어났다. CLAUDE.md를 자동 로드하긴 하지만, 그 안의 지시를 Claude가 자발적으로 실행하지는 않는다.
SessionStart 훅으로 CURRENT_TASK.md 내용을 자동 출력하게 만들어서 해결했다. 출력된 내용이 컨텍스트에 들어가면 Claude가 그걸 기반으로 다음 행동을 결정한다.
settings.json 덮어쓰기 정책
init.sh를 여러 번 실행해도 문제없어야 한다. 처음에는 “기존 훅을 보호”하려고 settings.json을 건너뛰게 했는데, 이러면 템플릿을 업데이트해도 기존 프로젝트에 반영이 안 된다.
고민 끝에 항상 최신 템플릿으로 덮어쓰기로 바꿨다. 사용자가 개별 프로젝트에서 훅을 커스텀할 일은 거의 없고, 있다면 별도 파일로 분리하는 게 낫다.
Stop 훅이 무한 루프에 빠진다
초기 Stop 훅은 “파일 수정이 있었는지”를 파악하지 않고 무조건 “문서 동기화 했냐”를 물어봤다. 파일 수정이 없는 단순 대화에서도 “CURRENT_TASK.md가 업데이트되지 않았습니다”라고 계속 경고를 띄웠다.
프롬프트를 개선해서 “이번 대화에서 Edit/Write 도구로 파일을 수정한 적이 있는지 먼저 확인”하도록 했다. 수정이 없으면 즉시 {"ok": true} 반환한다.
앞으로
- 서브에이전트 추가 (예:
db-migration-reviewer,test-writer) - 프로젝트 타입별 프리셋 (Next.js, Fastify, FastAPI 등)
/new-spec템플릿 커스터마이징