Hands-on Lab / Chapter 2

Subagents, 전문 에이전트를 만들고 지휘하기

Claude Code Deep Dive Workshop, Chapter 2 - Agents (Subagents) 실습

이 랩에서는 code-reviewer, test-writer, docs-writer 3종의 서브에이전트를 직접 정의하고, 도구 권한 격리를 체험한 뒤 자동 / 명시 / 병렬 디스패치 3가지 패턴과 Headless CI 통합까지 실습합니다. Chapter 1에서 만든 ~/claude-lab/ch1 프로젝트를 이어서 사용합니다.

소요 시간 40분 내외 Task 준비 + 5개 기준 버전 Claude Code 2.1.x Update 2026.07
TASK 00

사전 준비 확인

2분

Chapter 1 실습 프로젝트가 커밋 2개 이상(초기 + 버그 수정) 상태인지 확인합니다. Task 1의 첫 리뷰 대상이 이 수정 커밋의 diff입니다.

터미널 위치 이 랩은 Chapter 1에서 만든 ~/claude-lab/ch1에서 그대로 이어집니다. 아래에 나오는 .claude/agents/ 같은 상대 경로도 전부 이 폴더 기준입니다. 시작 전에 cd ~/claude-lab/ch1로 위치를 맞추세요.
1프로젝트 상태 확인
Terminal
cd ~/claude-lab/ch1
npm test          # 3개 모두 PASS 여야 합니다
git log --oneline # 커밋 2개 이상이어야 합니다
Chapter 1을 건너뛰었거나 프로젝트가 없는 경우에만 아래 블록을 통째로 실행하면 Chapter 1 완료 상태(버그 수정 커밋 포함)로 프로젝트가 재생성됩니다. 이미 프로젝트가 있다면 실행하지 마세요.
Terminal, 복구용 전체 복사
mkdir -p ~/claude-lab/ch1/src && cd ~/claude-lab/ch1

cat > package.json << 'EOF'
{
  "name": "user-service-lab",
  "version": "1.0.0",
  "description": "Claude Code Workshop sample project",
  "type": "module",
  "scripts": { "test": "node test.js" }
}
EOF

cat > src/users.js << 'EOF'
// 사용자 데이터 저장소
// 주의: 일부 사용자는 profile 필드가 없음 (마이그레이션 미수행 상황)
export const users = [
  { id: 1, name: "Kim", profile: { email: "kim@example.com", plan: "pro" } },
  { id: 2, name: "Lee", profile: { email: "lee@example.com", plan: "free" } },
  { id: 3, name: "Park" }
];
EOF

cat > src/userService.js << 'EOF'
import { users } from "./users.js";

export function getUser(id) {
  return users.find((u) => u.id === id);
}

export function getUserPlan(id) {
  const user = getUser(id);
  return user.profile.plan;
}
EOF

cat > test.js << 'EOF'
import { getUserPlan } from "./src/userService.js";

function check(name, fn, expected) {
  const actual = fn();
  console.log(`${name}: ${actual === expected ? "PASS" : `FAIL (got ${actual})`}`);
}

check("Test 1 - pro 플랜 사용자", () => getUserPlan(1), "pro");
check("Test 2 - free 플랜 사용자", () => getUserPlan(2), "free");
check("Test 3 - profile 없는 사용자", () => getUserPlan(3), "unknown");
EOF

git init -q
git config user.name  >/dev/null 2>&1 || git config user.name "lab"
git config user.email >/dev/null 2>&1 || git config user.email "lab@example.com"
git add -A && git commit -q -m "chore: initial lab project (with bug)"

cat > src/userService.js << 'EOF'
import { users } from "./users.js";

export function getUser(id) {
  return users.find((u) => u.id === id);
}

export function getUserPlan(id) {
  const user = getUser(id);
  return user?.profile?.plan ?? "unknown";
}
EOF

git add -A && git commit -q -m "fix: handle users without profile"
npm test && git log --oneline
2jq 설치 확인

Task 5의 헤드리스 통합에서 JSON 출력 파싱jq를 사용합니다. 없으면 한 줄로 설치하세요.

