Claude Code 훅이란? 예제와 사용 시점 (2026)
Claude Code 훅은 settings.json에 설정하는 셸 명령(또는 HTTP/MCP 호출)으로, Claude Code 세션의 특정 순간에 자동으로 실행돼요. 예를 들어 도구가 실행되기 전, 파일이 수정된 뒤, 또는 Claude가 한 턴을 마쳤을 때죠. 초보자에게 필요한 세 가지 핵심 이벤트는 PreToolUse(위험한 명령을 막을 수 있음), PostToolUse(코드 자동 포맷), Stop(「완료」 알림 전송)이에요. 훅은 샌드박스 없이 여러분의 사용자 권한 전체로 실행되니, 켜기 전에 꼼꼼히 확인하세요.
Claude Code는 업데이트가 빨라서 이벤트 목록도 계속 늘어나고 있어요.
Claude Code의 훅이란?
Claude Code의 훅은 미리 정의해 두는 명령으로, 세션 수명주기의 정해진 지점에서 Claude Code가 자동으로 실행하는 것이에요. Claude에게 무언가를 「기억해서」 해 주길 바라며 기대하는 대신, 훅은 그 동작을 결정적(deterministic)인 것으로 바꿔 줘요. 적절한 순간이 오면 모델의 「기분」과 상관없이 반드시 실행되죠.
혹시 Git 훅(커밋할 때마다 린터를 돌리는 pre-commit 같은 것)을 써 본 적이 있다면, 이 개념이 익숙하게 느껴질 거예요. 유일한 차이는 이 훅들이 Git이 아니라 AI 코딩 에이전트의 수명주기에 연결된다는 점뿐이에요. Claude가 도구를 실행하려 할 때, 파일 편집을 막 끝냈을 때, 또는 응답 턴을 마칠 때, Claude Code는 그 이벤트에 등록된 훅이 있는지 확인하고 있으면 실행해요.
훅의 가장 큰 강점은 결정적이며 차단할 수 있다는 점이에요. CLAUDE.md에 「파일을 수정한 뒤에는 Prettier를 실행하라」고 적어도 그건 제안일 뿐이라, 모델이 잊어버릴 수 있어요. 반면 PostToolUse 훅은 예외 없이 매번 Prettier를 실행하죠. PreToolUse를 쓰면 훅이 동작이 일어나기 전에 그것을 거부할 수도 있어요. 예를 들어 위험한 rm -rf를 막는 식이죠.
그래서 훅은 Claude Code 자동화의 핵심 도구가 돼요. 코드 포맷, 테스트 실행, 활동 로깅, 알림 전송, 또는 안전 가드레일 구축 같은 것이죠. 이 글에서는 settings.json을 통한 훅의 작동 방식, 복사해서 바로 쓰는 세 가지 예제, 써야 할(그리고 쓰지 말아야 할) 때, 그리고 대부분의 가이드가 건너뛰는 안전 섹션을 살펴볼게요.
훅은 어떻게 작동하나요? (settings.json)
훅은 settings.json 파일의 hooks 키 아래에 선언해요. 구조는 세 단계로 중첩돼요. 이벤트 이름 -> 매처 목록 -> 실행할 훅 목록이죠. 구체적으로는 hooks > EventName > [{ matcher, hooks: [{ type, command }] }]예요. 실제로 작동하는 최소한의 settings.json은 다음과 같아요.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_FILE_PATHS\""
}
]
}
]
}
}
안쪽에서부터 읽어 보세요. PostToolUse 이벤트가 발생하면, Claude Code는 matcher(Edit|Write)를 방금 실행된 도구의 이름과 비교해요. 일치하면 hooks 배열 안의 각 command를 실행하죠.
세 가지 설정 범위 — 이게 중요한 이유는 훅이 어디에 적용되는지, 그리고 Git에 커밋되는지를 결정하기 때문이에요.
| 파일 | 범위 | Git | 용도 |
|---|---|---|---|
~/.claude/settings.json | 머신 전체(글로벌) | 커밋 안 됨 | 모든 프로젝트에서 쓰고 싶은 개인용 훅 |
.claude/settings.json | 프로젝트별 | 커밋 가능, 팀과 공유 | 공유하는 프로젝트 훅(포맷, 테스트) |
.claude/settings.local.json | 프로젝트별, 나만 | Git 무시(기본값) | 비공개 토큰이나 경로가 든 민감한 훅 |
플러그인도 자체 hooks/hooks.json 파일을 통해 고유한 훅을 제공할 수 있어요.
type 필드는 다섯 가지를 지원해요. command(셸 명령 실행 — 단연 가장 흔함), http(URL 호출), mcp_tool(MCP 서버의 도구 호출 — MCP란 무엇이고 어떻게 쓰는지도 참고), prompt, agent예요. 이 글 전체에서는 command에 집중할게요. 복사해서 바로 쓸 수 있고, 필요한 것의 90%를 커버하니까요. (settings.json의 훅 문법을 제대로 쓰는 게 전제예요. JSON이 잘못되면 훅은 아무 말 없이 실행되지 않아요.)
주요 이벤트 유형(수명주기)
전부 외울 필요는 없어요. 시작 단계에서는 아래 몇 가지만으로도 대부분의 상황을 커버할 수 있어요.
| 이벤트 | 발생 시점 | 차단 가능? | 대표적인 용도 |
|---|---|---|---|
PreToolUse | Claude가 도구를 실행하기 전 | 예 | 위험한 명령 차단, 확인 강제 |
PostToolUse | 도구가 성공한 후 | 아니요 | 코드 포맷, 테스트 실행, 로깅 |
UserPromptSubmit | 프롬프트를 제출할 때 | 예 | 컨텍스트 주입, 입력 검증 |
Stop | Claude가 턴을 마칠 때 | 아니요 | 「완료」 알림 전송 |
SessionStart | 새 세션이 열릴 때 | 아니요 | 환경 변수 로드, 로깅 |
Notification | Claude가 알림을 낼 때 | 아니요 | 알림을 다른 채널로 라우팅 |
2026년 업데이트(정보 추가): 오래된 가이드 상당수는 여전히 고전적인 네 가지 이벤트만 나열해요. 실제로 Claude Code에는 이제 30개가 넘는 수명주기 이벤트가 있고, PostToolUseFailure, SubagentStart/SubagentStop, PreCompact/PostCompact, SessionEnd 등이 추가됐어요(Anthropic 공식 문서(code.claude.com/docs/en/hooks, 2026-08-09 접속) 기준). 하지만 걱정 마세요 — 초보자에게 필요한 건 위의 3~4개 핵심 이벤트뿐이고, 나머지는 고급 시나리오용이에요.
매처는 훅이 어떤 도구에 적용되는지를 결정해요. 흔히 쓰는 형태가 네 가지예요. 단일 도구에 정확히 일치("Bash"), 파이프로 여러 도구에 일치("Edit|Write"), 정규식(모든 MCP 도구를 잡는 "mcp__.*"), 그리고 비어 있거나 "*"로 모든 것에 일치예요. 아래 예제에서 PreToolUse와 PostToolUse를 비교해 볼게요.
예제 1 — PreToolUse: 위험한 명령 차단하기
이것은 가장 인상적인 사용 사례이자 가장 실용적인 안전 가드레일이기도 해요. 아이디어는 이래요. Claude가 어떤 Bash 명령을 실행하기 전에, 훅이 그 명령을 검사하고, rm -rf 같은 위험한 패턴을 발견하면 훅이 그것을 거부해서 명령을 절대 실행하지 못하게 해요.
PreToolUse에는 차단하는 방법이 두 가지 있어요. 「깔끔한」 방법은 permissionDecision을 세 값 중 하나로 설정한 JSON을 출력하는 거예요. "allow"(실행하고 확인 단계를 건너뜀), "deny"(완전히 차단), "ask"(확인 강제)죠. 빠르고 간단한 방법은 종료 코드를 쓰는 거예요. 스크립트가 exit 2로 종료하면 차단되고, 이때 stderr에 쓴 내용이 Claude에게 전달돼서 왜 차단됐는지 알 수 있어요. 설정은 다음과 같아요.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/block-danger.sh"
}
]
}
]
}
}
그리고 스크립트 .claude/hooks/block-danger.sh는 이래요.
#!/usr/bin/env bash
# Read the JSON payload from stdin, pull out the command Claude wants to run
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')
if echo "$command" | grep -Eq 'rm[[:space:]]+-rf|git[[:space:]]+push[[:space:]]+--force'; then
echo "Blocked: command matches a dangerous pattern ($command)" >&2
exit 2 # exit 2 = block, stderr is sent back to Claude
fi
exit 0 # exit 0 = allow it to continue
결과는 이래요. Claude가 rm -rf build/를 실행하려 하면, 훅이 그것을 잡아서 exit 2를 반환하고, 명령은 실행되지 않으며, Claude는 이유를 설명하는 메시지를 받아요. 이건 바로 CLAUDE.md 규칙으로는 보장할 수 없는 것이에요. 규칙은 부드러운 제안일 뿐이지만, 훅은 단단한 가드레일이죠. 더 높은 수준에서 권한을 조이고 싶다면 Claude Code의 권한과 안전한 설정에서 더 읽어 보세요.
예제 2 — PostToolUse: 코드 자동 포맷
제가 모든 프로젝트에서 가장 먼저 켜는 훅 중 하나예요. Claude가 파일을 편집할 때마다 자동으로 포매터를 실행하죠. 빠진 공백이나 잘못된 줄바꿈으로 인한 지저분한 diff와는 이제 안녕이에요. 이건 PostToolUse(도구가 성공한 뒤에 실행됨)라서 아무것도 차단하지 않고, 뒷정리만 해요.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "cd \"${CLAUDE_PROJECT_DIR}\" && npx prettier --write \"$CLAUDE_FILE_PATHS\""
}
]
}
]
}
}
매처 Edit|Write는 파일을 편집하는 두 도구를 모두 잡아요. Python 프로젝트라면 명령을 black "$CLAUDE_FILE_PATHS"나 ruff format으로 바꾸세요. Claude Code가 훅에 넘겨주는 유용한 환경 변수 몇 가지예요.
${CLAUDE_PROJECT_DIR}— 프로젝트 루트의 절대 경로예요. 명령이 올바른 디렉터리에서 실행되도록 해 주죠.$CLAUDE_FILE_PATHS— 방금 건드린 파일의 경로예요. 저장소 전체가 아니라 올바른 파일만 포맷할 수 있어요.- (예제 1처럼)
stdin에서 전체 JSON 페이로드를 읽어tool_input세부 정보를 얻는 것도 언제든 가능해요.
팁: 명령은 가볍고 빠르게 유지하세요. PostToolUse 훅은 모든 파일 편집 뒤에 실행되므로, 느린 포매터는 세션 전체를 느리게 만들어요.
예제 3 — Stop: Claude가 끝나면 알림 보내기
Claude에게 긴 작업을 맡기고 다른 일로 넘어가면, 돌아와서 확인하는 걸 잊기 쉬워요. Stop 훅은 Claude가 응답 턴을 마칠 때 실행되므로, 알림을 쏘기에 딱이에요. ntfy로 휴대폰에 푸시를 보내는 예제를 볼게요.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "curl -s -d \"Claude Code finished the task\" ntfy.sh/your-topic-name"
}
]
}
]
}
}
Stop은 어떤 도구에도 연결되지 않으므로 matcher가 필요 없어요. macOS에서는 osascript -e 'display notification "Done!" with title "Claude Code"'로, Linux에서는 notify-send로 바꿔 쓸 수 있어요. 작지만 여러 가지를 동시에 다룰 때 놀랍도록 유용해요.
훅을 써야 할 때 — 그리고 쓰지 말아야 할 때
훅은 강력하지만 모든 것에 쓰는 도구는 아니에요. 경계는 간단해요. 훅은 항상 반드시 실행돼야 하고 결정적인 것들을 위한 거예요 — 포맷, 테스트, 명령 차단, 로깅이죠. 필요한 게 행동 지침이나 능력이라면 더 적합한 도구가 따로 있어요.
| 해결하고 싶은 것 | 적합한 도구 |
|---|---|
| 매번 결정적으로 반드시 실행돼야 하는 것(포맷, 테스트, 명령 차단) | 훅 |
| Claude의 스타일/코드 규칙을 유도하기 | CLAUDE.md의 규칙 |
| 필요할 때 직접 실행하는 동작 | Claude Code의 슬래시 명령 |
| 재사용 가능한 능력(지침 + 스크립트) 패키징 | Claude Code의 스킬 |
틀리기 쉬운 예를 들어 볼게요. 「Claude에게 항상 테스트를 쓰라고 상기시키는 것」은 훅이 아니라 CLAUDE.md의 규칙이어야 해요 — 부드러운 지침이니까요. 하지만 「src/ 안의 파일을 편집한 뒤 전체 테스트 스위트를 실행하는 것」은 결정적이라서 진짜 훅에 어울려요. 이 네 개념이 아직 흐릿하게 느껴진다면, 스킬, 서브에이전트, 훅, MCP가 어떻게 다른지에 관한 별도의 글을 준비해 뒀어요 — 거기서 전체 그림이 하나로 맞춰져요.
⚠️ 훅을 쓸 때의 안전 유의사항
여기가 대부분의 가이드가 건너뛰는 섹션이지만, 가장 중요해요. 공식 문서(code.claude.com/docs/en/hooks, 2026-08-09 접속)에 따르면, 훅은 여러분의 사용자 계정 권한 전체로, 샌드박스 없이 실행돼요. 즉, 버그가 있는 훅 — 또는 실수로 복사해 온 악성 훅 — 이 파일을 삭제하거나, 비밀 정보를 유출하거나, 여러분의 머신에서 임의 코드를 실행하는 일이, 완전히 자동으로, 묻지도 않고 일어날 수 있다는 뜻이에요.
제가 항상 지키는 몇 가지 원칙이에요.
- 켜기 전에 모든 훅을 꼼꼼히 읽으세요 — 특히 플러그인, 키트, 또는 다른 사람의 저장소에서 온 훅은요. root로 실행되는 코드라고 여기고 다루세요.
- 비밀 정보(토큰, API 키)를 절대 하드코딩하지 마세요. 명령에 그대로 적지 말고 환경 변수에서 읽으세요.
- 민감한 훅은
.claude/settings.local.json(Git 무시)에 두세요. 공유 저장소에 실수로 커밋하지 않도록요. - 비상 정지 스위치를 알아 두세요. 디버깅 중이거나 뭔가 수상해 보일 때는
disableAllHooks로 모든 훅을 끌 수 있어요. 기업에서는allowManagedHooksOnly와allowedHttpHookUrls로 더 강하게 잠글 수 있어요. - 자동으로
allow하는PreToolUse는 조심하세요 — 확인 단계를 건너뛰어 편리하지만, 보호막 한 겹을 없애는 거예요.
핵심은 이거예요. 훅은 잘 드는 칼이에요. 아주 유용하지만 제대로 쥐어야 해요. 흔한 문제와 해결법은 Claude Code의 흔한 오류 트러블슈팅에서 더 확인하세요.
더 빠르게: 키트에서 제공하는 완성형 훅과 스킬
프로젝트마다 자기만의 훅, 스크립트, 스킬을 작성하는 건 꽤 품이 들어요 — 특히 일관된 안전 가드레일과 워크플로 세트를 원할 때는요. Claude Code용 키트 중에는 Claude Code용 AgentKit 키트처럼 스킬, 서브에이전트, 워크플로를 묶어 제공해서 모든 걸 처음부터 만들지 않아도 되는 것들이 있어요. 직접 보고 싶다면 AgentKit 가격 확인(링크로 20% 할인)을 해 보세요. 분명히 해 두자면, 이 글의 기본 훅은 직접 만들 수 있고 뭔가를 살 필요가 없어요. 키트는 완성된 세트 하나를 통째로 원할 때만 고려할 가치가 있어요.
디버깅: 왜 내 훅이 실행되지 않을까?
「조용한」 훅이 가장 흔한 문제예요. 이 체크리스트를 따라가면 거의 항상 원인을 찾을 수 있어요.
- JSON이 유효한가요?
settings.json에 후행 쉼표 하나만 있어도 파일 전체가 로드에 실패해요. JSON 검증기에 돌려 보세요. - 매처가 올바른 도구 이름을 쓰나요? 이름은 대소문자를 구분해요.
Bash,Edit,Write이지bash나edit가 아니에요. - 스크립트가 올바른 종료 코드를 반환하나요? 통과는
exit 0, (PreToolUse에서) 차단은exit 2예요. 다른 종료 코드는 무시될 수 있어요. - stdout이 「깨끗」한가요? 훅이 제어용 JSON을 반환한다면, stdout에는 그 JSON만 있어야 해요. 엉뚱한 텍스트가 있으면 파싱이 깨져요.
- 스크립트가 실행 가능한가요? macOS/Linux에서는
chmod +x를 잊지 마세요. disableAllHooks가 켜져 있나요? 디버깅하려고 아까 훅을 껐다면, 다시 켜는 걸 잊지 마세요.
자주 묻는 질문(FAQ)
훅은 슬래시 명령이나 스킬과 어떻게 다른가요?
훅은 수명주기의 여러 지점에서 자동으로 실행돼요(직접 부르는 게 아니에요). 슬래시 명령은 필요할 때 직접 실행하는 동작이에요. 스킬은 패키징된 능력(지침 + 스크립트)으로, 맥락이 맞으면 Claude가 스스로 로드해요. 한마디로, 훅 = 자동이고 결정적, 슬래시 명령 = 수동, 스킬 = 재사용 가능한 능력이에요.
훅이 Windows에서 작동하나요?
네. type: command는 셸 명령을 실행하므로, PowerShell 스크립트(powershell -File .claude\hooks\block-danger.ps1)를 가리키거나 Git Bash/WSL로 bash 스크립트를 실행할 수 있어요. 명령이 여러분 머신의 셸에서 유효하기만 하면 돼요.
훅이 Claude Code를 느리게 하나요?
명령이 무거우면 그럴 수 있어요. 훅은 이벤트 순간에 동기적으로 실행되므로, 느린 포매터나 테스트 스위트는 매 턴을 늘어지게 해요. 훅은 가볍게 유지하고, 저장소 전체가 아니라 방금 바뀐 파일만($CLAUDE_FILE_PATHS) 포맷하고, 무거운 테스트는 PostToolUse에서 빼는 걸 고려하세요.
글로벌 훅과 프로젝트 훅의 차이는 무엇인가요?
글로벌 훅(~/.claude/settings.json)은 머신의 모든 프로젝트에 적용되며 커밋되지 않아요. 프로젝트 훅(.claude/settings.json)은 그 프로젝트에만 적용되고, 커밋해서 팀 전체가 공유할 수 있어요. .local.json 변형은 프로젝트별이지만 Git 무시라서, 나만의 비공개 설정용이에요.
훅으로 위험한 명령을 차단할 수 있나요?
네, 그게 바로 PreToolUse의 강점이에요. 훅은 명령이 실행되기 전에 검사해서 permissionDecision: "deny"나 종료 코드 2로 거부해요. 예를 들어 rm -rf나 git push --force를 차단하죠(위의 예제 1 참고).
훅은 안전한가요?
훅은 여러분의 사용자 권한 전체로, 샌드박스 없이 실행되므로, 버그가 있거나 악성인 훅은 실제 피해(파일 삭제, 비밀 정보 유출)를 줄 수 있어요. 훅을 신중히 작성하고 검토한다면 메커니즘 자체는 안전해요. 위험은 읽지 않고 알 수 없는 훅을 켜는 데서 와요. 훅은 항상 최고 권한으로 실행되는 코드로 여기고 다루세요.
결론 + 다음 단계
세 가지 핵심 이벤트를 익히면, 필요한 거의 모든 것에 훅을 쓸 수 있어요. 위험한 명령을 막는 PreToolUse, 자동 포맷/테스트의 PostToolUse, 알림을 받는 Stop이죠. 황금률을 기억하세요 — 훅은 권한 전체로, 샌드박스 없이 실행되니 켜기 전에 꼼꼼히 확인하세요. 더 나아가려면 Claude Code의 스킬이 무엇인지와 Claude Code의 슬래시 명령을 읽거나, 한발 물러서서 스킬, 서브에이전트, 훅, MCP가 어떻게 다른지와 각각을 언제 쓰는지 큰 그림을 확인하세요.