자동화 워크플로우를 구축하는 이벤트 시스템
Hooks는 Claude Code의 생명주기 내 특정 지점에서 자동으로 실행되는 사용자 정의 셸 명령, HTTP 엔드포인트, 또는 LLM 프롬프트입니다. 예시와 함께하는 빠른 시작 가이드를 보려면 Automate actions with hooks를 참조하세요. 이 레퍼런스를 사용하여 이벤트 스키마, 설정 옵션, JSON 입력/출력 형식, 비동기 Hook, HTTP Hook, MCP 도구 Hook과 같은 고급 기능을 확인할 수 있습니다. Hooks를 처음 설정하는 경우, 가이드부터 시작하는 것이 좋습니다.
Hooks를 쉽게 이해하기 위해 다음과 같이 생각할 수 있습니다:
Hooks는 **"자동 반응 규칙"**입니다. 스마트폰의 자동화 앱(예: IFTTT)과 비슷합니다.
예를 들면:
**"어떤 일이 일어나면 → 자동으로 이것을 해라"**라는 규칙을 설정하는 것이 Hooks입니다.
Hooks는 Claude Code 세션의 특정 지점에서 실행됩니다. 이벤트가 발생하고 매처가 일치하면, Claude Code는 Hook 핸들러에 이벤트에 대한 JSON 컨텍스트를 전달합니다.
핸들러는 입력을 검사하고, 조치를 취하며, 선택적으로 의사결정(decision)을 반환할 수 있습니다.
이벤트는 3가지 주기로 발생합니다:
아래 표는 각 이벤트가 언제 발생하는지 요약합니다. Hook 이벤트 섹션은 각 이벤트에 대한 전체 입력 스키마 및 의사결정 제어 옵션을 문서화합니다.
Hook이 어떻게 동작하는지 이해하기 위해, 위험한 셸 명령을 차단하는 PreToolUse Hook을 예시로 살펴보겠습니다. 매처는 Bash 도구 호출로 좁혀지고, if 조건은 rm *와 일치하는 Bash 서브커맨드로 더 좁혀지므로, block-rm.sh는 두 필터가 모두 일치할 때만 실행됩니다:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"args": []
}
]
}
]
}
}스크립트는 stdin에서 JSON 입력을 읽고, 명령어를 추출한 후 rm -rf를 포함하면 permissionDecision을 "deny"로 반환합니다:
#!/bin/bash
# .claude/hooks/block-rm.sh
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 # no decision; normal permission flow applies
fi이제 Claude Code가 Bash "rm -rf /tmp/build"를 실행하려고 한다고 가정해 봅시다. 다음 상황이 발생합니다:
{
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf /tmp/build"
},
"session_id": "abc123",
"cwd": "/home/user/project",
"hook_event_name": "PreToolUse"
}"*"를 사용하면 이벤트 발생 시마다 그룹이 활성화됩니다.if 조건 "Bash(rm *)"가 일치합니다. rm -rf /tmp/build는 rm * 패턴과 일치하는 서브커맨드이기 때문입니다. 따라서 이 핸들러가 실행됩니다. 만약 명령어가 npm test였다면 if 검사는 실패하고 block-rm.sh는 실행되지 않아 프로세스 생성 오버헤드를 피할 수 있습니다. if 필드는 선택 사항이며, 없으면 일치하는 그룹의 모든 핸들러가 실행됩니다.rm -rf를 발견하면 stdout으로 결정을 출력합니다.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook"
}
}rm file.txt와 같이 더 안전한 rm 변형이었다면, 스크립트는 대신 exit 0을 실행했을 것입니다. 출력이 없는 종료 코드 0은 Hook이 보고할 결정이 없음을 의미하므로, 도구 호출은 정상적인 권한 흐름을 통해 계속됩니다. Hook은 호출을 거부할 수 있지만, 침묵한다고 해서 승인하는 것은 아닙니다.Claude Code는 다음과 같은 Hook 이벤트를 지원합니다:
| 이벤트 | 언제 발생하는지 |
|---|---|
| SessionStart | 세션이 시작되거나 재개될 때 |
| Setup | --init-only 플래그 또는 -p 모드에서 --init이나 --maintenance 플래그로 Claude Code를 시작할 때. CI 또는 스크립트에서 일회성 준비를 위해 사용합니다 |
| UserPromptSubmit | 사용자가 프롬프트를 제출하면, Claude가 처리하기 전 |
| UserPromptExpansion | 사용자가 입력한 명령이 프롬프트로 확장되려고 할 때. 확장을 차단할 수 있습니다. |
| PreToolUse | 도구 호출이 실행되기 전입니다. 도구 호출을 차단할 수 있습니다. |
| PermissionRequest | 권한 대화가 나타날 때 |
| PermissionDenied | 도구 호출이 자동 모드 분류기에 의해 거부될 때. 모델이 거부된 도구 호출을 재시도할 수 있다는 것을 알리려면 {retry: true}를 반환하세요. |
| PostToolUse | 도구 호출이 성공한 후 |
| PostToolUseFailure | 도구 호출이 실패한 후 |
| PostToolBatch | 병렬 도구 호출의 전체 배치가 해결된 후, 다음 모델 호출 이전 |
| Notification | Claude Code가 알림을 보낼 때 |
| MessageDisplay | 어시스턴트 메시지 텍스트가 표시되는 동안 |
| SubagentStart | 서브에이전트가 생성될 때 |
| SubagentStop | 서브에이전트가 종료될 때 |
| TaskCreated | TaskCreate를 통해 작업이 생성될 때 |
| TaskCompleted | 작업이 완료로 표시될 때 |
| Stop | Claude가 응답을 마칠 때 |
| StopFailure | 턴이 API 오류로 인해 종료될 때. 출력 및 종료 코드는 무시됩니다. |
| TeammateIdle | 에이전트 팀의 팀메이트가 유휴 상태가 될 때 |
| InstructionsLoaded | CLAUDE.md 또는 .claude/rules/*.md 파일이 컨텍스트에 로드될 때 (세션 시작 및 지연 로드 시) |
| ConfigChange | 세션 중 설정 파일이 변경될 때 |
| CwdChanged | 작업 디렉토리가 변경될 때 (예: Claude가 cd 명령을 실행할 때). direnv 같은 도구를 사용한 반응형 환경 관리에 유용합니다. |
| FileChanged | 감시 파일이 디스크에서 변경될 때. matcher 필드는 어떤 파일명을 감시할지 지정합니다. |
| WorktreeCreate | --worktree 또는 isolation: "worktree"를 통해 워크트리가 생성될 때. 기본 git 동작을 대체합니다. |
| WorktreeRemove | 워크트리가 제거될 때 (세션 종료 또는 서브에이전트 종료 시) |
| PreCompact | 컨텍스트 압축 직전 |
| PostCompact | 컨텍스트 압축 완료 후 |
| Elicitation | MCP 서버가 도구 실행 중 사용자 입력을 요청할 때 |
| ElicitationResult | 사용자가 MCP elicitation에 응답한 후 서버로 응답을 보내기 전 |
| SessionEnd | 세션이 종료될 때 |
처음에는 아래 3가지만 알면 충분합니다:
나머지는 필요할 때 하나씩 배우면 됩니다.
각 Hook 이벤트에는 5가지 타입의 핸들러를 연결할 수 있습니다.
핸들러는 "이벤트가 발생했을 때 실제로 무엇을 할지"를 정하는 것입니다.
prettier로 코드 정리) → 가장 많이 사용처음에는 Command 핸들러만 사용해도 충분합니다.
셸 명령을 실행하는 가장 일반적인 핸들러입니다. JSON을 stdin으로 받고, stdout으로 JSON을 출력합니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write $CLAUDE_FILE_PATH"
}
]
}
]
}
}외부 HTTP 엔드포인트로 요청을 보냅니다. 원격 서버나 웹훅 서비스와 통합할 때 사용합니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "http",
"url": "https://example.com/hooks/post-tool",
"method": "POST"
}
]
}
]
}
}MCP (Model Context Protocol) 서버의 도구를 호출합니다. 외부 도구와 통합하여 Hook에서 복잡한 작업을 수행할 때 사용합니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "mcp_tool",
"tool_name": "external_service",
"arguments": {
"action": "validate"
}
}
]
}
]
}
}단일 LLM 호출을 수행합니다. yes/no 판단이나 간단한 분석에 적합합니다.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "방금 완료한 작업이 원래 요청을 충분히 만족하는지 검토하세요. 누락된 부분이 있다면 'no'를, 완료되었다면 'yes'를 응답하세요."
}
]
}
]
}
}Prompt 핸들러가 "no"를 반환하면 Claude가 추가 작업을 수행합니다.
서브에이전트를 실행하여 복잡한 검증을 수행합니다. 최대 50턴, 60초 타임아웃이 적용됩니다.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "이전 작업의 결과를 검토하고, 모든 요구사항이 충족되었는지 확인하세요. 필요한 경우 추가 작업을 수행하세요."
}
]
}
]
}
}