4. Hooks로 워크플로우 자동화하기
Hook 이벤트·prompt/agent hook·HTTP 전송·디버깅 등 워크플로우 자동화 가이드
Hooks로 워크플로우 자동화하기
Claude Code가 파일을 편집하거나, 작업을 완료하거나, 입력이 필요할 때 셸 명령을 자동으로 실행합니다. 코드 포맷팅, 알림 전송, 명령 검증, 프로젝트 규칙 적용 등을 수행할 수 있습니다.
Hooks는 Claude Code의 라이프사이클에서 특정 시점에 실행되는 사용자 정의 셸 명령입니다. Claude Code의 동작에 대한 결정론적 제어를 제공하여, LLM이 실행 여부를 판단하는 것에 의존하지 않고 특정 작업이 반드시 수행되도록 보장합니다. Hooks를 사용하여 프로젝트 규칙을 적용하고, 반복 작업을 자동화하며, Claude Code를 기존 도구와 통합할 수 있습니다.
결정론적 규칙이 아닌 판단이 필요한 결정에는 프롬프트 기반 hooks나 에이전트 기반 hooks를 사용하여 Claude 모델이 조건을 평가하도록 할 수 있습니다.
Claude Code를 확장하는 다른 방법으로는, Claude에게 추가 지침과 실행 가능한 명령을 제공하는 skills, 격리된 컨텍스트에서 작업을 실행하는 subagents, 프로젝트 간에 공유할 수 있는 확장을 패키징하는 plugins를 참고하세요.
팁: 이 가이드는 일반적인 사용 사례와 시작하는 방법을 다룹니다. 전체 이벤트 스키마, JSON 입출력 형식, 비동기 hooks 및 MCP 도구 hooks 등 고급 기능에 대해서는 Hooks 레퍼런스를 참조하세요.
첫 번째 hook 설정하기
Hook을 만드는 가장 빠른 방법은 Claude Code의 /hooks 대화형 메뉴를 사용하는 것입니다. 이 가이드에서는 데스크톱 알림 hook을 만들어, 터미널을 지켜보는 대신 Claude가 입력을 기다릴 때 알림을 받을 수 있도록 합니다.
Step 1: hooks 메뉴 열기
Claude Code CLI에서 /hooks를 입력합니다. 사용 가능한 모든 hook 이벤트 목록과 모든 hooks를 비활성화하는 옵션이 표시됩니다. 각 이벤트는 사용자 정의 코드를 실행할 수 있는 Claude 라이프사이클의 특정 시점에 해당합니다. Notification을 선택하여 Claude가 주의를 요할 때 실행되는 hook을 만드세요.
Step 2: 매처 설정하기
메뉴에 매처 목록이 표시되며, 매처는 hook이 실행되는 시점을 필터링합니다. 모든 알림 유형에 대해 실행되도록 매처를 *로 설정합니다. 나중에 permission_prompt이나 idle_prompt 같은 특정 값으로 매처를 변경하여 범위를 좁힐 수 있습니다.
Step 3: 명령 추가하기
+ Add new hook…을 선택합니다. 메뉴에서 이벤트가 발생할 때 실행할 셸 명령을 입력하라는 메시지가 표시됩니다. Hooks는 제공하는 모든 셸 명령을 실행할 수 있으므로, 사용하는 플랫폼의 내장 알림 도구를 사용할 수 있습니다. OS에 맞는 명령을 복사하세요:
macOS
osascript를 사용하여 AppleScript를 통해 네이티브 macOS 알림을 트리거합니다:
osascript -e 'display notification "Claude Code needs your attention" with title "Claude Code"'
Linux
대부분의 Linux 데스크톱에 알림 데몬과 함께 사전 설치된 notify-send를 사용합니다:
notify-send 'Claude Code' 'Claude Code needs your attention'
Windows (PowerShell)
PowerShell을 사용하여 .NET의 Windows Forms를 통해 네이티브 메시지 박스를 표시합니다:
powershell.exe -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')"
Step 4: 저장 위치 선택하기
메뉴에서 hook 설정을 저장할 위치를 묻습니다. User settings를 선택하면 ~/.claude/settings.json에 저장되어 모든 프로젝트에 hook이 적용됩니다. Project settings를 선택하면 현재 프로젝트에만 적용됩니다. 사용 가능한 모든 범위에 대해서는 hook 저장 위치 설정을 참조하세요.
Step 5: hook 테스트하기
Esc를 눌러 CLI로 돌아갑니다. Claude에게 권한이 필요한 작업을 요청한 다음 터미널에서 다른 곳으로 전환합니다. 데스크톱 알림을 받아야 합니다.
자동화할 수 있는 것들
Hooks를 사용하면 Claude Code 라이프사이클의 주요 시점에서 코드를 실행할 수 있습니다: 편집 후 파일 포맷팅, 실행 전 명령 차단, Claude가 입력을 필요로 할 때 알림 전송, 세션 시작 시 컨텍스트 주입 등. hook 이벤트의 전체 목록은 Hooks 레퍼런스를 참조하세요.
각 예제에는 설정 파일에 추가할 수 있는 바로 사용 가능한 설정 블록이 포함되어 있습니다. 가장 일반적인 패턴들:
Claude가 입력을 필요로 할 때 알림 받기
Claude가 작업을 마치고 입력을 필요로 할 때마다 데스크톱 알림을 받아, 터미널을 확인하지 않고도 다른 작업으로 전환할 수 있습니다.
이 hook은 Claude가 입력이나 권한을 기다릴 때 실행되는 Notification 이벤트를 사용합니다. 아래 각 탭은 플랫폼의 네이티브 알림 명령을 사용합니다. ~/.claude/settings.json에 추가하거나, 위의 대화형 안내를 사용하여 /hooks로 설정하세요:
macOS
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
Linux
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
}
]
}
]
}
}
Windows (PowerShell)
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
}
]
}
]
}
}
편집 후 코드 자동 포맷팅
Claude가 편집하는 모든 파일에 Prettier를 자동으로 실행하여, 수동 개입 없이 일관된 포맷팅을 유지합니다.
이 hook은 PostToolUse 이벤트에 Edit|Write 매처를 사용하여 파일 편집 도구 사용 후에만 실행됩니다. 명령은 jq로 편집된 파일 경로를 추출하고 Prettier에 전달합니다. 프로젝트 루트의 .claude/settings.json에 추가하세요:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
참고: 이 페이지의 Bash 예제는 JSON 파싱에
jq를 사용합니다.brew install jq(macOS),apt-get install jq(Debian/Ubuntu)로 설치하거나,jq다운로드를 참조하세요.
보호된 파일 편집 차단
Claude가 .env, package-lock.json 또는 .git/ 내의 파일과 같은 민감한 파일을 수정하지 못하도록 합니다. Claude는 편집이 차단된 이유에 대한 피드백을 받아 접근 방식을 조정할 수 있습니다.
이 예제는 hook이 호출하는 별도의 스크립트 파일을 사용합니다. 스크립트는 대상 파일 경로를 보호된 패턴 목록과 대조하여 확인하고, 편집을 차단하기 위해 종료 코드 2로 종료합니다.
Step 1: hook 스크립트 생성
다음을 .claude/hooks/protect-files.sh에 저장합니다:
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0
Step 2: 스크립트를 실행 가능하게 만들기 (macOS/Linux)
Claude Code가 실행할 수 있도록 hook 스크립트에 실행 권한이 있어야 합니다:
chmod +x .claude/hooks/protect-files.sh
Step 3: hook 등록
.claude/settings.json에 Edit 또는 Write 도구 호출 전에 스크립트를 실행하는 PreToolUse hook을 추가합니다:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
컴팩션 후 컨텍스트 재주입
Claude의 컨텍스트 윈도우가 가득 차면, 컴팩션이 대화를 요약하여 공간을 확보합니다. 이 과정에서 중요한 세부 사항이 손실될 수 있습니다. compact 매처가 있는 SessionStart hook을 사용하여 모든 컴팩션 후에 중요한 컨텍스트를 재주입합니다.
명령이 stdout에 쓰는 모든 텍스트는 Claude의 컨텍스트에 추가됩니다. 이 예제는 Claude에게 프로젝트 규칙과 최근 작업을 상기시킵니다. 프로젝트 루트의 .claude/settings.json에 추가하세요:
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
}
]
}
]
}
}
echo를 git log --oneline -5와 같이 동적 출력을 생성하는 다른 명령으로 대체할 수 있습니다. 모든 세션 시작 시 컨텍스트를 주입하려면 CLAUDE.md를 사용하는 것을 고려하세요. 환경 변수에 대해서는 레퍼런스의 CLAUDE_ENV_FILE을 참조하세요.
설정 변경 감사
세션 중 설정이나 스킬 파일이 변경될 때 추적합니다. ConfigChange 이벤트는 외부 프로세스나 편집기가 설정 파일을 수정할 때 발생하므로, 규정 준수를 위해 변경 사항을 기록하거나 승인되지 않은 수정을 차단할 수 있습니다.
이 예제는 각 변경 사항을 감사 로그에 추가합니다. ~/.claude/settings.json에 추가하세요:
{
"hooks": {
"ConfigChange": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
}
]
}
]
}
}
매처는 설정 유형별로 필터링합니다: user_settings, project_settings, local_settings, policy_settings, 또는 skills. 변경이 적용되는 것을 차단하려면 종료 코드 2로 종료하거나 {"decision": "block"}을 반환합니다. 전체 입력 스키마는 ConfigChange 레퍼런스를 참조하세요.
Hooks의 작동 방식
Hook 이벤트는 Claude Code의 특정 라이프사이클 시점에서 발생합니다. 이벤트가 발생하면 일치하는 모든 hooks가 병렬로 실행되며, 동일한 hook 명령은 자동으로 중복 제거됩니다. 아래 표는 각 이벤트와 실행 시점을 보여줍니다:
| 이벤트 | 실행 시점 |
|---|---|
SessionStart | 세션이 시작되거나 재개될 때 |
UserPromptSubmit | 프롬프트를 제출할 때, Claude가 처리하기 전 |
PreToolUse | 도구 호출이 실행되기 전. 차단 가능 |
PermissionRequest | 권한 대화상자가 나타날 때 |
PostToolUse | 도구 호출이 성공한 후 |
PostToolUseFailure | 도구 호출이 실패한 후 |
Notification | Claude Code가 알림을 보낼 때 |
SubagentStart | 서브에이전트가 생성될 때 |
SubagentStop | 서브에이전트가 완료될 때 |
Stop | Claude가 응답을 마칠 때 |
TeammateIdle | 에이전트 팀 팀원이 유휴 상태로 전환되려 할 때 |
TaskCompleted | 작업이 완료로 표시될 때 |
InstructionsLoaded | CLAUDE.md 또는 .claude/rules/*.md 파일이 컨텍스트에 로드될 때. 세션 시작 시와 세션 중 파일이 지연 로드될 때 발생 |
ConfigChange | 세션 중 설정 파일이 변경될 때 |
WorktreeCreate | --worktree 또는 isolation: "worktree"를 통해 worktree가 생성될 때. 기본 git 동작을 대체 |
WorktreeRemove | 세션 종료 시 또는 서브에이전트가 완료될 때 worktree가 제거될 때 |
PreCompact | 컨텍스트 컴팩션 전 |
SessionEnd | 세션이 종료될 때 |
각 hook에는 실행 방식을 결정하는 type이 있습니다. 대부분의 hooks는 셸 명령을 실행하는 "type": "command"를 사용합니다. 세 가지 다른 유형도 사용할 수 있습니다:
"type": "http": 이벤트 데이터를 URL로 POST합니다. HTTP hooks를 참조하세요."type": "prompt": 단일 턴 LLM 평가. 프롬프트 기반 hooks를 참조하세요."type": "agent": 도구 접근이 가능한 다중 턴 검증. 에이전트 기반 hooks를 참조하세요.
입력 읽기와 출력 반환
Hooks는 stdin, stdout, stderr, 종료 코드를 통해 Claude Code와 통신합니다. 이벤트가 발생하면 Claude Code는 이벤트별 데이터를 JSON으로 스크립트의 stdin에 전달합니다. 스크립트는 해당 데이터를 읽고 작업을 수행한 다음, 종료 코드를 통해 Claude Code에 다음 행동을 알려줍니다.
Hook 입력
모든 이벤트에는 session_id와 cwd 같은 공통 필드가 포함되지만, 각 이벤트 유형은 서로 다른 데이터를 추가합니다. 예를 들어, Claude가 Bash 명령을 실행할 때 PreToolUse hook은 stdin으로 다음과 같은 데이터를 받습니다:
{
"session_id": "abc123", // unique ID for this session
"cwd": "/Users/sarah/myproject", // working directory when the event fired
"hook_event_name": "PreToolUse", // which event triggered this hook
"tool_name": "Bash", // the tool Claude is about to use
"tool_input": { // the arguments Claude passed to the tool
"command": "npm test" // for Bash, this is the shell command
}
}
스크립트에서 해당 JSON을 파싱하고 모든 필드에 대해 작업할 수 있습니다. UserPromptSubmit hooks는 대신 prompt 텍스트를 받고, SessionStart hooks는 source (startup, resume, clear, compact)를 받는 등 이벤트마다 다릅니다. 공유 필드는 레퍼런스의 공통 입력 필드를, 이벤트별 스키마는 각 이벤트 섹션을 참조하세요.
Hook 출력
스크립트는 stdout 또는 stderr에 쓰고 특정 종료 코드로 종료하여 Claude Code에 다음 행동을 알려줍니다. 예를 들어, 명령을 차단하려는 PreToolUse hook:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q "drop table"; then
echo "Blocked: dropping tables is not allowed" >&2 # stderr becomes Claude's feedback
exit 2 # exit 2 = block the action
fi
exit 0 # exit 0 = let it proceed
종료 코드에 따라 다음 동작이 결정됩니다:
- 종료 코드 0: 작업이 진행됩니다.
UserPromptSubmit과SessionStarthooks의 경우, stdout에 쓴 내용이 Claude의 컨텍스트에 추가됩니다. - 종료 코드 2: 작업이 차단됩니다. stderr에 이유를 쓰면 Claude가 피드백으로 받아 조정할 수 있습니다.
- 기타 종료 코드: 작업이 진행됩니다. stderr는 기록되지만 Claude에게 표시되지 않습니다.
Ctrl+O로 상세 모드를 토글하면 트랜스크립트에서 이 메시지를 볼 수 있습니다.
구조화된 JSON 출력
종료 코드는 허용과 차단 두 가지 옵션을 제공합니다. 더 세밀한 제어를 위해 종료 코드 0으로 종료하고 대신 stdout에 JSON 객체를 출력합니다.
참고: stderr 메시지와 함께 종료 코드 2를 사용하여 차단하거나, 구조화된 제어를 위해 JSON과 함께 종료 코드 0을 사용하세요. 두 가지를 혼합하지 마세요: 종료 코드 2일 때 Claude Code는 JSON을 무시합니다.
예를 들어, PreToolUse hook은 도구 호출을 거부하고 Claude에게 이유를 알리거나, 사용자에게 승인을 위해 에스컬레이션할 수 있습니다:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Use rg instead of grep for better performance"
}
}
Claude Code는 permissionDecision을 읽고 도구 호출을 취소한 다음, permissionDecisionReason을 피드백으로 Claude에 전달합니다. 다음 세 가지 옵션은 PreToolUse에 특화되어 있습니다:
"allow": 권한 프롬프트를 표시하지 않고 진행"deny": 도구 호출을 취소하고 이유를 Claude에 전송"ask": 사용자에게 권한 프롬프트를 정상적으로 표시
다른 이벤트는 다른 결정 패턴을 사용합니다. 예를 들어, PostToolUse와 Stop hooks는 최상위 레벨의 decision: "block" 필드를 사용하고, PermissionRequest는 hookSpecificOutput.decision.behavior를 사용합니다. 이벤트별 전체 분류는 레퍼런스의 요약 표를 참조하세요.
UserPromptSubmit hooks의 경우, Claude의 컨텍스트에 텍스트를 주입하려면 대신 additionalContext를 사용합니다. 프롬프트 기반 hooks (type: "prompt")는 출력을 다르게 처리합니다: 프롬프트 기반 hooks를 참조하세요.
매처로 hooks 필터링하기
매처가 없으면 hook은 해당 이벤트가 발생할 때마다 실행됩니다. 매처를 사용하면 범위를 좁힐 수 있습니다. 예를 들어, 모든 도구 호출 후가 아닌 파일 편집 후에만 포매터를 실행하려면 PostToolUse hook에 매처를 추가합니다:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "prettier --write ..." }
]
}
]
}
}
"Edit|Write" 매처는 도구 이름과 일치하는 정규식 패턴입니다. 이 hook은 Claude가 Edit 또는 Write 도구를 사용할 때만 실행되며, Bash, Read 또는 다른 도구를 사용할 때는 실행되지 않습니다.
각 이벤트 유형은 특정 필드에 대해 매칭합니다. 매처는 정확한 문자열과 정규식 패턴을 지원합니다:
| 이벤트 | 매처 필터 대상 | 매처 값 예시 |
|---|---|---|
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest | 도구 이름 | Bash, Edit|Write, mcp__.* |
SessionStart | 세션 시작 방식 | startup, resume, clear, compact |
SessionEnd | 세션 종료 이유 | clear, logout, prompt_input_exit, bypass_permissions_disabled, other |
Notification | 알림 유형 | permission_prompt, idle_prompt, auth_success, elicitation_dialog |
SubagentStart | 에이전트 유형 | Bash, Explore, Plan, 또는 사용자 정의 에이전트 이름 |
PreCompact | 컴팩션 트리거 | manual, auto |
SubagentStop | 에이전트 유형 | SubagentStart와 동일한 값 |
ConfigChange | 설정 소스 | user_settings, project_settings, local_settings, policy_settings, skills |
UserPromptSubmit, Stop, TeammateIdle, TaskCompleted, WorktreeCreate, WorktreeRemove | 매처 미지원 | 모든 발생 시 항상 실행 |
다양한 이벤트 유형에서의 매처 사용 예시:
모든 Bash 명령 기록
Bash 도구 호출만 매칭하고 각 명령을 파일에 기록합니다. PostToolUse 이벤트는 명령이 완료된 후 발생하므로, tool_input.command에 실행된 명령이 포함됩니다. Hook은 이벤트 데이터를 JSON으로 stdin에서 받고, jq -r '.tool_input.command'가 명령 문자열만 추출하며, >>가 로그 파일에 추가합니다:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
}
]
}
]
}
}
MCP 도구 매칭
MCP 도구는 내장 도구와 다른 명명 규칙을 사용합니다: mcp__<server>__<tool>, 여기서 <server>는 MCP 서버 이름이고 <tool>은 제공하는 도구입니다. 예를 들어, mcp__github__search_repositories 또는 mcp__filesystem__read_file입니다. 정규식 매처를 사용하여 특정 서버의 모든 도구를 대상으로 하거나, mcp__.*__write.* 같은 패턴으로 여러 서버에 걸쳐 매칭할 수 있습니다. 전체 예시 목록은 레퍼런스의 MCP 도구 매칭을 참조하세요.
아래 명령은 jq로 hook의 JSON 입력에서 도구 이름을 추출하여 stderr에 쓰며, 상세 모드(Ctrl+O)에서 표시됩니다:
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__github__.*",
"hooks": [
{
"type": "command",
"command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >&2"
}
]
}
]
}
}
세션 종료 시 정리
SessionEnd 이벤트는 세션이 종료된 이유에 대한 매처를 지원합니다. 이 hook은 clear (/clear 실행 시)에서만 실행되며, 일반 종료 시에는 실행되지 않습니다:
{
"hooks": {
"SessionEnd": [
{
"matcher": "clear",
"hooks": [
{
"type": "command",
"command": "rm -f /tmp/claude-scratch-*.txt"
}
]
}
]
}
}
전체 매처 구문은 Hooks 레퍼런스를 참조하세요.
Hook 저장 위치 설정
hook을 추가하는 위치에 따라 적용 범위가 결정됩니다:
| 위치 | 범위 | 공유 가능 여부 |
|---|---|---|
~/.claude/settings.json | 모든 프로젝트 | 불가, 로컬 머신 전용 |
.claude/settings.json | 단일 프로젝트 | 가능, 저장소에 커밋 가능 |
.claude/settings.local.json | 단일 프로젝트 | 불가, gitignore 처리됨 |
| 관리형 정책 설정 | 조직 전체 | 가능, 관리자 제어 |
Plugin hooks/hooks.json | 플러그인 활성화 시 | 가능, 플러그인에 번들 |
| Skill 또는 agent frontmatter | 스킬 또는 에이전트 활성 중 | 가능, 컴포넌트 파일에 정의 |
Claude Code에서 /hooks 메뉴를 사용하여 hooks를 대화형으로 추가, 삭제, 조회할 수도 있습니다. 모든 hooks를 한 번에 비활성화하려면 /hooks 메뉴 하단의 토글을 사용하거나 설정 파일에서 "disableAllHooks": true를 설정합니다.
/hooks 메뉴를 통해 추가된 hooks는 즉시 적용됩니다. Claude Code가 실행 중일 때 설정 파일을 직접 편집하면, /hooks 메뉴에서 검토하거나 세션을 다시 시작할 때까지 변경 사항이 적용되지 않습니다.
프롬프트 기반 hooks
결정론적 규칙이 아닌 판단이 필요한 결정에는 type: "prompt" hooks를 사용합니다. 셸 명령을 실행하는 대신, Claude Code가 프롬프트와 hook의 입력 데이터를 Claude 모델(기본적으로 Haiku)에 보내 결정을 내립니다. 더 높은 성능이 필요한 경우 model 필드로 다른 모델을 지정할 수 있습니다.
모델의 유일한 역할은 JSON으로 예/아니오 결정을 반환하는 것입니다:
"ok": true: 작업이 진행됩니다"ok": false: 작업이 차단됩니다. 모델의"reason"이 Claude에 피드백으로 전달되어 조정할 수 있습니다.
이 예제는 Stop hook을 사용하여 요청된 모든 작업이 완료되었는지 모델에 묻습니다. 모델이 "ok": false를 반환하면 Claude는 계속 작업하며 reason을 다음 지시로 사용합니다:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
}
]
}
]
}
}
전체 설정 옵션은 레퍼런스의 프롬프트 기반 hooks를 참조하세요.
에이전트 기반 hooks
검증에 파일 검사나 명령 실행이 필요한 경우 type: "agent" hooks를 사용합니다. 단일 LLM 호출을 하는 프롬프트 hooks와 달리, 에이전트 hooks는 파일을 읽고, 코드를 검색하고, 다른 도구를 사용하여 조건을 확인한 후 결정을 반환하는 서브에이전트를 생성합니다.
에이전트 hooks는 프롬프트 hooks와 동일한 "ok" / "reason" 응답 형식을 사용하지만, 기본 타임아웃이 60초이고 최대 50번의 도구 사용 턴을 지원합니다.
이 예제는 Claude가 멈추기 전에 테스트가 통과하는지 확인합니다:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
"timeout": 120
}
]
}
]
}
}
hook 입력 데이터만으로 결정을 내릴 수 있는 경우 프롬프트 hooks를 사용합니다. 코드베이스의 실제 상태와 대조하여 무언가를 검증해야 하는 경우 에이전트 hooks를 사용합니다.
전체 설정 옵션은 레퍼런스의 에이전트 기반 hooks를 참조하세요.
HTTP hooks
셸 명령을 실행하는 대신 HTTP 엔드포인트로 이벤트 데이터를 POST하려면 type: "http" hooks를 사용합니다. 엔드포인트는 command hook이 stdin으로 받는 것과 동일한 JSON을 수신하고, 동일한 JSON 형식으로 HTTP 응답 본문을 통해 결과를 반환합니다.
HTTP hooks는 웹 서버, 클라우드 함수 또는 외부 서비스가 hook 로직을 처리하도록 하려는 경우에 유용합니다: 예를 들어, 팀 전체의 도구 사용 이벤트를 기록하는 공유 감사 서비스가 있습니다.
이 예제는 모든 도구 사용을 로컬 로깅 서비스에 전송합니다:
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/tool-use",
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}
엔드포인트는 command hooks와 동일한 출력 형식을 사용하는 JSON 응답 본문을 반환해야 합니다. 도구 호출을 차단하려면 적절한 hookSpecificOutput 필드와 함께 2xx 응답을 반환합니다. HTTP 상태 코드만으로는 작업을 차단할 수 없습니다.
헤더 값은 $VAR_NAME 또는 ${VAR_NAME} 구문을 사용한 환경 변수 보간을 지원합니다. allowedEnvVars 배열에 나열된 변수만 치환되며, 나머지 $VAR 참조는 비어 있는 상태로 유지됩니다.
참고: HTTP hooks는 설정 JSON을 직접 편집하여 구성해야 합니다.
/hooks대화형 메뉴는 command hooks 추가만 지원합니다.
전체 설정 옵션과 응답 처리에 대해서는 레퍼런스의 HTTP hooks를 참조하세요.
제한 사항 및 문제 해결
제한 사항
- Command hooks는 stdout, stderr, 종료 코드를 통해서만 통신합니다. 직접 명령이나 도구 호출을 트리거할 수 없습니다. HTTP hooks는 대신 응답 본문을 통해 통신합니다.
- Hook 타임아웃은 기본적으로 10분이며,
timeout필드(초 단위)로 hook별 설정이 가능합니다. PostToolUsehooks는 도구가 이미 실행되었으므로 작업을 취소할 수 없습니다.PermissionRequesthooks는 비대화형 모드 (-p)에서 실행되지 않습니다. 자동화된 권한 결정에는PreToolUsehooks를 사용하세요.Stophooks는 작업 완료 시에만이 아니라 Claude가 응답을 마칠 때마다 실행됩니다. 사용자 중단 시에는 실행되지 않습니다.
Hook이 실행되지 않는 경우
Hook이 설정되었지만 실행되지 않습니다.
/hooks를 실행하여 올바른 이벤트 아래에 hook이 표시되는지 확인합니다- 매처 패턴이 도구 이름과 정확히 일치하는지 확인합니다 (매처는 대소문자를 구분합니다)
- 올바른 이벤트 유형을 트리거하고 있는지 확인합니다 (예:
PreToolUse는 도구 실행 전,PostToolUse는 실행 후) - 비대화형 모드(
-p)에서PermissionRequesthooks를 사용하는 경우, 대신PreToolUse로 전환합니다
출력의 hook 오류
트랜스크립트에 "PreToolUse hook error: ..." 같은 메시지가 표시됩니다.
- 스크립트가 예기치 않게 0이 아닌 종료 코드로 종료되었습니다. 샘플 JSON을 파이프하여 수동으로 테스트합니다:
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh echo $? # Check the exit code - "command not found"가 표시되면, 절대 경로나
$CLAUDE_PROJECT_DIR를 사용하여 스크립트를 참조합니다 - "jq: command not found"가 표시되면,
jq를 설치하거나 Python/Node.js를 사용하여 JSON을 파싱합니다 - 스크립트가 전혀 실행되지 않으면, 실행 권한을 부여합니다:
chmod +x ./my-hook.sh
/hooks에 구성된 hooks가 표시되지 않는 경우
설정 파일을 편집했지만 메뉴에 hooks가 나타나지 않습니다.
- 세션을 다시 시작하거나
/hooks를 열어 다시 로드합니다./hooks메뉴를 통해 추가된 hooks는 즉시 적용되지만, 수동 파일 편집은 다시 로드해야 합니다. - JSON이 유효한지 확인합니다 (후행 쉼표와 주석은 허용되지 않습니다)
- 설정 파일이 올바른 위치에 있는지 확인합니다: 프로젝트 hooks는
.claude/settings.json, 전역 hooks는~/.claude/settings.json
Stop hook이 무한 실행되는 경우
Claude가 멈추지 않고 무한 루프로 계속 작업합니다.
Stop hook 스크립트는 이미 연속 실행을 트리거했는지 확인해야 합니다. JSON 입력에서 stop_hook_active 필드를 파싱하고 true이면 조기에 종료합니다:
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # Allow Claude to stop
fi
# ... rest of your hook logic
JSON 검증 실패
hook 스크립트가 유효한 JSON을 출력하는데도 Claude Code가 JSON 파싱 오류를 표시합니다.
Claude Code가 hook을 실행할 때 프로필(~/.zshrc 또는 ~/.bashrc)을 소스하는 셸을 생성합니다. 프로필에 조건 없는 echo 문이 포함되어 있으면, 해당 출력이 hook의 JSON 앞에 추가됩니다:
Shell ready on arm64
{"decision": "block", "reason": "Not allowed"}
Claude Code가 이를 JSON으로 파싱하려 하면 실패합니다. 이를 수정하려면 셸 프로필의 echo 문을 대화형 셸에서만 실행되도록 감쌉니다:
# In ~/.zshrc or ~/.bashrc
if [[ $- == *i* ]]; then
echo "Shell ready"
fi
$- 변수는 셸 플래그를 포함하며, i는 대화형을 의미합니다. Hooks는 비대화형 셸에서 실행되므로 echo가 건너뛰어집니다.
디버그 기법
Ctrl+O로 상세 모드를 토글하여 트랜스크립트에서 hook 출력을 확인하거나, claude --debug를 실행하여 어떤 hooks가 매칭되었고 종료 코드가 무엇인지 등 전체 실행 세부 사항을 확인합니다.
더 알아보기
- Hooks 레퍼런스: 전체 이벤트 스키마, JSON 출력 형식, 비동기 hooks, MCP 도구 hooks
- 보안 고려 사항: 공유 환경이나 프로덕션 환경에 hooks를 배포하기 전에 검토
- Bash 명령 검증기 예제: 완전한 참조 구현