← 모든 글

Claude Code Hooks: 95개의 Hook이 각각 존재하는 이유

From the guide: Claude Code Comprehensive Guide

Claude Code용으로 95개의 hook을 만들었습니다. 모두 먼저 무언가 잘못되었기 때문에 존재합니다. git-safety-guardian이 존재하는 이유는 Claude이 main에 force-push를 했기 때문입니다. recursion-guard가 존재하는 이유는 서브에이전트가 무한히 자식을 생성했기 때문입니다. blog-quality-gate가 존재하는 이유는 수동태 문장 7개와 끊어진 각주가 있는 글을 발행했기 때문입니다.1

Claude Code hook은 특정 라이프사이클 시점(세션 시작, 도구 사용 전후, 프롬프트 제출, 응답 완료)에 실행되는 셸 명령으로, 확률적인 LLM 동작 위에 결정론적 가드레일을 제공합니다. Hook은 stdin으로 JSON을 받고 stdout으로 결정(차단, 허용, 또는 수정)을 반환합니다. main 브랜치로의 force-push 차단, 재귀적 에이전트 생성 방지, 세션별 컨텍스트 주입, LLM 단독으로는 보장할 수 없는 출력 품질 통제 같은 정책을 강제합니다.

TL;DR

Claude Code hook은 AI 보조 개발 중 특정 라이프사이클 시점에 셸 명령을 실행합니다. Hook은 확률적 시스템(LLM) 위에 결정론적 보장(위험한 git 명령 차단, 컨텍스트 주입, 품질 강제)을 제공합니다. 제 인프라 전반에 95개의 hook을 구축한 결과, 가장 좋은 hook은 계획이 아니라 사고에서 나온다는 것을 알게 되었습니다. 이 글에서는 아키텍처, 가장 중요한 hook들의 기원 이야기, 그리고 9개월간의 hook 개발에서 배운 패턴을 다룹니다.


아키텍처

Claude Code은 hook이 동작을 가로채거나 수정하거나 차단할 수 있는 26개의 라이프사이클 이벤트를 노출합니다(v2.1.116 기준, 2026년 4월).2 제 hook들은 가장 흔한 6개 이벤트를 대상으로 합니다.

세션 이벤트

이벤트 발생 시점 제 Hook
SessionStart 새 세션 시작 session-start.sh: 날짜 주입, venv 검증, 재귀 상태 초기화
SessionEnd 세션 종료 임시 파일 정리

도구 실행 이벤트

이벤트 발생 시점 제 Hook
PreToolUse 모든 도구 실행 전 git-safety-guardian.sh, recursion-guard.sh, credentials-check.sh
PostToolUse 도구 완료 후 post-deliberation.sh, log-bash.sh

응답 이벤트

이벤트 발생 시점 제 Hook
UserPromptSubmit 사용자가 프롬프트 전송 컨텍스트 주입기(날짜, 브랜치, 모델 정보)
Stop Claude이 응답 완료 deliberation-pride-check.sh, reviewer-stop-gate.sh

모든 hook은 stdin으로 JSON을 받고 stdout으로 통신합니다.

{"decision": "block", "reason": "Force push to main is prohibited"}

또는 종료 코드 0으로 조용히 허용합니다.3


기원 이야기: 가장 중요한 Hook들

Hook 1: git-safety-guardian.sh (PreToolUse:Bash)

사고: 초기 Claude Code 세션에서 에이전트에게 “git 히스토리를 정리해줘”라고 요청했습니다. 에이전트는 git push --force origin main을 실행했습니다. 그 force push는 공유 브랜치의 3일치 커밋을 덮어썼습니다. 로컬 백업으로 복구했지만, 4시간의 복구 과정을 거치면서 확률적 판단이 결코 파괴적인 git 작업을 통제해서는 안 된다는 확신을 얻었습니다.

Hook:

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Only check git commands
echo "$COMMAND" | grep -qE '\bgit\b' || exit 0

# Block force push to main/master
if echo "$COMMAND" | grep -qiE 'git\s+push\s+.*--force.*\b(main|master)\b'; then
    cat << EOF
{"decision": "block", "reason": "Force push to main/master blocked by safety hook"}
EOF
fi

교훈: 이 hook은 의도를 이해하려고 하지 않습니다. 명령 문자열에 대해 패턴 매칭을 합니다. 단순하고, 결정론적이며, 영리한 프롬프팅으로도 우회할 수 없습니다. 에이전트는 여전히 기능 브랜치에는 force-push할 수 있지만(때로는 합당한 작업), main/master는 영구적으로 보호됩니다.4

누적 방어 횟수: 9개월 동안 8번의 force-push 시도를 차단했습니다.

