Claude Code를 제대로 써보자 (feat. 3-Agent 오케스트레이션)
JAY.LOG
dev/April 10, 2026

Claude Code를 제대로 써보자 (feat. 3-Agent 오케스트레이션)

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.jsonpermissions.deny로 처리했다.

"permissions": {
  "deny": [
    "Read(./.env)",
    "Read(./.env.*)",
    "Read(./**/*.pem)",
    "Read(./**/*.key)",
    "Read(./**/credentials*)",
    "Read(./**/*secret*)"
  ]
}

Claude가 실수로도 .env를 읽으려 하면 도구 호출 자체가 실패한다.

실제 사용

최초 실행

claude
> 이 프로젝트 파악해줘

Claude가 코드를 읽고 architecture.mdconventions.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.md
  • CLAUDE.md
  • CURRENT_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 템플릿 커스터마이징

참고