Terminal, 전체 복사
jq --version || sudo dnf install -y jq
출력 예시
jq-1.7.1
CHECKPOINT
TASK 01

첫 Subagent, code-reviewer 만들기

8분

.claude/agents/에 code-reviewer를 정의하고 등록을 확인한 뒤, Chapter 1의 버그 수정 커밋을 첫 리뷰 대상으로 호출합니다.

Subagent가 왜 필요한가, Context Isolation 서브에이전트는 자신만의 200K 컨텍스트, 시스템 프롬프트, 도구 권한, 모델을 갖는 독립 에이전트입니다. 파일 수십 개를 읽는 무거운 분석을 위임하면 서브에이전트 컨텍스트에서 소비되고, 메인에는 요약만 반환되어 메인 컨텍스트가 보호됩니다.
1에이전트 정의 파일 작성

정의 파일은 YAML frontmatter 핵심 4필드(name / description / tools / model, 필수는 name과 description)와 시스템 프롬프트 본문으로 구성됩니다. 프로젝트 루트에서 아래 블록을 실행하세요.

Terminal, 전체 복사
cd ~/claude-lab/ch1
mkdir -p .claude/agents

cat > .claude/agents/code-reviewer.md << 'EOF'
---
name: code-reviewer
description: |
  PR diff를 검토하여 가독성, 안전성, 성능, 테스트 커버리지 관점에서
  개선 사항을 제안하는 시니어 코드 리뷰어. 사용 시점: 코드 리뷰가
  필요하다고 사용자가 요청하거나 git diff 검토가 필요한 경우.
tools: Read, Grep, Glob, Bash(git diff:*), Bash(git log:*)
model: sonnet
---
당신은 10년 경력의 시니어 소프트웨어 엔지니어입니다.

# 검토 우선순위
1. 안전성: null 참조, race condition, 입력 검증 누락
2. 가독성: 명명, 함수 크기, 주석 필요성
3. 성능: 알고리즘 복잡도, 불필요한 I/O
4. 테스트: 새 코드의 테스트 커버리지

# 출력 형식
- Severity: high | medium | low
- 파일:라인, 문제 설명, 권장 수정안
EOF
필드역할이 예시의 선택
name고유 식별자, kebab-case 영문 소문자code-reviewer
description자동 디스패치의 판단 기준, 사용 시점까지 명시리뷰 요청 / git diff 검토 시
tools도구 화이트리스트, Bash는 명령 패턴으로 제한읽기 전용 + git diff / log만
model별칭 sonnet / opus / haiku, 전체 모델 ID, 또는 inherit. 생략 시 inherit(메인 모델 상속)sonnet, 일상 리뷰의 표준
2등록 확인
Terminal
claude
변경 안내, /agents 위저드 제거 (v2.1.198+) 구버전에서 쓰던 /agents는 이제 안내 문구만 출력합니다. 에이전트 관리 자체가 파일 기반으로 바뀐 것은 아니며(파일 위치와 frontmatter는 동일), 확인은 @ 멘션 typeahead 또는 Claude에게 질문으로, 생성/수정은 파일 편집 또는 Claude에게 요청으로 합니다.

입력창에 @를 입력하면 파일 멘션과 함께 code-reviewer (agent)가 typeahead 목록에 나타납니다(Esc로 닫기). 또는 아래처럼 직접 물어봐도 됩니다.

Claude 세션 입력
현재 이 세션에서 사용할 수 있는 서브에이전트 목록을 알려주세요
출력 예시
사용 가능한 서브에이전트:
  - code-reviewer : PR diff 검토 (프로젝트, .claude/agents/)
  - Explore, Plan, general-purpose : 내장 에이전트
3첫 호출, 버그 수정 커밋 리뷰
Claude 세션 입력
code-reviewer 에이전트를 사용해서 최근 커밋의 변경분(git diff HEAD~1)을 검토해 주세요
관찰 포인트
[메인] code-reviewer 에이전트를 호출합니다.
  → Agent(code-reviewer, "Review git diff HEAD~1")
