Code 실전

Claude Code Hooks 완전 분석 — 8종 이벤트로 AI 실행을 통제하는 법

seunghyeonlab 2026. 5. 1. 12:02

hero

Claude Code를 단순히 명령 받는 도구로 쓰고 있다면, Hooks 시스템을 모르는 것이다. 이 글은 PreToolUse부터 Stop까지 8종 훅 이벤트 전체를 실제 프로젝트에 붙여 검증한 경험을 담는다. 위험 명령 차단, 감사 로그, 슬랙 알림까지 .claude/ 폴더 하나로 수렴시키는 방법을 따라가 보자.

Claude Code Hooks 전체 흐름


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 차단 결정 흐름

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가 응답을 완전히 멈춘 시점이다. 이 둘을 겹치면 '무엇을 실행했고 어떻게 끝났는지'가 한 쌍의 로그로 남는다.

PostToolUse와 Stop 이중 레이어

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.jsonhooks 키에서 이벤트·매처·명령을 매핑한다.

settings.json 배선 구조

하나의 이벤트에 여러 훅을 직렬로 붙이는 것도 가능하다. 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 구독