Hook 2: recursion-guard.sh (PreToolUse:Task)

사고: 심의 시스템을 구축하는 동안, 3개의 탐색 서브에이전트를 생성하는 세션을 실행했습니다. 각 서브에이전트는 생성 한도가 없어서 자기 자신의 서브에이전트를 또 생성했습니다. 그 재귀는 API 토큰을 평소의 10배 속도로 소비했습니다. 토큰 소비가 가속되는 것을 알아챈 후 수동으로 세션을 종료했습니다.

Hook:

#!/bin/bash
CONFIG_FILE="${HOME}/.claude/configs/recursion-limits.json"
STATE_FILE="${HOME}/.claude/state/recursion-depth.json"

MAX_DEPTH=2
MAX_CHILDREN=5
DELIB_SPAWN_BUDGET=2
DELIB_MAX_AGENTS=12

# Validate integers safely (((VAR++)) crashes with set -e when VAR=0)
is_positive_int() {
    [[ "$1" =~ ^[0-9]+$ ]] && [[ "$1" -gt 0 ]]
}

핵심 설계 결정: 에이전트는 깊이를 증가시키는 대신 부모로부터 생성 예산을 상속받습니다. budget=12를 가진 루트 에이전트는 어떤 트리 형태로든 그 예산을 분배할 수 있습니다. 깊이 기반 한도는 너무 경직되어 있습니다(완벽하게 안전한 깊지만 좁은 체인을 막아버립니다).5

누적 차단 횟수: 23번의 폭주 생성 시도를 막았습니다.

Hook 3: blog-quality-gate.sh (Stop)

사고: 수동태 문장 7개, 본문에서는 참조되지만 참고 문헌 섹션에는 누락된 각주, 그리고 “팀에 의해 구현되었다”라는 첫 문장이 들어간 블로그 글을 발행했습니다. 이 글은 제 에디터에서는 다듬어진 것처럼 보였지만, 어떤 인간 리뷰어든 잡아냈을 기본적인 품질 검사를 통과하지 못했습니다.

Hook: 수정된 모든 블로그 콘텐츠 파일에 대해 12개 모듈 블로그 린터를 실행합니다. 수동태, 끊어진 각주, 누락된 메타 설명, 태그 없는 코드 블록, 인용 무결성을 점검합니다. 각 발견 사항은 구체적입니다. “47번 줄: ‘was implemented by the team’에서 수동태가 감지됨. 제안: ‘the team implemented.’”

인간의 피드백과의 평행: Hook은 작업자가 아니라 작업물을 비평합니다. “당신은 글을 못 쓴다”가 아니라 “47번 줄에 수동태가 있다”라고 말합니다. 인간의 피드백을 건설적으로 만드는 동일한 원칙이 자동화된 피드백을 유용하게 만듭니다.


95개 Hook 뒤의 패턴

설정 기반 아키텍처

제 hook들은 하드코딩된 값에서 설정 기반 동작으로 진화했습니다.

~/.claude/configs/
├── recursion-limits.json     # Depth, spawn budgets, timeouts
├── deliberation-config.json  # Consensus thresholds per task type
├── consensus-profiles.json   # security=85%, docs=50%
├── circuit-breaker.json      # Failure mode configurations
└── file-scope-rules.json     # Path-scoped hook application

임계값을 JSON 설정으로 옮긴다는 것은 bash 스크립트를 편집하지 않고도 동작을 조정할 수 있다는 의미였습니다. 보안 관련 합의는 85%로, 문서화는 50%로 필요했을 때, 설정 변경은 30초가 걸렸습니다. 코드 변경이라면 편집, 테스트, 재배포가 필요했을 것입니다.6

라이프사이클 계층화 패턴

제 95개 hook은 4개 계층으로 안전망을 형성합니다.

1계층: 예방(PreToolUse): 나쁜 일이 일어나기 전에 막습니다. git-safety-guardian, credentials-check, recursion-guard.

2계층: 컨텍스트(UserPromptSubmit, SessionStart): 에이전트가 필요로 하는 정보를 주입합니다. 날짜, 브랜치, 프로젝트 컨텍스트, 메모리 항목, 철학 원칙.

3계층: 검증(PostToolUse): 완료된 작업이 기준을 충족하는지 확인합니다. post-deliberation 합의 점검, 출력 로깅.

4계층: 품질(Stop): 최종 출력을 통제합니다. Pride check, quality gate, reviewer stop gate. 이 계층은 메타인지 모니터링을 구현하는데, 에이전트가 자신의 출력뿐 아니라 자신의 추론 품질도 평가합니다.

