Claude Code를 쓰다 보면 한 가지 답답한 순간이 옵니다. "매번 Prettier 실행해달라고 말해야 하나?" 같은 생각이요. Claude Code Hooks는 바로 그 지점을 해결합니다. AI한테 부탁하는 게 아니라, 특정 시점에 명령이 자동으로 실행되도록 못을 박아두는 방식입니다.
Claude Code Hooks가 하는 일
공식 문서에는 이렇게 적혀 있습니다.
"Hooks provide deterministic control over Claude Code's behavior, ensuring certain actions always happen rather than relying on the LLM to choose to run them."
번역하면 이렇습니다. Claude가 "기억해서" 해주길 기다리는 게 아니라, 반드시 실행된다를 보장하는 레이어입니다.
저도 처음엔 CLAUDE.md에 "파일 수정 후 꼭 black 실행해줘"라고 써뒀는데, 가끔 빠뜨리더라고요. Hooks를 쓰고 나서는 그 걱정이 사라졌습니다.
2026년 현재 v2.1.141+ 기준으로 27~30개의 수명주기 이벤트를 지원합니다. 다섯 가지 핸들러 타입도 있어요.
| 타입 | 설명 |
|---|---|
command |
셸 명령어. stdin으로 JSON 수신 |
http |
HTTP POST 엔드포인트 |
mcp_tool |
MCP 서버 툴 호출 |
prompt |
단발성 LLM 평가 (최대 30초) |
agent |
툴 접근 가능한 서브에이전트 (최대 60초) |
핵심 이벤트 5가지 — 이것만 알면 시작 가능
PreToolUse: 가장 강력한 보안 게이트
Claude가 툴을 실행하기 직전에 발동합니다. exit 2로 아예 차단할 수 있어서, 5가지 이벤트 중에 제일 강력합니다.
{
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "rm -rf dist/" }
}
이런 JSON이 stdin으로 들어오고, 스크립트가 exit 2를 반환하면 해당 명령은 실행되지 않습니다. git push --force main이나 DROP TABLE 같은 걸 원천 차단하는 데 딱입니다.
PostToolUse: 코드 포맷팅 자동화의 핵심
툴이 성공적으로 끝난 뒤 실행됩니다. 차단은 안 되고, 피드백이나 추가 작업만 가능합니다. 포맷터를 여기서 연결해두면 파일 수정할 때마다 자동으로 돌아갑니다.
Stop: Claude 응답 완료 후
Claude가 응답을 마쳤을 때 실행됩니다. exit 2로 계속 작업을 강제할 수도 있고, macOS 알림을 띄우는 데도 씁니다.
PreCompact / PostCompact: 컨텍스트 압축 전후
컨텍스트 윈도우가 가득 찼을 때 압축 직전/직후에 발동합니다. PreCompact에서 트랜스크립트를 백업해두면 중요한 작업 흐름을 잃지 않아요. manual(사용자가 /compact 직접 실행)과 auto(자동 압축)로 구분해서 다르게 처리할 수도 있습니다.
Notification: 관찰 전용 이벤트
permission_prompt, auth_success, elicitation_dialog 같은 알림이 발송될 때 실행됩니다. 차단은 안 되고, Slack 채널로 전달하거나 데스크탑 알림으로 바꾸는 용도로 씁니다.
settings.json 설정 방법
설정 파일은 두 곳에 둘 수 있습니다.
| 위치 | 범위 | 팀 공유 |
|---|---|---|
~/.claude/settings.json |
사용자 전역 | 불가 |
.claude/settings.json |
프로젝트 단위 | 가능 (git 커밋) |
.claude/settings.local.json |
프로젝트 단위 (개인) | 불가 (gitignore) |
구조는 3단 중첩입니다. 이벤트 이름 → 매처 그룹 배열 → 핸들러 배열 순서입니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/security-check.sh"
}
]
}
]
}
}
매처(Matcher)는 어떤 툴에 반응할지 필터링합니다. "Bash"는 Bash 툴만, "Edit|Write"는 둘 다, "mcp__.*__write.*"는 모든 MCP 서버의 write 계열 툴에 반응합니다.
종료 코드는 간단합니다. 0이면 성공, 2면 차단(stderr를 Claude에게 전달), 그 외 숫자면 오류를 표시하되 실행은 계속합니다.
바로 쓸 수 있는 실전 설정 4가지
1. 파일 수정 시 자동 포맷팅
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]]; then black --quiet \"$FILE_PATH\"; elif [[ \"$FILE_PATH\" == *.js ]] || [[ \"$FILE_PATH\" == *.ts ]]; then npx prettier --write \"$FILE_PATH\"; fi'"
}
]
}
]
}
}
Python이면 Black, JS/TS면 Prettier가 자동으로 돌아갑니다. 매번 수동으로 실행할 필요가 없어집니다.
2. 위험 명령어 차단 스크립트
#!/bin/bash
# .claude/hooks/security-gate.sh
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r ".tool_input.command")
DANGEROUS_PATTERNS="rm\s+-rf\s+/|git\s+push\s+(-f|--force)\s+(origin\s+)?main|git\s+reset\s+--hard|DROP\s+TABLE"
if echo "$CMD" | grep -qE "$DANGEROUS_PATTERNS"; then
echo "BLOCKED: 위험 명령어 감지: $CMD" >&2
exit 2
fi
rm -rf /, git push --force main, git reset --hard, DROP TABLE을 차단합니다. PreToolUse에 Bash 매처로 연결해두면 됩니다.
3. .env 파일 보호
#!/usr/bin/env python3
import json
import sys
input_data = json.load(sys.stdin)
file_path = input_data.get("tool_input", {}).get("file_path", "")
dangerous_paths = [".env", ".env.local", "secrets.json", "credentials.json"]
if any(pattern in file_path for pattern in dangerous_paths):
print(f"차단: {file_path}는 민감 파일입니다", file=sys.stderr)
sys.exit(2)
sys.exit(0)
4. 작업 완료 알림 (macOS)
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude 작업 완료\" with title \"Claude Code\"'"
}
]
}
]
}
}
Linux는 notify-send "Claude Code" "작업이 완료되었습니다"로 바꾸면 됩니다.
Hooks vs MCP vs 서브에이전트, 뭐가 다른가
솔직히 말하면 처음에 이 셋이 헷갈렸습니다.
| 특성 | Hooks | MCP | 서브에이전트 |
|---|---|---|---|
| 역할 | 강제 집행 | 외부 툴 연결 | 독립 작업 위임 |
| 트리거 | 이벤트 기반 (자동) | 툴 호출 시 | 명시적 생성 |
| 차단 능력 | 있음 (exit 2) | 없음 | 없음 |
기준은 이렇게 잡으면 됩니다.
Hooks를 쓸 때: 포맷팅, 린트, 검증처럼 항상 실행되어야 하는 규칙. "AI가 기억하길 바라는" 게 아니라 반드시 실행되어야 할 때.
MCP를 쓸 때: GitHub, Jira, 데이터베이스 등 외부 시스템 연결. 외부 데이터를 가져오는 작업.
서브에이전트를 쓸 때: 코드베이스 탐색, PR 리뷰처럼 깊은 탐색 작업. 메인 컨텍스트를 오염시키지 않고 독립적으로 처리해야 할 때.
MCP 툴도 mcp__<server>__<tool> 형식의 툴명으로 매처에 잡히기 때문에, MCP 서버의 쓰기 작업에도 동일한 보안 게이트를 걸 수 있습니다.
보안 주의사항: 가볍게 보면 안 됩니다
CVE-2025-59536 — Check Point Research가 2026년 초에 발견한 취약점입니다. 악성 저장소의 .claude/settings.json에 숨겨진 SessionStart hook이 자동 실행되어 역쉘(reverse shell)을 설치하거나 API 토큰을 탈취할 수 있다는 내용입니다. Anthropic은 현재 버전에서 패치했지만, 로직 자체는 여전히 유효합니다.
신뢰하지 않는 저장소를 클론할 때는 .claude/ 디렉터리를 코드와 똑같이 검토해야 합니다. .claude/settings.json을 "설정 파일"이 아니라 "실행 코드"로 보는 게 맞습니다.
성능 측면에서는 PostToolUse hook이 파일 편집마다 실행되기 때문에, 500ms 이상 걸리는 hook은 체감 지연을 일으킵니다. 실전에서 95개 hook을 운용해도 각 200ms 이내면 지연이 없다는 사례가 있습니다. 비차단 작업은 async: true로 백그라운드 실행을 권장합니다.
타임아웃 기본값도 알아두세요. command, http, mcp_tool은 600초, prompt는 30초, agent는 60초입니다.
자주 묻는 질문
Q. settings.json을 git에 커밋해도 되나요?
.claude/settings.json은 커밋해서 팀과 공유할 수 있습니다. 단, 민감한 API 토큰이나 경로가 담긴 개인 설정은 .claude/settings.local.json에 넣고 gitignore로 제외해야 합니다.
Q. hook이 실패하면 어떻게 되나요?
종료 코드 2면 해당 이벤트가 차단되고 stderr가 Claude에게 전달됩니다. 그 외 0이 아닌 코드는 오류를 표시하되 실행은 계속됩니다. PostToolUse는 차단 자체가 불가능합니다.
Q. 모든 툴에 hook을 걸려면?
매처를 "*"나 ""로 설정하거나 아예 생략하면 됩니다. 모든 툴 이벤트에 반응합니다.
Q. prompt 타입 hook은 언제 쓰나요?
yes/no 판단이 필요한 경우입니다. 예를 들어 Stop hook에서 "테스트가 통과했는지" LLM에게 물어보고, 미통과 시 exit 2로 차단해서 Claude가 계속 작업하도록 강제하는 패턴에 씁니다. 최대 30초 타임아웃이 있습니다.
직접 하나씩 설정해보는 게 제일 빠릅니다. PostToolUse에 간단한 포맷터 하나 연결해보는 것부터 시작하면 금방 감이 잡혀요. 더 복잡한 보안 게이트나 테스트 자동 실행 패턴이 궁금하다면, DEV Community의 "20+ Examples" 글이나 Pixelmojo의 "6 Production Patterns"를 같이 보시길 권합니다.
'Trend' 카테고리의 다른 글
| Cursor AI 요금제 비교 2026: 무료 vs Pro vs Business 뭐가 이득? (0) | 2026.06.19 |
|---|---|
| Claude Code vs Cursor vs GitHub Copilot: 2026년 실무자가 직접 비교한 AI 코딩 도구 3종 (0) | 2026.06.19 |
| Claude Pro·Max·Team 요금제 완전 분석: 내 상황엔 어떤 플랜이 맞을까? (0) | 2026.06.19 |
| ChatGPT Codex vs Claude Code: 2026년 AI 코딩 에이전트 비교, 어떤 걸 써야 할까 (0) | 2026.06.19 |
| 클로드 코드 핵심 기능 5가지 — 진짜 쓸 만한 것만 골랐습니다 (0) | 2026.06.19 |
