Claude Code hook은 도구 실행 직전, 권한이 필요할 때, Claude가 응답을 마쳤을 때처럼 세션의 정해진 시점에 Claude Code가 자동으로 실행하는 명령(또는 HTTP 호출, prompt 등 몇 가지 다른 핸들러 유형)이에요. ~/.claude/settings.json 같은 설정 파일의 hooks 키 아래에 정의해요. 각 hook은 이벤트에 대한 JSON을 stdin으로 받고, 종료 코드나 stdout의 JSON 객체로 응답할 수 있어요. 이를 통해 로그를 남기거나, 알림을 보내거나, 동작을 차단하거나, 사용자 대신 응답할 수 있어요.
01hook의 구성
hook 설정은 세 단계로 이루어져요:
- hook 이벤트:
PreToolUse나Stop등이 있어요. - matcher 그룹: "Bash 도구일 때만"처럼 언제 실행될지 걸러내요.
- 하나 이상의 hook 핸들러: 실제로 실행되는 부분이에요.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "/path/to/lint-check.sh"
}
]
}
]
}
}
이 예시는 Claude가 파일을 작성하거나 수정한 뒤 lint 스크립트를 실행해요. 이벤트를 하나 더 추가하려면 같은 hooks 객체 안에 형제 키를 추가하면 돼요.
02hook을 두는 위치
~/.claude/settings.json모든 프로젝트아니요, 내 컴퓨터에서만.claude/settings.json한 프로젝트예, 커밋할 수 있어요.claude/settings.local.json한 프로젝트아니요hooks/hooks.json플러그인이 활성화된 동안예여러 파일의 hook은 서로 덮어쓰지 않고 합쳐져요. Claude Code에서 /hooks를 입력하면 활성화된 모든 hook과 각각의 출처를 확인할 수 있어요.
대화형 세션에서는 해당 폴더의 workspace trust 대화상자를 수락하기 전까지 Claude Code가 hook을 실행하지 않아요. 직접 만든 hook도 마찬가지예요.
03핸들러 유형
대부분의 hook은 셸 명령을 실행하는 "type": "command"예요. 그 밖에 네 가지 유형이 있어요:
http: 이벤트 JSON을 URL로 POST하고 응답 본문을 읽어요.mcp_tool: 설정해 둔 MCP 서버의 도구를 호출해요.prompt: Claude 모델에 단일 턴 질문을 보내고 JSON 형식의 결정을 읽어요.agent: 결정 전에 파일을 읽을 수 있는 subagent를 실행해요(실험적 기능).
command hook은 사용자의 전체 권한으로 실행돼요. macOS와 Linux에서는 sh -c로, Windows에서는 Git Bash(또는 PowerShell)로 실행돼요. "async": true를 추가하면 백그라운드에서 실행되지만, 그 대신 결정을 내릴 수는 없어요.
04Matcher
matcher는 이벤트 중 어떤 경우에 hook을 실행할지 정해요. 무엇과 비교할지는 이벤트마다 달라요. 도구 이벤트는 도구 이름, Notification은 알림 유형, SessionStart는 세션 시작 방식과 비교하는 식이에요.
"*",""를 쓰거나 matcher를 아예 생략하면 모든 경우에 일치해요.- 문자, 숫자,
_,-, 공백,|,,로만 이루어진 값은 정확한 이름으로 취급돼요.Edit|Write는 두 도구 중 하나와 일치해요. - 그 외의 값은 JavaScript 정규식으로 처리되므로,
mcp__memory__.*는 한 MCP 서버의 모든 도구와 일치해요.
matcher는 대소문자를 구분해요. Stop, UserPromptSubmit 등 일부 이벤트는 matcher를 무시해요. 도구 이벤트에서는 "Bash(git *)" 같은 권한 규칙을 담은 if 필드로 개별 핸들러의 범위를 더 좁힐 수 있어요.
05입력: hook이 받는 데이터
모든 hook은 stdin으로 JSON을 받아요. 여기에는 session_id, transcript_path, cwd, hook_event_name 같은 공통 필드와 이벤트별 필드가 들어 있어요. 예를 들어 Bash 호출에 대한 PreToolUse hook은 tool_name과 tool_input.command도 함께 받아요. jq나 다른 JSON 파서로 읽으면 돼요:
#!/bin/bash
input=$(cat)
cmd=$(jq -r '.tool_input.command // empty' <<<"$input")
echo "Claude wants to run: $cmd" >> ~/claude-commands.log
exit 0
06출력: 종료 코드와 JSON
hook이 결과를 돌려주는 방법은 두 가지예요.
종료 코드. 0은 성공이에요. 2는 차단 오류로, 차단이 가능한 이벤트에서는 동작이 중단되고 stderr 내용이 그 사유가 돼요. 흔히 쓰는 1을 포함한 그 밖의 코드는 모두 비차단 오류라서, 동작은 그대로 진행되고 hook 오류 알림만 표시돼요. hook으로 규칙을 강제하려면 1이 아니라 2로 종료하세요.
stdout으로 JSON 출력. 더 세밀하게 제어하려면 0으로 종료하면서 JSON 객체 하나를 출력하세요. Claude를 완전히 멈추는 continue: false처럼 모든 이벤트에서 동작하는 필드도 있고, 이벤트별로 다른 필드도 있어요. Stop은 reason과 함께 decision: "block"을 쓰고, PreToolUse와 PermissionRequest는 결정을 hookSpecificOutput 안에 넣어요. stdout에는 다른 내용을 출력하지 마세요. 셸 프로필에서 나온 엉뚱한 한 줄 때문에 Claude Code가 JSON을 읽지 못하게 돼요.
07전체 hook 이벤트
SessionStart세션이 시작되거나 재개될 때아니요 (컨텍스트 추가)Setup--init-only로 시작하거나, -p와 함께 --init / --maintenance로 시작할 때아니요InstructionsLoadedCLAUDE.md 또는 .claude/rules/*.md 파일이 로드될 때아니요UserPromptSubmitprompt를 제출한 직후, Claude가 보기 전예UserPromptExpansion입력한 명령이 prompt로 확장될 때예PreToolUse모든 도구 호출 전예PermissionRequest도구 호출에 권한 결정이 필요할 때JSON으로 허용 또는 거부PermissionDeniedAuto 모드가 도구 호출을 거부할 때아니요PostToolUse도구 호출이 성공한 후아니요 (도구가 이미 실행됨)PostToolUseFailure도구 호출이 실패한 후아니요PostToolBatch병렬 도구 호출 묶음이 끝난 후, 다음 모델 호출 전예NotificationClaude Code가 알림을 보낼 때아니요MessageDisplayClaude의 응답 텍스트가 표시되는 동안아니요 (표시 내용 변경 가능)SubagentStart / SubagentStopsubagent가 시작되거나 종료될 때SubagentStop만 가능TaskCreated / TaskCompleted작업이 생성되거나 완료로 표시될 때예StopClaude가 응답을 마칠 때예 (Claude가 계속 진행)StopFailure턴이 API 오류로 끝날 때아니요TeammateIdle에이전트 팀의 팀원이 유휴 상태가 되기 직전예ConfigChange세션 중 설정 파일이 변경될 때예CwdChanged작업 디렉터리가 변경될 때아니요DirectoryAdded/add-dir로 디렉터리가 추가될 때아니요FileChanged감시 중인 파일이 디스크에서 변경될 때아니요WorktreeCreate / WorktreeRemoveworktree가 생성되거나 제거될 때예PreCompact / PostCompact컨텍스트 압축 전후PreCompact만PreModelSwitch / PostModelSwitch모델이 바뀌기 전과 후PreModelSwitch만Elicitation / ElicitationResultMCP 서버가 입력을 요청할 때와 답한 후예SessionEnd세션이 끝날 때아니요Anthropic은 이벤트를 수시로 추가하므로, 최신 목록과 각 이벤트의 입력 필드는 hooks 레퍼런스에서 확인하세요.
08알림에 중요한 이벤트
알림받고 싶은 상황은 대부분 다음 네 가지 이벤트로 해결돼요.
- "Claude가 작업을 마쳤어요"에는
Stop. Stop hook 가이드를 참고하세요. - "Claude가 기다리고 있어요"에는
Notification.permission_prompt,idle_prompt같은 타입이 있어요. Notification hook 가이드를 참고하세요. - 권한이 필요한 순간 바로 알림을 받거나 응답하려면
PermissionRequest. PermissionRequest 가이드를 참고하세요. - Claude의 객관식 질문을 보거나 답하려면
AskUserQuestion도구에 대한PreToolUse. 휴대폰에서 질문에 답하기를 참고하세요.
바로 붙여넣어 쓸 수 있는 데스크톱 설정이 필요하다면 Claude Code가 끝났을 때 알림 받는 방법부터 시작하세요.
09Claude Code Notifier의 역할
Claude Code Notifier는 전적으로 이 hook들 위에 만들어졌어요. 무료 커맨드라인 도구인 ccn은 Mac, Windows, Linux에서 동작해요. 설치하면 ~/.claude/settings.json에 각각 ccn hook을 실행하는 hook 항목 7개가 추가되고, 파일 백업도 한 번 만들어 둬요. ccn은 이벤트마다 몇 밀리초만 실행되고 종료되므로 계속 떠 있는 프로세스가 없어요.
단독으로는 데스크톱 알림을 보여 줘요. iPhone이나 Android 앱을 페어링하면 같은 hook으로 응답도 돌려보낼 수 있어요. 권한 허용이나 거부, 질문 답변, Claude가 멈춘 뒤 다음 지시 보내기까지요. Anthropic이 만든 것이 아닌 독립 도구예요. 로그나 lint 단계만 필요하다면 직접 몇 줄 작성하는 편이 더 간단해요. 자세한 내용은 ccn 페이지에 있어요.
10FAQ
Claude를 기다리지 않고 hook을 테스트하려면 어떻게 하나요?
샘플 JSON을 스크립트에 파이프로 넘기고 종료 코드를 확인하세요: echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh; echo $?. 그다음 Claude Code에서 /hooks를 실행해 올바른 이벤트에 등록됐는지 확인하세요.
hook이 실행되지 않는 이유는 뭔가요?
흔한 원인은 settings.json의 잘못된 JSON(끝에 붙은 쉼표, 주석), 정확히 일치하지 않는 matcher(대소문자를 구분해요), 실행 권한이 없는 스크립트, 아직 신뢰하지 않은 폴더예요. claude --debug로 Claude Code를 시작한 뒤 디버그 로그에서 무엇이 실행됐는지 확인하세요. 전체 목록은 문제 해결 체크리스트에 있어요.
모든 hook을 잠시 끄려면 어떻게 하나요?
설정 파일에 "disableAllHooks": true를 넣으세요. 한 번만 건너뛰려면 --settings '{"disableAllHooks": true}'로 Claude Code를 시작하세요. hook 하나만 끄는 스위치는 없으니 해당 항목을 지워야 해요. 두 방법 모두 조직이 managed settings로 설정한 hook은 끌 수 없어요.
hook의 기본 timeout은 얼마인가요?
command, HTTP, MCP 도구 hook은 10분(600초), prompt hook은 30초, agent hook은 60초예요. 일부 이벤트는 더 짧은 기본값을 쓰고, 어떤 핸들러든 timeout을 초 단위로 지정할 수 있어요.