[reviewer] Bash  git diff HEAD~1
[reviewer] Read  src/userService.js
[메인] 결과 요약:
  - low  src/userService.js:9, 옵셔널 체이닝 적용 적절
  - medium test.js, 존재하지 않는 id 케이스 테스트 부재 ...

결과를 받으면 출력 형식이 시스템 프롬프트에서 지정한 Severity 구조를 따르는지, 우선순위 4개 관점이 반영되었는지 확인하세요.

CHECKPOINT
TASK 02

도구 권한 격리 체험

6분

읽기 전용 에이전트에 일부러 권한 밖 요청을 보내 거부 동작을 관찰하고, tools 필드에 권한을 명시적으로 추가한 뒤 새 위임으로 차이를 체험합니다. 최소 권한 원칙과 정의 갱신 타이밍의 실습입니다.

Tools란 무엇인가 서브에이전트 정의의 tools 필드는 그 에이전트가 쓸 수 있는 손발의 목록입니다. Read(파일 읽기), Grep/Glob(검색), Edit/Write(수정과 생성), Bash(명령 실행)처럼 하나하나가 구체적 능력이고, 목록에 없는 도구는 시도 자체가 차단됩니다. 이 Task는 그 울타리를 일부러 밟아 보며 최소 권한이 실제로 동작하는지 확인하는 실험입니다.
1읽기 전용 테스트 에이전트 작성
Terminal, 전체 복사
cat > .claude/agents/dangerous-test.md << 'EOF'
---
name: dangerous-test
description: 의도적 권한 테스트용 에이전트. 사용 시점: 사용자가 이름으로 직접 지정할 때만.
tools: Read, Grep
model: haiku
---
당신은 테스트 에이전트입니다.
파일을 분석하고 결과를 보고하세요.
EOF
2일부러 권한을 벗어난 요청

tools에 Edit이 없는 상태에서 파일 수정을 요청합니다. Claude Code가 .claude/agents/를 감시하므로 파일 저장 후 몇 초 안에 자동 인식됩니다(Task 1에서 디렉토리를 만든 뒤 세션을 시작했기 때문에 재시작 불필요).

Claude 세션 입력
dangerous-test 에이전트를 사용해서 src/users.js의 Park 사용자에게 profile 필드를 추가해 주세요
관찰 포인트
[dangerous-test] Read src/users.js
[dangerous-test] 파일 수정이 필요하지만 Edit 도구 권한이 없습니다.
  분석 결과만 보고합니다: Park(id 3)에 profile 필드가 없습니다.
  → 수정은 Edit 권한이 있는 에이전트 또는 메인에서 수행해야 합니다.
tools 필드 생략의 위험 tools 필드를 아예 생략하면 메인의 모든 도구를 상속합니다(지양). 화이트리스트를 명시하는 습관이 안전의 기본입니다. 반대로 대부분을 상속하되 일부만 빼려면 disallowedTools: Write, Edit처럼 거부 목록을 쓸 수 있습니다.
3권한을 명시적으로 부여
Terminal, tools에 Edit 추가
sed -i.bak 's/^tools: Read, Grep$/tools: Read, Grep, Edit/' .claude/agents/dangerous-test.md
grep '^tools:' .claude/agents/dangerous-test.md   # tools: Read, Grep, Edit 확인
4/exit로 나갔다 들어와 새 위임으로 재시도
중요, 같은 요청을 그냥 반복하면 여전히 거부됩니다 정의 파일 변경은 다음 새 위임부터 적용됩니다. 그런데 메인은 직전 턴에서 "이 에이전트는 수정 불가"를 이미 학습했기 때문에, 같은 요청을 반복하면 재위임 없이 기존 맥락으로 답하거나 이전 인스턴스를 재개(resume)할 수 있습니다. 재개된 서브에이전트는 스폰 시점의 도구 권한을 그대로 유지합니다. /exit로 세션을 완전히 나갔다가 claude로 다시 들어오세요. 갱신된 정의로 새 인스턴스가 뜹니다(같은 세션에서 /clear로 맥락만 비워도 동일 효과).
Claude 세션 입력, 세션 종료
/exit
Terminal (~/claude-lab/ch1), 재진입
claude
Claude 세션 입력, 재시도
dangerous-test 에이전트를 사용해서 src/users.js의 Park 사용자에게 profile 필드를 추가해 주세요
관찰 포인트
[dangerous-test] Read src/users.js
[dangerous-test] Edit src/users.js   # 이번에는 Edit 도구 사용 (승인 요청 표시)
  Park(id 3)에 profile 필드를 추가했습니다.

