Claude Code를 단순히 명령 받는 도구로 쓰고 있다면, Hooks 시스템을 모르는 것이다. 이 글은 PreToolUse부터 Stop까지 8종 훅 이벤트 전체를 실제 프로젝트에 붙여 검증한 경험을 담는다. 위험 명령 차단, 감사 로그, 슬랙 알림까지 .claude/ 폴더 하나로 수렴시키는 방법을 따라가 보자.
8종 훅 이벤트 — 발화 시점 정리
Claude Code가 공식 지원하는 훅 이벤트는 아래 8종이다.
| 이벤트 | 발화 시점 |
|---|---|
UserPromptSubmit |
사용자 메시지 전송 직후 |
PreToolUse |
도구 실행 직전 |
PostToolUse |
도구 실행 완료 직후 |
Stop |
Claude 응답 완전 종료 시점 |
SubagentStop |
서브에이전트 종료 시점 |
PreCompact |
컨텍스트 압축 직전 |
PostCompact |
컨텍스트 압축 완료 직후 |
Notification |
시스템 알림 발생 시 |
공항 보안 검색대로 생각하면 외우기 쉽다. PreToolUse는 X-레이 통과 단계, PostToolUse는 기내 반입 후 확인, Stop은 착륙 후 최종 로그다.
각 훅은 환경변수로 컨텍스트를 받는다.
# PreToolUse 훅이 받는 주요 환경변수
echo $CLAUDE_TOOL_NAME # 실행될 도구명 (예: Bash, Edit)
echo $CLAUDE_TOOL_INPUT # JSON 직렬화된 인자
echo $CLAUDE_SESSION_ID # 세션 식별자
echo $CLAUDE_HOOK_EVENT # 현재 훅 이벤트명
환경변수 하나하나가 훅 스크립트 안에서 판단 근거가 된다. 도구명이 뭔지, 어떤 인자를 받았는지, 어느 세션인지 — 이 세 가지만 있으면 대부분의 정책을 구현할 수 있다.
PreToolUse로 위험 명령 차단 게이트 만들기
PreToolUse 훅은 exit code 하나로 실행 여부를 결정한다. exit 0이면 통과, exit 2면 차단. 이 두 줄 차이가 '보안 정책'과 '장식'을 가른다.
n8n 2.8.4 연동 자동화 파이프라인을 테스트하면서 rm -rf 패턴을 가진 Bash 호출 14건을 전부 차단했다. 단 한 건도 슬립스루 없었다.
#!/usr/bin/env bash
# .claude/hooks/pre_tool_use.sh
# PreToolUse: Bash 위험 명령 차단 게이트
if [ "$CLAUDE_TOOL_NAME" = "Bash" ]; then
INPUT="$CLAUDE_TOOL_INPUT"
if echo "$INPUT" | grep -qE '(rm -rf|DROP TABLE|sudo rm)'; then
echo '{"decision":"block","reason":"위험 명령 패턴 감지"}'
exit 2
fi
fi
exit 0
차단 시 JSON으로 reason을 돌려주면 Claude가 왜 막혔는지 맥락을 파악한다. 빈 응답보다 디버깅이 훨씬 빠르다.
패턴은 정규식으로 얼마든지 확장할 수 있다. git push --force, chmod 777, curl | bash 같은 패턴도 같은 방식으로 추가하면 된다.
PostToolUse + Stop 조합으로 감사 로그 + 슬랙 알림
PostToolUse는 도구 실행 결과가 나온 직후 발화한다. Stop은 Claude가 응답을 완전히 멈춘 시점이다. 이 둘을 겹치면 '무엇을 실행했고 어떻게 끝났는지'가 한 쌍의 로그로 남는다.
Draw Things 이미지 생성 배치 자동화 파이프라인에 붙여봤다. 세션당 평균 37개의 도구 호출이 기록됐고, Stop 훅이 슬랙으로 요약을 쐈다. 응답 완료까지 평균 4.2초 — 사람이 Cmd+Tab으로 확인창 찾는 시간보다 빠르다.
#!/usr/bin/env bash
# .claude/hooks/post_tool_use.sh
# PostToolUse + Stop 겸용 감사 로그 및 슬랙 알림
LOG_DIR="$HOME/.claude/audit"
mkdir -p "$LOG_DIR"
# 모든 도구 실행 기록
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) \
TOOL=$CLAUDE_TOOL_NAME \
SESSION=$CLAUDE_SESSION_ID" \
>> "$LOG_DIR/audit.log"
# Stop 이벤트일 때만 슬랙 전송
if [ "${CLAUDE_HOOK_EVENT}" = "Stop" ]; then
PAYLOAD='{"text":"Claude 세션 종료: '"$CLAUDE_SESSION_ID"'"}'
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "$PAYLOAD" > /dev/null
fi
exit 0
$SLACK_WEBHOOK_URL은 환경변수로 주입하면 된다. 스크립트에 하드코딩하면 버전 관리 시 유출 위험이 있으니 주의한다.
settings.json 한 파일로 전체 훅 배선
훅은 .claude/settings.json의 hooks 키에서 이벤트·매처·명령을 매핑한다.
하나의 이벤트에 여러 훅을 직렬로 붙이는 것도 가능하다. PreToolUse 차단 게이트 + PostToolUse 감사 로그 + Stop 슬랙 알림, 이 세 스크립트가 settings.json 한 파일로 수렴한다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": ".claude/hooks/pre_tool_use.sh" }
]
}
],
"PostToolUse": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": ".claude/hooks/post_tool_use.sh" }
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": ".claude/hooks/post_tool_use.sh" }
]
}
]
}
}
matcher를 비워두면 모든 도구 호출에 반응한다. 특정 도구만 감시하려면 "Bash", "Edit" 처럼 이름을 직접 쓰면 된다.
운영 시 주의할 함정
훅 스크립트 실행 권한 빠짐. 가장 흔한 실수다. 스크립트 추가 후 반드시 chmod +x .claude/hooks/*.sh를 실행한다.
exit code 혼동. exit 1은 오류로 처리되지 않고 통과될 수 있다. 차단은 반드시 exit 2로 써야 한다.
환경변수 미주입. 슬랙 알림 훅에서 $SLACK_WEBHOOK_URL이 비어있으면 curl이 조용히 실패한다. 훅 스크립트 앞에 변수 검증 로직을 넣는 게 좋다.
# 변수 미설정 시 조기 종료
if [ -z "$SLACK_WEBHOOK_URL" ]; then
echo "SLACK_WEBHOOK_URL 미설정, 슬랙 전송 건너뜀" >&2
exit 0
fi
Mac vs Linux 날짜 포맷 차이. date -u +%Y-%m-%dT%H:%M:%SZ는 GNU date 기준이다. macOS에서는 동일하게 동작하지만, Alpine 기반 Docker 이미지에서는 busybox date라 플래그가 다를 수 있다.
마무리
Hooks는 Claude Code를 '쓰는 도구'에서 '통제하는 시스템'으로 격상시킨다. PreToolUse 차단 + PostToolUse 로그 + Stop 알림, 이 세 조합만으로 감사 추적과보안 정책이 .claude/ 폴더 안에 완결된다. settings.json을 팀 저장소에 커밋하는 순간, 정책이 코드가 된다.
다음 글은 SubagentStop과 PreCompact를 활용해 멀티에이전트 파이프라인의 중간 체크포인트를 설계하는 방법을 다룬다.
🐦 X에서 더 빠르게: @baegseungh7061
📚 이 시리즈 더 보기: Code 실전
💌 새 글 알림: X 팔로우 또는 블로그 RSS 구독
'Code 실전' 카테고리의 다른 글
| Claude Code Pre/Post 훅을 이벤트 버스로 연결해 비동기 파이프라인 구성하기 (0) | 2026.05.09 |
|---|---|
| Claude Code 실행 이력을 JSON으로 수집해 이상 탐지 자동화하기 (0) | 2026.05.08 |
| MCP 서버 내부 상태를 실시간으로 들여다보는 법 — 스냅샷 엔드포인트로 툴 호출 흐름 시각화하기 (0) | 2026.05.07 |
| Mac Mini 클러스터에 자가 치유 시스템 심기 — n8n으로 만드는 로컬 AI 면역 체계 (0) | 2026.04.30 |
| Claude Code로 자율 검증 파이프라인 구축하기 — Plan-Execute-Verify 루프 실전 적용 (0) | 2026.04.29 |