Claude Hook 은 Claude Code의 특정 lifecycle 지점에서 자동 실행되는 사용자 정의 자동화입니다.
쉽게 말하면, Claude가 작업하는 중간중간에 “반드시 실행되어야 하는 검사나 후처리”를 끼워 넣는 장치입니다.
Claude에게 프롬프트로 “위험한 명령은 실행하지 마”, “수정 후 lint를 돌려”, “작업 끝나기 전에 테스트해”라고 지시할 수도 있지만, 프롬프트 지시는 세션이 길어지거나 컨텍스트가 바뀌면 누락될 수 있습니다.
반면 Hook은 Claude의 판단에 맡기는 것이 아니라, 미리 정해진 lifecycle 지점에서 결정론적으로 실행됩니다.
따라서 Hooks는 다음과 같은 상황에 적합합니다.
- 위험한 명령 실행 전 차단
- 파일 수정 후 formatter, lint, typecheck 자동 실행
- Claude가 멈추기 전에 테스트 결과나 체크리스트 확인
- 사용자 프롬프트 제출 시 프로젝트 상태, 티켓 정보, 규칙 자동 주입
- 알림, 로그, 감사 기록, 외부 시스템 연동
반대로 “Claude가 참고하면 좋은 설명”이나 “작업 방식에 대한 가이드”는 Hook보다 CLAUDE.md, Skill, Agent, Memory에 두는 편이 더 적합합니다. Hook은 참고 문서가 아니라 자동 실행 규칙에 가깝습니다.
Claude Hook 핵심 개념 #
Hook은 크게 세 가지 질문으로 구성됩니다.
- 언제 실행할 것인가? →
event - 어떤 상황에서만 실행할 것인가? →
matcher, 필요하면if - 무엇을 실행할 것인가? →
handler
공식 문서에서도 Hook 설정은 event → matcher group → hook handler의 3단계 구조로 설명합니다.
구조 #
Hooks 설정은 세 단계로 읽습니다.
1. event #
event는 Hook이 실행되는 시점입니다.
대표적인 이벤트는 다음과 같습니다.
| event | 실행 시점 | 주 용도 |
|---|---|---|
SessionStart | 세션 시작 또는 재개 시 | 프로젝트 상태, 환경 정보 주입 |
UserPromptSubmit | 사용자가 프롬프트를 제출한 직후, Claude가 처리하기 전 | 프롬프트 검사, 컨텍스트 자동 주입 |
PreToolUse | Claude가 도구를 실행하기 직전 | 위험 명령 차단, 파일 접근 제한 |
PermissionRequest | 권한 승인 창이 뜨기 직전 | 특정 안전 작업 자동 승인 |
PostToolUse | 도구 실행이 성공한 직후 | formatter, lint, 로그 기록 |
PostToolUseFailure | 도구 실행이 실패한 직후 | 실패 로그 수집, 재시도 힌트 제공 |
Notification | Claude Code 알림 발생 시 | 데스크톱 알림, Slack/Discord 알림 |
Stop | Claude가 응답을 끝내려는 시점 | 테스트 미완료, 체크리스트 누락 시 계속 작업 요구 |
SubagentStart / SubagentStop | Subagent 시작/종료 시 | 서브 에이전트별 검사 |
PreCompact / PostCompact | 컨텍스트 압축 전후 | 압축 전 요약 저장, 압축 후 컨텍스트 재주입 |
SessionEnd | 세션 종료 시 | 로그 정리, 작업 요약 저장 |
Claude Code는 현재 더 많은 이벤트를 지원하지만, 처음에는 PreToolUse, PostToolUse, Stop, Notification, UserPromptSubmit 정도부터 이해하면 충분합니다.
2. matcher group #
matcher는 해당 이벤트 중에서도 어떤 도구나 상황에 반응할지 좁히는 필터입니다.
예를 들어: "matcher": "Bash"는 Bash 도구에만 반응합니다.
"matcher": "Edit|Write" 는 Edit 또는 Write 도구에만 반응합니다.
"matcher": "mcp__.*" 는 MCP 도구 전체에 반응하는 정규식 패턴으로 사용할 수 있습니다.
공식 문서 기준으로 matcher는 값의 형태에 따라 exact match 또는 JavaScript 정규식처럼 동작합니다.
Bash, Edit|Write처럼 단순한 값은 정확히 일치하는 도구명을 대상으로 하고, mcp__.*처럼 특수 문자가 들어가면 정규식으로 해석됩니다.
matcher가 도구 이름 수준의 큰 필터라면, if는 더 세부적인 조건입니다.
예를 들어 Bash 도구 중에서도 rm 명령이 포함된 경우에만 Hook을 실행하고 싶다면 다음처럼 작성할 수 있습니다.
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
}
이렇게 하면 모든 Bash 명령마다 스크립트를 실행하지 않고, 조건에 맞는 경우에만 handler가 실행되므로 불필요한 오버헤드를 줄일 수 있습니다.
공식 문서에서도 if 조건이 맞지 않으면 handler 자체를 실행하지 않는다고 설명합니다.
3. handler #
handler는 실제로 실행되는 작업입니다.
Claude Code Hooks는 다음과 같은 handler 타입을 지원합니다.
| type | 설명 | 예시 |
|---|---|---|
command | 로컬 shell command 실행 | shell script, npm script, Python script |
http | HTTP endpoint 호출 | 사내 API, Slack webhook, audit server |
mcp_tool | 연결된 MCP 서버의 tool 호출 | memory 저장, ticket 조회, DB 검사 |
prompt | 단발성 LLM 평가 | “작업 완료 여부를 검토하라” |
agent | 도구 사용이 가능한 agent 실행 | 보안 리뷰, 테스트 검증, 복잡한 사후 검사 |
대부분의 실무 Hook은 command로 시작하면 됩니다.
prompt나 agent는 판단이 필요한 검사에는 유용하지만, 실행 비용과 시간이 더 들 수 있으므로 자주 실행되는 이벤트에는 신중하게 사용하는 것이 좋습니다.
http handler나 mcp_tool hook은 에이전트의 장기 메모리 구축에 잘 활용 될 수 있을 것 같습니다.
현재 공식 문서에서 agent hook은 실험적일 수 있다고 안내합니다.
Cladue Hook 예시 #
아래 예시는 Claude가 Bash 도구로 rm 계열 명령을 실행하려 할 때, 사전에 검사 스크립트를 실행하는 Hook입니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
}
]
}
]
}
}
이 설정은 다음 순서로 동작합니다.
- Claude가 Bash 도구를 사용하려고 함
PreToolUse이벤트 발생matcher: "Bash"에 일치if: "Bash(rm *)"조건 검사- 조건이 맞으면
block-rm.sh실행 - 스크립트 결과에 따라 Bash 실행 허용 또는 차단
Claude Hook 실행 스크립트 예시 #
.claude/hooks/block-rm.sh
#!/usr/bin/env bash
INPUT="$(cat)"
COMMAND="$(echo "$INPUT" | jq -r '.tool_input.command')"
if echo "$COMMAND" | grep -Eq 'rm\\s+-rf|rm\\s+-fr'; then
echo "Blocked: destructive rm command is not allowed." >&2
exit 2
fi
exit 0
Hook 스크립트는 Claude Code로부터 JSON 입력을 stdin으로 받습니다.
Bash 도구의 경우 tool_input.command 안에 Claude가 실행하려던 shell command가 들어 있습니다.
중요한 점은 exit code입니다.
| exit code | 의미 |
|---|---|
0 | 문제 없음. 단, PreToolUse에서는 이것만으로 자동 승인되는 것은 아니며 일반 permission flow가 계속 적용됨 |
2 | 차단. Claude에게 stderr 내용이 피드백으로 전달됨 |
| 그 외 | Hook 오류로 간주될 수 있으나, 대부분의 경우 작업은 계속 진행됨 |
공식 문서에 따르면 exit 0은 “반대 의견 없음”에 가깝고, PreToolUse에서 도구 실행을 자동 승인한다는 뜻은 아닙니다.
반대로 exit 2는 차단 의미로 사용되며, PreToolUse, UserPromptSubmit, Stop 등 차단 가능한 이벤트에서 실제 흐름을 멈출 수 있습니다.
Claude Hook 실행 결과로 할 수 있는 일 #
Hook은 단순히 스크립트를 실행하는 것에서 끝나지 않습니다.
실행 결과를 통해 Claude Code의 흐름에 영향을 줄 수 있습니다.
여러 형태로 활용할 수 있지만 대표적으로 다음이 가능합니다.
| 동작 | 설명 |
|---|---|
| 허용 | 별도 차단 없이 기존 흐름 계속 |
| 차단 | 위험 명령, 금지 파일 수정, 미완료 작업 등을 막음 |
| 피드백 제공 | Claude에게 실패 사유나 수정 방향 전달 |
| 컨텍스트 주입 | Claude가 다음 판단에 참고할 정보를 추가 |
| 알림 전송 | 사용자 또는 외부 시스템에 상태 전달 |
| 로그 기록 | 명령, 파일 변경, 실패 이력 저장 |
예를 들어 UserPromptSubmit이나 SessionStart에서는 stdout에 쓴 내용이 Claude의 컨텍스트로 들어갈 수 있습니다.
반면 PostToolUse는 이미 도구 실행이 끝난 뒤이므로, 해당 도구 실행 자체를 되돌릴 수는 없습니다. 이 차이를 이해하는 것이 중요합니다.
여러 Hook이 동시에 걸리는 경우 #
같은 이벤트에 여러 Hook이 매칭될 수 있습니다.
예를 들어 Bash 실행 전에 다음 두 Hook이 동시에 걸릴 수 있습니다.
- Bash 명령 로그 기록 Hook
- 위험 명령 차단 Hook
이 경우 Claude Code는 매칭된 Hook들을 실행한 뒤 결과를 합칩니다.
중요한 점은, 하나의 Hook이 차단 결정을 내려도 다른 Hook 실행이 자동으로 취소되는 것은 아니라는 점입니다.
따라서 “차단 Hook이 먼저 실행될 테니 뒤의 Hook은 실행되지 않겠지”라고 가정하면 안 됩니다.
공식 문서에 따르면 여러 Hook이 같은 이벤트에 매칭되면 각각 실행되고, PreToolUse의 permission decision에서는 더 제한적인 결정이 우선합니다.
즉 deny가 ask보다, ask가 allow보다 우선합니다.
추천 파일 구조 #
.claude/
├── settings.json # 팀 공유 hook 설정
├── settings.local.json # 개인 로컬 hook 설정, gitignore 권장
├── hooks/
│ ├── block-rm.sh
│ ├── lint-after-edit.sh
│ └── ensure-tests.sh
├── skills/
└── agents/
settings.json에는 팀 전체가 공유해도 되는 안정적인 Hook을 둡니다.
예:
- 위험 명령 차단
- 공통 formatter
- 필수 테스트 검사
- protected file 수정 방지
settings.local.json에는 개인 환경에만 필요한 Hook을 둡니다.
예:
- 개인 알림 설정
- 로컬 경로에 의존하는 스크립트
- 개인용 실험 Hook
- 특정 IDE, 터미널, OS에 의존하는 설정
Claude Hook 파일 위치 #
| 위치 | 범위 | 공유 여부 | 용도 |
|---|---|---|---|
~/.claude/settings.json | 내 모든 프로젝트 | 개인 로컬 | 개인 공통 설정 |
.claude/settings.json | 현재 프로젝트 | git 공유 가능 | 팀 표준 규칙 |
.claude/settings.local.json | 현재 프로젝트의 내 환경 | 공유하지 않음 | 개인 실험, 로컬 전용 |
Hook은 사용자 설정, 프로젝트 설정, 로컬 설정, 관리형 정책, Plugin, Skill/Agent frontmatter 등 여러 위치에서 정의할 수 있습니다.
Skill이나 Agent frontmatter에 정의된 Hook은 해당 컴포넌트가 활성화된 동안만 적용됩니다.
팀 공유 Hook은 반드시 코드 리뷰
Hook은 사용자의 로컬 권한으로 shell command를 실행할 수 있습니다.
따라서 프로젝트에 포함된 Hook 스크립트는 신뢰하기 전 반드시 내용을 확인해야 합니다.
특히 다음 동작이 있는지 확인합니다.
- 알 수 없는 binary 실행
- 외부 서버로 파일 전송
- .env, SSH key, token 읽기
curl | sh형태의 원격 스크립트 실행- 광범위한 파일 삭제
- 운영 DB 또는 배포 시스템 접근
Claude Hook 확인 및 비활성화 #
Claude Code 안에서 /hooks 명령을 사용하면 현재 설정된 Hook을 확인할 수 있습니다.
이 메뉴에서는 Hook이 어떤 이벤트에 걸려 있는지, 어떤 matcher를 쓰는지, 어느 설정 파일에서 왔는지 확인할 수 있습니다. 단, /hooks 메뉴는 읽기 전용이므로 수정은 설정 파일에서 해야 합니다.
Hook을 제거하려면 설정 파일에서 해당 항목을 삭제합니다.
전체 Hook을 임시 비활성화하려면 다음 설정을 사용할 수 있습니다.
{
"disableAllHooks": true
}다만 개별 Hook만 켜고 끄는 기능은 없으므로, 특정 Hook만 임시 비활성화하려면 설정에서 해당 Hook 항목을 제거해야 합니다.
공식 문서도 개별 Hook disable 기능은 없고, 전체 비활성화는 disableAllHooks로 처리한다고 설명합니다.