이것이 의도적 권한 부여와 무분별한 전체 권한의 차이입니다. 동시에, 서브에이전트의 도구 권한이 인스턴스 스폰 시점에 고정된다는 점과 메인의 대화 맥락이 재위임을 가로막을 수 있다는 점, 그리고 /exit 재진입(또는 /clear)이 이런 상황의 실전 해결책이라는 점까지 함께 체득한 것입니다.

5실습 프로젝트 원상 복구
Terminal, 데이터 원복
git checkout -- src/users.js
rm -f .claude/agents/dangerous-test.md .claude/agents/dangerous-test.md.bak
CHECKPOINT
TASK 03

에이전트 3종 완성, test-writer와 docs-writer 추가

7분

Task 4의 디스패치 실습을 위해 test-writer(테스트 작성)와 docs-writer(문서 작성)를 추가합니다. 두 에이전트의 description이 서로 영역이 겹치지 않게 작성된 점에 주목하세요.

1두 에이전트 한 번에 생성
Terminal, 전체 복사
cat > .claude/agents/test-writer.md << 'EOF'
---
name: test-writer
description: |
  기존 코드의 테스트 커버리지를 분석하고 누락된 엣지 케이스, 분기,
  예외 경로에 대한 테스트를 작성. 사용 시점: 커버리지 부족이 확인되거나
  새 함수에 테스트가 필요한 경우.
tools: Read, Grep, Glob, Edit, Write, Bash(npm test:*)
model: sonnet
---
당신은 테스트 자동화 전문가입니다.

# 작업 절차
1. 대상 함수의 시그니처와 분기 구조 분석
2. 기존 테스트 파일이 있다면 스타일 일치
3. 정상 케이스, 엣지 케이스, 예외 케이스 모두 커버
4. 테스트 작성 후 실제로 실행하여 통과 확인

# 작성 원칙
- 테스트 하나가 한 가지만 검증
- 의미 있는 테스트 이름
- AAA 패턴 (Arrange Act Assert)
EOF

cat > .claude/agents/docs-writer.md << 'EOF'
---
name: docs-writer
description: |
  README, API 문서, 아키텍처 결정 문서(ADR)를 코드와 일치하도록 작성하거나
  업데이트. 사용 시점: 신규 기능 추가 후 문서 작성이 필요하거나 기존 문서가
  코드와 불일치할 때.
tools: Read, Grep, Glob, Edit, Write, Bash(git log:*)
model: sonnet
---
당신은 기술 문서 작성가입니다.

# 문서 종류별 가이드
- README: 프로젝트 개요, 설치, 사용법, 예시
- API 문서: 함수 시그니처, 파라미터, 반환값, 예시
- ADR: Context, Decision, Consequences 형식

# 작성 원칙
- 코드와 실제 동작이 일치해야 함
- 예시는 실제로 실행 가능한 것
- 한국어와 영어 혼용 가능, 일관성 유지
EOF

ls .claude/agents/
23종 등록 확인

세션 입력창에 @를 입력해 typeahead에 3종 모두 (agent)로 표시되는지 확인하세요. 직접 @agent-test-writer처럼 입력해도 멘션이 인식됩니다. 또는 아래처럼 물어봐도 됩니다.

Claude 세션 입력
사용 가능한 서브에이전트를 모두 알려주세요
출력 예시
프로젝트 서브에이전트 (.claude/agents/):
  - code-reviewer : PR diff 검토
  - test-writer   : 테스트 커버리지 확장
  - docs-writer   : 문서 작성과 동기화