각 계층은 독립적입니다. PreToolUse hook이 조용히 실패해도, Stop hook은 여전히 품질 문제를 잡아냅니다. AI 에이전트 동작에 적용된 심층 방어입니다.


설정

Hook은 .claude/settings.json에 들어 있습니다.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "~/.claude/hooks/git-safety-guardian.sh",
        "timeout": 5000
      }
    ],
    "PreToolUse:Task": [
      {
        "command": "~/.claude/hooks/recursion-guard.sh"
      }
    ]
  }
}

matcher 필드는 어떤 도구가 hook을 트리거할지 필터링합니다. PreToolUse:Task는 Task 도구 호출에서만 발생하는 matcher의 축약형입니다. 비동기 hook(async: true)은 차단 없이 백그라운드에서 실행됩니다.7

범위 계층

  1. 사용자 수준(~/.claude/settings.json): 모든 프로젝트에 적용. 제 95개 hook이 여기에 있습니다.
  2. 프로젝트 수준(.claude/settings.json): 프로젝트 전용 hook 추가.
  3. Skill/Subagent frontmatter: 특정 컴포넌트의 라이프사이클로 한정.8

다르게 했을 것들

25개가 아니라 3개로 시작하세요. 첫 달에 25개의 hook을 만들었는데, 그중 다수는 에이전트가 이미 가지고 있던 컨텍스트를 추가하는 것이었습니다. 모든 도구 호출에 25개 hook을 로드하는 오버헤드는 측정 가능한 수준이었습니다. 결국 실제 가치를 만드는 hook들(실제 사고를 방지하고 실제 품질 문제를 잡아낸 hook들)로 가지치기했습니다.

처음부터 설정 기반으로. 하드코딩된 임계값을 JSON 설정으로 리팩터링하는 데 2주를 썼습니다. 처음부터 설정으로 시작했다면 그 리팩터링은 무료였을 것입니다.

테스트 인프라를 일찍. 처음 20개 hook에는 테스트가 없었습니다. 테스트 하니스(48개의 bash 통합 테스트)를 추가했을 때, 엣지 케이스에서 조용히 실패하는 3개의 hook을 발견했습니다. 테스트는 1번 hook과 함께 출시되었어야 했습니다.


핵심 요약

Hook을 시작하는 개발자에게: - 세 개의 hook으로 시작하세요. git 안전(PreToolUse:Bash), 컨텍스트 주입(UserPromptSubmit), 품질 게이트(Stop). 그 외에는 정당화할 수 있는 사고가 발생했을 때만 추가하세요 - 의사 결정 타이밍 프레임워크를 사용하세요. Hook 아키텍처는 되돌릴 수 없으므로(95개 hook이 그것에 의존합니다), hook을 작성하기 전에 라이프사이클 모델에 투자하세요

Hook을 표준화하는 팀에게: - 모든 팀원이 동일한 안전 가드레일을 받도록 사용자 수준에서 hook을 표준화하세요 - 유지 비용을 정당화하기 위해 hook 메트릭(차단된 작업, 가로챈 사고)을 추적하세요 - 자동화할 가치가 있는 새로운 패턴을 식별하기 위해 매월 hook 로그를 검토하세요


FAQ

Claude Code hook이란 무엇인가요?

Hook은 Claude Code 세션 동안 특정 라이프사이클 시점에 실행되는 셸 스크립트입니다. 특정 순간에 결정론적으로 발생합니다. 도구 실행 전(PreToolUse), 도구 완료 후(PostToolUse), 프롬프트 전송 시(UserPromptSubmit), 세션 시작 시(SessionStart), Claude이 응답 완료 시(Stop). Hook은 stdin으로 전체 컨텍스트(도구 이름, 입력, 세션 ID)와 함께 JSON을 받고, 작업을 차단하거나, 컨텍스트를 주입하거나, 품질 게이트를 강제할 수 있습니다. 확률적 LLM 위에 결정론적 보장을 제공합니다.

Claude Code이 실행할 수 있는 hook 개수는 얼마이고 성능 비용이 있나요?

Hook 개수에 엄격한 제한은 없습니다. 저는 사용 가능한 26개 라이프사이클 이벤트 중 6개에 걸쳐 95개의 hook을 실행하고 있으며(v2.1.116 기준, 2026년 4월), 이벤트당 약 200ms의 총 오버헤드가 있습니다. 실질적인 한계는 지연 시간입니다. 각 hook은 도구 호출 전후에 실행 시간을 추가합니다. 핵심 최적화는 stdin을 각각 별도로 읽는 독립적인 hook 대신, 디스패처(이벤트당 하나, 캐시된 stdin에서 hook을 순차적으로 실행)를 사용하는 것입니다. 독립적인 hook들의 동시 쓰기가 초기 설정에서 JSON 손상을 일으켰는데, 디스패처가 그 실패 모드를 완전히 제거했습니다.

