상위: 08_00_MOC
Claude Code — Hooks 컨텍스트 전처리와 강제 집행
Hooks의 본질
훅은 CLAUDE.md와 근본적으로 다르다. CLAUDE.md는 “부탁”이고 훅은 “코드로 실행되는 강제”다.
“Settings rules are enforced by the client regardless of what Claude decides to do. CLAUDE.md instructions shape Claude’s behavior but are not a hard enforcement layer.” — How Claude remembers your project, Anthropic
훅은 Claude의 판단을 우회해 특정 라이프사이클 이벤트에서 셸 명령을 실행한다.
라이프사이클 이벤트
이벤트 목록은 다음과 같다.
세션 단위
SessionStart— 세션 시작 또는 재개 시SessionEnd— 세션 종료 시
턴 단위
UserPromptSubmit— 프롬프트 제출 직후, Claude 처리 전Stop— Claude가 응답 완료 시StopFailure— API 오류로 턴 종료 시
도구 호출 단위 (에이전틱 루프)
PreToolUse— 도구 실행 전 (차단 가능)PostToolUse— 도구 실행 성공 후PostToolUseFailure— 도구 실행 실패 후PostToolBatch— 병렬 도구 호출 배치 완료 후
기타
InstructionsLoaded— CLAUDE.md 또는.claude/rules/*.md파일 로드 시SubagentStart— 서브에이전트 스폰 시SubagentStop— 서브에이전트 완료 시PermissionRequest— 권한 대화상자 표시 시PermissionDenied— 도구 호출 거부 시
additionalContext — 컨텍스트 주입의 올바른 방법
훅이 Claude의 컨텍스트에 정보를 주입하려면 반드시 additionalContext JSON 필드를 써야 한다.
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "This file is generated. Edit src/schema.ts and run `bun generate` instead."
}
}주의할 점이 있다. 일반 stdout(exit 0)은 Claude 컨텍스트에 들어가지 않고 디버그 로그에만 남는다. “훅은 도는데 Claude가 못 읽는” 문제는 대개 여기서 비롯된다.
additionalContext가 컨텍스트에 주입되는 위치 (이벤트별):
SessionStart/SubagentStart: 대화 시작 전, 첫 프롬프트 앞UserPromptSubmit: 제출된 프롬프트 옆PreToolUse/PostToolUse/PostToolBatch: 도구 결과 옆Stop/SubagentStop: 턴 끝 (대화 계속됨)
10,000자 한도: additionalContext를 포함한 출력 문자열은 10,000자가 상한이다. 초과분은 파일에 저장되고 그 경로가 Claude에 전달된다.
작성 원칙은 다음과 같다.
✅ "The deployment target is production" — 사실 서술
✅ "This repo uses `bun test`" — 사실 서술
❌ "You must use bun test" — 명령형 → 프롬프트 인젝션 방어 트리거
도구 출력 변환 — 토큰 절약의 핵심
PreToolUse: 입력 수정
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}PostToolUse: 출력 요약
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"updatedToolOutput": "Formatted summary of tool result"
}
}토큰 절약 실전 예 — 테스트 출력 필터링:
#!/bin/bash
# ~/.claude/hooks/filter-test-output.sh
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')
# 테스트 실행 시 실패/에러만 필터링해 컨텍스트 절약
if [[ "$cmd" =~ ^(npm test|pytest|go test) ]]; then
filtered_cmd="$cmd 2>&1 | grep -A 5 -E '(FAIL|ERROR|error:)' | head -100"
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"updatedInput\":{\"command\":\"$filtered_cmd\"}}}"
else
echo "{}"
fisettings.json 연결:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/filter-test-output.sh"
}
]
}
]
}
}“Instead of Claude reading a 10,000-line log file to find errors, a hook can grep for ERROR and return only matching lines, reducing context from tens of thousands of tokens to hundreds.” — Manage costs effectively, Anthropic
강제 차단 — PreToolUse 패턴
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0 # 결정 없음 — 일반 권한 흐름 적용
fiexit code 의미:
0: JSON 처리, stdout은 디버그 로그에만2: JSON 무시, stderr 내용을 Claude에 오류 메시지로 전달 (도구 차단)
SessionStart 훅 — 세션 컨텍스트 주입
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2",
"sessionTitle": "auth-refactor"
}
}세션을 시작할 때마다 개발 컨텍스트를 자동으로 주입하므로, Claude가 현재 작업 상태를 곧바로 파악한다.
--continue나 --resume으로 재개하면 SessionStart 훅은 source: "resume"로 다시 실행되어 타임스탬프나 커밋 SHA 같은 동적 정보를 갱신할 수 있다. 반면 다른 이벤트의 additionalContext는 트랜스크립트에 저장됐다가 그대로 재생되므로 다시 실행되지 않는다.
InstructionsLoaded 훅 — 로드 디버깅
어떤 instruction 파일이 언제 로드됐는지 기록한다.
{
"hooks": {
"InstructionsLoaded": [
{
"hooks": [
{
"type": "command",
"command": "echo \"Loaded: $CLAUDE_INSTRUCTION_FILE\" >> ~/.claude/instruction-log.txt"
}
]
}
]
}
}path-scoped 규칙을 디버깅하거나 온디맨드 로드 타이밍을 확인할 때 쓸모가 있다.
실패모드 매핑
- 01 오염 완화: 대용량 도구 출력을
updatedToolOutput으로 요약 → 메인 컨텍스트 오염 방지 - 04 충돌 완화: CLAUDE.md의 “절대 금지” 대신
PreToolUse훅으로 결정론적 차단 - 산만 완화: 로그·테스트 출력을 grep 필터로 압축 → 핵심 정보만 컨텍스트에
참고문헌
- Claude Code Hooks — Anthropic, 공식 문서
- Manage costs effectively — Anthropic, 공식 문서
- How Claude remembers your project — Anthropic, 공식 문서
- Explore the context window — Anthropic, 공식 문서
관련 노트
- 08_01-CLAUDE.md-메모리계층 — CLAUDE.md vs Hooks 역할 구분
- 08_09-실전레시피-스니펫모음 — 훅 스니펫 모음
- 08_08-실패모드-공식기능-매핑표 — 오염·충돌 실패모드 대응