description이 디스패치 정확도를 결정한다 메인은 사용자 요청과 각 에이전트의 description을 대조해 자동 선택합니다. "코드를 분석하는 도우미"처럼 모호하거나 "모든 개발 작업을 돕는 만능"처럼 광범위하면 잘못된 디스패치가 발생합니다. 구체적 역할 + 사용 시점을 명시하고, 에이전트끼리 경계를 겹치지 않게 하세요.
이 정의 파일들은 Git으로 팀과 공유됩니다 .claude/agents/는 프로젝트 단위(팀 공유, Git 커밋 대상), ~/.claude/agents/는 개인 전역(모든 프로젝트에서 사용)입니다. 지금 만든 3종을 커밋해 두면 팀 전체가 동일한 에이전트를 사용합니다.
CHECKPOINT
TASK 04

Task 디스패치 3패턴

10분

같은 에이전트들을 자동 디스패치, 명시적 호출, 병렬 호출 3가지 방식으로 부려 보고, /cost로 에이전트별 비용을 비교합니다. 각 패턴 사이에 /exit로 나갔다 들어오거나 /clear로 컨텍스트를 정리하면 관찰이 깨끗해집니다.

현재 버전 참고, Agent 도구와 백그라운드 실행 위임에 쓰이는 내부 도구 이름이 Task에서 Agent로 변경되었습니다(구 Task 표기는 별칭으로 동작). 또한 서브에이전트는 기본적으로 백그라운드로 실행됩니다. 진행 상황은 프롬프트 아래 패널과 /tasks에서 확인하고, 권한 요청은 메인 세션에 표시되며, Ctrl + B로 실행 중 작업을 백그라운드로 보낼 수 있습니다.
1패턴 A, 자동 디스패치

에이전트를 지정하지 않고 요청합니다. 메인이 description을 대조해 스스로 code-reviewer를 선택하는지 관찰하세요.

Claude 세션 입력
이 프로젝트의 최근 변경 사항을 코드 리뷰해 주세요
관찰 포인트
[메인] 코드 리뷰 요청으로 판단, code-reviewer를 호출합니다.
  → Agent(code-reviewer, "Review recent changes")
# 사용자가 지정하지 않아도 description 매칭으로 선택됨
2패턴 B, 명시적 호출

이름으로 직접 지정하면 메인은 다른 에이전트를 검토하지 않고 예측 가능하게 해당 에이전트를 호출합니다.

Claude 세션 입력
test-writer 에이전트를 사용해서 src/userService.js의 테스트 커버리지를 분석하고, 존재하지 않는 사용자 id 같은 누락된 엣지 케이스 테스트를 추가해 주세요

test-writer가 테스트를 추가하고 npm test를 직접 실행해 통과를 확인하는지 지켜보세요(시스템 프롬프트의 작업 절차 4번). 특정 에이전트 실행을 보장하고 싶다면 자연어 대신 @를 입력해 typeahead에서 test-writer (agent)를 선택하는 @-멘션 방식을 쓸 수 있습니다.

3패턴 C, 병렬 호출

독립적인 3개 작업을 한 번에 요청합니다. 메인이 3개의 Task를 동시에 발행하고, 총 시간은 합산이 아니라 가장 오래 걸린 작업 기준(max)이 됩니다.

Claude 세션 입력
이 코드베이스를 종합 점검해 주세요. 다음 세 가지를 병렬로 진행해 주세요.
1) 코드 품질 리뷰
2) 테스트 커버리지 분석
3) README 문서 초안 작성
관찰 포인트
[메인] 3개의 Subagent를 병렬로 호출합니다.
  → Agent(code-reviewer, ...)
  → Agent(test-writer, ...)
  → Agent(docs-writer, ...)
[reviewer] 분석 중... 이슈 2건
[tester  ] 분석 중... 엣지 케이스 1건 보강
[docs    ] README.md 초안 작성 중...
[메인] 결과 종합: 코드 이슈 2건, 테스트 1건 추가, README 초안 완료.
4비용 확인

현재 버전에서 /cost/usage의 별칭입니다. 세션 사용량과 어떤 서브에이전트가 한도를 소비했는지 확인할 수 있습니다.

Claude 세션 입력
/cost
출력 예시 (개념, 실제 표시 형식은 버전에 따라 다름)
Current session cost: $0.42
Breakdown:
  Main agent    (Sonnet): $0.09
  code-reviewer (Sonnet): $0.11
  test-writer   (Sonnet): $0.13
  docs-writer   (Sonnet): $0.09