Claude Code hook은 어떤 이벤트에서 발생하나요?

Claude Code은 v2.1.116(2026년 4월) 기준으로 hook을 위한 26개의 라이프사이클 이벤트를 노출합니다. 제 hook이 대상으로 하는 가장 흔한 6개는 SessionStart(새 세션 시작), SessionEnd(세션 종료), PreToolUse(모든 도구 실행 전), PostToolUse(도구 완료 후), UserPromptSubmit(사용자가 프롬프트 전송), Stop(Claude이 응답 완료)입니다. 그 외에 SubagentStart, PermissionRequest, PermissionDenied, TaskCreated, CwdChanged, FileChanged, PreCompact 등이 있습니다. PreToolUse와 PostToolUse는 PreToolUse:Bash 또는 PreToolUse:Task 같은 matcher로 더 한정해서 특정 도구 유형에서만 발생하도록 할 수 있습니다. 각 이벤트는 stdin으로 관련 컨텍스트와 함께 JSON을 받습니다.

Claude Code hook이 도구 호출을 차단할 수 있나요?

네. {"decision": "block", "reason": "explanation"}을 stdout으로 출력하는 hook은 도구 호출 실행을 막습니다. 이것이 안전 hook의 핵심 메커니즘입니다. 제 git-safety-guardian은 main으로의 force push를 차단하고, credentials-check는 .env 파일의 읽기를 차단하며, recursion-guard는 과도한 에이전트 생성을 차단합니다. 차단은 결정론적이며 프롬프팅으로 우회할 수 없습니다. 종료 코드 0으로 조용히 종료하는 hook은 작업이 진행되도록 허용합니다.

PreToolUse와 PostToolUse hook의 차이점은 무엇인가요?

PreToolUse는 도구 실행 전에 발생하며 작업을 완전히 차단할 수 있습니다. 안전 게이트에 사용하세요. 위험한 명령 차단, 자격 증명 점검, 생성 예산 강제. PostToolUse는 도구 완료 후에 발생하며 피드백을 제공하거나 후속 작업을 트리거할 수 있습니다. 검증에 사용하세요. 출력 품질 점검, 활동 로깅, 합의 검증. 둘은 함께 4계층 안전망의 1계층과 3계층을 형성합니다. 예방(PreToolUse), 컨텍스트 주입(UserPromptSubmit), 검증(PostToolUse), 품질 통제(Stop).

참고 문헌


  1. 저자의 hook 인프라. v2.1.116(2026년 4월) 기준 사용 가능한 26개 라이프사이클 이벤트 중 6개에 걸친 95개 hook, 9개월(2025-2026)에 걸쳐 개발됨. 

  2. Anthropic, “Claude Code Hooks Reference,” 2026. v2.1.116(2026년 4월) 기준 26개 라이프사이클 이벤트 유형. 

  3. Anthropic, “Claude Code Documentation,” 2025. Hook 입력/출력 JSON 형식. 

  4. 저자의 git-safety-guardian.sh. ~/.claude/state/에 추적된 8번의 force-push 시도 차단. 

  5. 저자의 recursion-guard.sh. ~/.claude/configs/recursion-limits.json에 문서화된 예산 상속 모델. 

  6. 저자의 설정 기반 아키텍처. 모든 hook 임계값과 규칙을 인코딩하는 14개의 JSON 설정 파일. 

  7. Anthropic, “Claude Code Documentation,” 2025. Hook 설정 및 비동기 실행. 

  8. Anthropic, “Claude Code Documentation,” 2025. Hook 범위 계층. 

관련 게시물

Claude Code 커스텀 스킬 만들기: 완벽 튜토리얼

코드 리뷰 스킬을 처음부터 구축합니다. 디렉토리 구조, 프론트매터 필드, LLM 기반 매칭, 컨텍스트 예산, 자동 활성화를 다룹니다.

10 분 소요

컨텍스트 윈도우 관리: 50번의 세션에서 배운 AI 개발의 교훈

50번의 Claude Code 세션에서 토큰 소비량을 측정했습니다. 컨텍스트 소진은 눈치채기 전에 출력 품질을 저하시킵니다. 이를 해결하는 패턴을 소개합니다.

6 분 소요

Claude Code Hooks 튜토리얼: 처음부터 만드는 5가지 프로덕션 Hooks

5가지 실용적인 Claude Code hooks를 처음부터 구축합니다: 자동 포맷팅, 보안 게이트, 테스트 러너, 알림, 품질 검사.

6 분 소요