2026-04-05·[회고 및 생각]
Claude Code 서브에이전트, 설정만 해놓고 안 쓰고 있었다
에이전트 설정을 다 켜놨는데, 한 달 치 데이터를 까보니까 실제로 쓰고 있는 건 executor뿐이었다.
나는 Claude Code를 꽤 잘 쓰고 있다고 생각했다. 설정도 다 켜놨고.
.omc-config.json에 이렇게 적어뒀다:
{
"defaultExecutionMode": "ultrawork",
"autoCodeReview": true,
"autoSecurityReview": true,
"useGitMaster": true
}
자동 코드 리뷰, 보안 검토, Git 마스터 — 전부 true. 완벽해 보이는 설정이었다. 그리고 한 달 치 데이터를 뜯어보기 전까지는 그렇게 믿고 있었다.
데이터를 까보니
86세션, 304회 에이전트 호출. 카테고리별로 정리해보니:
| 에이전트 | 호출 수 | 상태 |
|---|---|---|
| executor | 90 | 잘 쓰는 중 |
| explore | 54 | 잘 쓰는 중 |
| explore-medium | 51 | 잘 쓰는 중 |
| code-reviewer | 4 | 거의 안 씀 |
| security-reviewer | 0 | 아예 안 씀 |
| git-master | 0 | 아예 안 씀 |
구현 45% + 탐색 45% = 90%. 품질 검증은 2%.
솔직히 좀 충격이었다. autoCodeReview: true라고 적어놨는데 실제로 code-reviewer를 부른 건 한 달에 4번. security-reviewer는 0번. true로 설정해놓으면 알아서 돌아가는 줄 알았는데, 그게 아니었다. 내가 직접 호출하지 않으면 아무 일도 일어나지 않는 것이었다.
설정은 의도의 선언이었다. "나는 코드 리뷰를 중요하게 생각해"라는 선언. 코드 리뷰가 실제로 돌아가는 게 아니라.
(그리고 한 달이나 몰랐다는 게 더 문제였다.)
그래서 자동으로 돌게 만들었다
방향을 바꿨다. 설정에 의존하는 대신, 조건이 충족되면 자동으로 실행되는 구조를 만들기로 했다.
Claude Code의 PostToolUse hook은 도구 실행 후 셸 스크립트를 돌릴 수 있다. 여기서 핵심을 좀 삽질하면서 배웠는데 — 알림이 아니라 지시(directive)를 출력해야 한다는 것이다. 처음에 hook 출력을 "Consider running code-reviewer"로 썼는데 Claude가 거의 무시했다. "MUST"로 바꾸니까 바로 실행하더라. Claude는 hook 출력을 시스템 컨텍스트로 받기 때문에, 지시어의 강도가 실제 행동 확률에 직접 영향을 준다는 걸 그때 알았다.
흐름은 이렇다:
파일 수정 → hook이 수정 횟수 추적 → 임계치 도달 → "code-reviewer 돌려" 지시 출력 → Claude가 실행
코드 리뷰 자동 에스컬레이션
파일 수정 횟수를 추적해서, 단계별로 리뷰 강도를 높인다.
#!/bin/bash
# ~/.claude/hooks/code-quality-tracker.sh
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
if [[ "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "Write" ]]; then exit 0; fi
# 소스 코드 파일만 추적
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ ! "$FILE_PATH" =~ \.(ts|tsx|js|jsx|py|go|rs)$ ]]; then exit 0; fi
TRACKER="$HOME/.claude/.edit-tracker"
COUNT=0; [ -f "$TRACKER" ] && COUNT=$(cat "$TRACKER" 2>/dev/null)
COUNT=$((COUNT + 1)); echo "$COUNT" > "$TRACKER"
if [ "$COUNT" -eq 10 ]; then
echo "INFO: 10 code edits. A code-review (sonnet) is recommended before committing."
elif [ "$COUNT" -eq 15 ]; then
echo "IMPORTANT: 15+ edits. You MUST delegate to code-reviewer (sonnet)."
elif [ "$COUNT" -eq 30 ]; then
echo "DEEP REVIEW REQUIRED: 30+ edits. You MUST run a THOROUGH code review with opus."
fi
10회면 추천, 15회면 필수, 30회면 opus 딥 리뷰. "MUST"가 핵심이다.
보안 파일 자동 감지
파일 경로에 보안 관련 패턴이 포함되면 자동으로 감지한다.
#!/bin/bash
# ~/.claude/hooks/security-alert.sh
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
[[ "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "Write" ]] && exit 0
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE_PATH" =~ (auth|login|token|credential|password|secret|cookie|session|oauth|payment|billing|jwt|cors|csrf|permission|role|admin) ]]; then
SEC_TRACKER="$HOME/.claude/.security-edit-tracker"
SEC_COUNT=0
[ -f "$SEC_TRACKER" ] && SEC_COUNT=$(cat "$SEC_TRACKER" 2>/dev/null)
SEC_COUNT=$((SEC_COUNT + 1))
echo "$SEC_COUNT" > "$SEC_TRACKER"
if [ "$SEC_COUNT" -eq 1 ]; then
echo "SECURITY: Modified security-sensitive file ($FILE_PATH). Run security-reviewer before committing."
elif [ "$SEC_COUNT" -ge 3 ]; then
echo "SECURITY MANDATORY: ${SEC_COUNT} security-sensitive files modified. You MUST delegate to security-reviewer."
fi
fi
처음엔 auth|token|password만 있었는데, 결제 관련 파일을 놓칠 뻔한 적이 있어서 payment, billing, jwt, cors, admin 패턴을 나중에 추가했다.
빌드 에러 자동 디버거 연결
#!/bin/bash
# ~/.claude/hooks/build-error-handler.sh
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
[[ "$TOOL_NAME" != "Bash" ]] && exit 0
TOOL_OUTPUT=$(echo "$INPUT" | jq -r '.tool_output // empty')
if echo "$TOOL_OUTPUT" | grep -qiE '(error TS|SyntaxError|TypeError|Cannot find module|Build failed|compilation failed|tsc.*error)'; then
echo "BUILD ERROR DETECTED: Delegate to debugger agent for root-cause analysis instead of manual fixing."
fi
이전에는 빌드 에러가 나면 executor가 직접 고치려고 삽질했다. 이제는 debugger가 먼저 근본 원인을 분석한 뒤 수정에 들어간다. 속이 시원했다.
Hook 등록
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/code-quality-tracker.sh" },
{ "type": "command", "command": "~/.claude/hooks/security-alert.sh" }
]
},
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/build-error-handler.sh" }
]
}
]
}
}
hook을 붙이고 나서 처음으로 code-reviewer가 자동으로 돌아갈 때, 뭔가 제대로 돌아가고 있다는 느낌이 들었다. (설정에 true 적어놨을 때는 없던 감각이다.)
스킬을 팀으로 퍼뜨리기
hook으로 자동화를 잡고 나서, 다음 문제가 보였다. 나는 이제 자동으로 code-reviewer가 돌아가지만, 팀원들은 그렇지 않다는 것.
스킬은 ~/.claude/skills/skill-name/SKILL.md 파일 하나가 /skill-name 커맨드로 실행되는 구조다.
---
name: team-commit
description: "Use when user explicitly asks to commit changes."
disable-model-invocation: true
---
# 팀 커밋 메시지 생성 및 커밋
1. `git diff --cached` 확인
2. 변경 내용 분석
3. 팀 컨벤션 메시지 생성
4. **사용자 확인 필수**
5. `git commit -m "..."`
처음엔 개인 반복 작업을 하나씩 자동화했다 — Jira 업무 조회, 디자인 시스템 레퍼런스 자동 로드, TDD 워크플로우 등 5개. 전환점은 15개 디자인 QA 티켓을 일괄 처리할 때였다. 개별 스킬을 따로 쓰는 게 아니라 "Jira 조회 → 코드 수정 → 커밋 → 상태 전환"이라는 사이클 자체를 스킬로 정의하니까 훨씬 강력해졌다.
팀으로 퍼뜨릴 스킬을 고를 때 기준은 두 가지였다. 팀원 3명 이상이 반복하는 작업인가, 팀 컨벤션을 강화하는가. 이 기준으로 6개를 추렸다: team-commit, team-create-mr, team-code-review, team-design-system, team-msw-handler, team-figma-diff.
팀 스킬은 프로젝트 저장소에 포함시켜서 git pull하면 자동으로 받도록 했다. .gitignore 수정이 핵심이다:
# 기존: .claude 전체 무시
.claude/*
# 변경: team- prefix 팀 스킬만 추적
!.claude/skills/team-*/
!.claude/skills/team-*/SKILL.md
team- prefix = 팀 스킬(git 추적), 나머지 = 개인 스킬(로컬 only). 단순하지만 명확한 구분이다. git log --follow .claude/skills/team-commit/SKILL.md 하나면 스킬의 변경 이력을 전부 볼 수 있다.
처음 만든 스킬들은 개인용 수준이었다. 특정 상황만 커버하고, 실패 케이스가 없었다. Opus 모델로 각 스킬 스펙을 검토받으니 반복적으로 같은 문제가 나왔다 — 사용자 확인 단계 누락, 트리거 문구가 실제로 사람들이 쓰는 말과 다름, 실패 케이스 처리 없음.
team-commit이 대표적이다:
Before:
# Commit 스킬
Git diff 확인하고 메시지 생성해서 커밋한다.
After (Critic 리뷰 후):
---
name: team-commit
description: "Use when user explicitly asks to commit. Generates message following .cursorrules convention."
disable-model-invocation: true
---
## 동작 순서
1. `git diff --cached` 확인
2. 변경 내용 분석
3. 팀 컨벤션(.cursorrules 기준) 메시지 생성
4. **사용자 확인**: "아래 메시지로 커밋할까요?" → 수동 승인 필수
5. `git commit -m "..."`
## 실패 케이스
- Staged changes 없음 → "스테이징할 파일이 없습니다"
- 커밋 실패 → 에러 메시지와 함께 중단 (--amend 시도 금지)
## 금지 사항
- `--force` 플래그 사용 금지
- `--no-verify`로 훅 스킵 금지
disable-model-invocation: true가 중요하다. 이 플래그가 없으면 Claude가 상황을 보고 "지금 커밋할 타이밍인 것 같은데?" 하고 자동으로 실행할 수 있다. 커밋은 명시적 호출에만 반응해야 한다.
삽질하면서 배운 것들이 있다. 트리거 문구는 내가 부르는 이름이 아니라 실제로 팀원들이 쓰는 말로 정해야 한다. invoke-jira-workflow라고 부르는 사람은 없다. 커밋, MR 생성, 파일 삭제처럼 돌이킬 수 없는 동작엔 무조건 사용자 확인 단계를 넣어야 한다 — 한 번 실수하고 나서 알았다. 스킬을 git으로 관리하면 "왜 바꿨는지"가 커밋 히스토리에 자연스럽게 쌓이는데, 별도 CHANGELOG를 만들 생각은 애초에 안 하게 됐다. Critic 리뷰를 초기에 거치면 나중에 "내가 쓸 땐 됐는데 팀원이 쓰면 왜 안 되지?" 상황이 훨씬 줄어든다.
그래서 지금은
돌아보면 역할이 다른 것 같다. 설정 파일은 "나는 이걸 중요하게 생각한다"는 선언이고, hook은 그 선언이 실제로 실행되도록 강제하는 장치고, 스킬은 반복 패턴을 팀이 공유할 수 있는 표준으로 만드는 방법이다.
Before:
executor 90회, code-reviewer 4회, security-reviewer 0회
모든 작업을 executor 하나에 의존
코드 리뷰는 "나중에 해야지" — 결국 안 함
빌드 에러는 직접 삽질
After:
15회 수정 → 자동 sonnet 코드 리뷰
30회 수정 → 자동 opus 딥 리뷰
보안 파일 3회 수정 → 자동 security-reviewer
빌드 에러 → 자동 debugger 근본 원인 분석
아직 가는 길이 멀긴 한데, 팀원들이 /team-commit으로 커밋 메시지를 만들기 시작한 걸 보면 방향은 맞는 것 같다.