병렬화의 트레이드오프 병렬 호출은 시간은 max로 단축되지만 비용은 합산됩니다. 강의 예시 기준 단일 $0.23 대비 3개 병렬 약 7배까지 증가할 수 있습니다. 반복적인 단순 작업은 model: haiku로 지정해 비용을 조절하고, Opus는 심층 분석에만 사용하세요.
CHECKPOINT
TASK 05

Headless 통합과 CI 맛보기

7분

서브에이전트를 대화 없이 스크립트에서 호출하고 JSON으로 후처리합니다. 마지막으로 같은 패턴이 GitHub Actions에서 어떻게 동작하는지 확인합니다. 세션 밖 터미널에서 실행하세요.

1헤드리스로 에이전트 호출
Terminal
git diff HEAD~1 | claude -p \
  "code-reviewer 에이전트로 이 diff를 검토해 주세요" \
  --output-format json > review-result.json

head -c 400 review-result.json
2JSON 후처리
Terminal
# jq가 있다면: 요약과 high severity만 추출
jq '.summary' review-result.json
jq '.issues | map(select(.severity == "high"))' review-result.json 2>/dev/null

# jq가 없다면
python3 -m json.tool review-result.json | head -30
3CI 통합 패턴 확인 (참고)

같은 명령을 GitHub Actions에 넣으면 PR마다 자동 리뷰가 됩니다. 아래는 강의에서 다룬 직렬(리뷰) + 병렬(테스트와 보안) 복합 패턴의 워크플로입니다. GitHub 저장소가 있다면 push해서 실험해 보세요. 선택 과제입니다.

.github/workflows/pr-check.yml, 참고용
name: PR Check with Subagents
on: pull_request

jobs:
  comprehensive-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - run: npm install -g @anthropic-ai/claude-code

      # 직렬: code-reviewer 먼저 실행 후 결과 보고
      - name: Code Review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          claude -p "code-reviewer 에이전트로 git diff origin/main..HEAD를 검토해 주세요" \
            --output-format text > review.md

      # 병렬 요청: 테스트와 보안을 한 번에
      - name: Tests + Security
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          claude -p "다음을 모두 점검해 주세요: 1) 테스트 커버리지 부족 2) 보안 이슈" \
            --output-format json > result.json

      # 결과를 PR 코멘트로 게시
      - uses: peter-evans/create-pr-comment@v1
        with: { body-path: review.md }
CI에서의 안전장치 무인 실행에서는 API Key를 반드시 Secrets로 주입하고, 읽기 전용 검사라면 --allowed-tools "Read,Grep,Glob"으로 도구를 제한하세요. Chapter 5(CLI Reference)에서 권한 플래그와 파이프라인 패턴을 심화합니다.
CHECKPOINT

마무리, 학습 목표 체크리스트

Chapter 2의 학습 목표를 스스로 점검하세요. 미달성 항목은 강의 자료의 해당 Part를 다시 확인하거나 Slack 채널에 질문을 남기세요.

목표확인 질문관련
ConceptContext Isolation과 위임의 가치를 설명할 수 있는가강의 Part 1
DefineYAML frontmatter 4필드로 에이전트를 정의했는가Task 1
Least Privilegetools 화이트리스트로 권한 거부와 부여를 체험했는가Task 2
Description자동 디스패치가 description 기반임을 확인했는가Task 3, 4
Dispatch자동 / 명시 / 병렬 3패턴을 모두 실행했는가Task 4
Cost/cost로 에이전트별 비용과 병렬 트레이드오프를 확인했는가Task 4
Automation헤드리스 + JSON으로 CI 통합 패턴을 실행했는가Task 5
NEXT CHAPTER

Chapter 3 - Admin Setup

Enterprise 환경 배포의 모든 것. 사내 배포 전략(npm 미러, 대량 배포), 자격증명 관리(Bedrock, SSO, IAM), 네트워크와 보안(프록시, 화이트리스트), 거버넌스(조직 정책, 사용량 모니터링)를 다룹니다.