Free

Claude Code Hooks 완전 가이드: Claude의 모든 동작을 통제하라

Claude Code Hooks 메커니즘 완전 분석. 5가지 이벤트 유형부터 종료 코드 제어까지, 실전 설정 4가지로 AI 작업을 감사·차단·자동화하는 방법.


Claude Code가 파일을 읽고, 코드를 작성하고, 명령을 실행할 때마다 백그라운드에서 이벤트 시스템이 작동합니다. Hooks는 이 시스템에 접속하는 인터페이스입니다——원하는 시점에 자신의 로직을 주입하고, 자동으로 코드 검사를 실행하고, 로그를 기록하고, 위험한 작업을 차단하고, 어떤 셸 명령이든 트리거할 수 있습니다.

이 글에서는 Hooks의 메커니즘, 설정 방법, 실전 활용법을 완전히 설명합니다.


Hooks란 무엇인가

Hooks는 settings.json에 설정된 셸 명령으로, 특정 이벤트가 발생할 때 Claude Code가 자동으로 실행합니다.

가장 직관적인 비유: git hooks입니다. git은 commit, push 등의 작업 전후에 스크립트를 트리거할 수 있습니다. Claude Code Hooks도 완전히 같은 방식이며, 차이점은 트리거 시점이 AI의 도구 호출이라는 것입니다.

왜 중요한가?

Claude Code의 능력이 강해질수록 결정론적인 제어 레이어가 더 필요합니다. Hooks가 제공하는 것:
- 프롬프트에 의존하지 않는 실행 보장 (Claude는 지시를 무시할 수 있지만 hook은 반드시 실행됨)
- 감사 가능한 작업 기록
- 자동화된 품질 검사


다섯 가지 Hook 유형

유형 트리거 시점 주요 용도
PreToolUse 도구 호출 위험한 작업 차단, 의도 기록
PostToolUse 도구 호출 자동 lint, 테스트 실행
PreCompact 컨텍스트 압축 현재 상태 스냅샷 저장
Notification Claude가 알림을 보낼 때 데스크톱 알림, Slack 메시지
Stop Claude가 응답을 완료했을 때 로그 집계, 후속 플로우 트리거

가장 자주 쓰이는 것은 PreToolUsePostToolUse로, 도구 호출을 가로채고 후처리하는 데 사용합니다.


설정 형식

Hooks는 ~/.claude/settings.json(전역) 또는 프로젝트 루트의 .claude/settings.json(프로젝트 단위)에 작성합니다:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint --silent"
          }
        ]
      }
    ]
  }
}

세 가지 핵심 필드:

  • matcher: 도구 이름과 매칭하는 정규식. 어떤 도구 호출 후에 이 hook이 트리거될지 결정합니다. "Write|Edit"은 파일 쓰기 또는 편집 시 트리거됩니다. 비워두거나 ".*"로 모든 도구와 매칭합니다.
  • type: 현재는 "command"만 지원.
  • command: 임의의 셸 명령.

도구 이름 참조

matcher를 작성할 때 필요한 도구 이름:

도구 이름 해당 작업
Write 새 파일 쓰기
Edit 파일 편집
Bash 셸 명령 실행
Read 파일 읽기
Glob 파일 검색
Grep 내용 검색
TodoWrite 작업 목록 업데이트

Hook 실행 환경

실행 시 stdin을 통해 JSON이 전달됩니다:

{
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.rb",
    "content": "..."
  },
  "tool_response": "..."
}

PreToolUsetool_input(호출 전 인수)을 받고, PostToolUsetool_response(도구 반환값)도 함께 받습니다.

조건 로직이 있는 hook 예시:

#!/bin/bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path')

# .rb 파일에만 rubocop 실행
if [[ "$file" == *.rb ]]; then
  rubocop "$file" --autocorrect-all --no-color
fi

종료 코드의 의미

Hook의 종료 코드가 Claude Code의 후속 동작을 제어합니다:

종료 코드 의미
0 성공, 실행 계속
2 차단: 현재 도구 호출을 취소하고 stderr 출력을 Claude에 피드백
기타 비제로 오류 기록 후 실행 계속

종료 코드 2가 가장 유용합니다——PreToolUse에서 차단 로직을 작성해 Claude의 작업을 막고 이유를 전달할 수 있습니다.


실전: 네 가지 Hook 설정

1. 파일 쓰기 후 자동 포맷

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.rb ]] && rubocop -A \"$file\" --no-color -q || true'"
          }
        ]
      }
    ]
  }
}

2. 특정 디렉토리 삭제 금지

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'cmd=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .command); if echo \"$cmd\" | grep -qE \"rm.*/(migrations|seeds)\"; then echo \"migrations 또는 seeds 디렉토리 삭제는 금지되어 있습니다\" >&2; exit 2; fi'"
          }
        ]
      }
    ]
  }
}

3. 작업 로그

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"$(date '+%Y-%m-%d %H:%M:%S') $CLAUDE_TOOL_NAME: $(echo $CLAUDE_TOOL_INPUT | jq -c .)\" >> ~/.claude/audit.log"
          }
        ]
      }
    ]
  }
}

4. 완료 시 데스크톱 알림

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude가 완료되었습니다\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

전역 vs 프로젝트 단위

Hooks를 배치할 수 있는 두 곳:

전역(~/.claude/settings.json): 모든 프로젝트에 공통으로 적용되는 규칙 (로그, 알림 등).

프로젝트 단위(.claude/settings.json): 프로젝트 고유의 검사. 프로젝트 단위 hooks와 전역 hooks는 병합되어 함께 실행됩니다.

팀 프로젝트에서는 프로젝트 단위 settings.json을 git에 커밋해 모든 팀원이 동일한 hooks를 실행하도록 권장합니다.


Hook 디버깅

Hook이 작동하지 않을 때 확인 순서:

  1. matcher 철자 확인: 도구 이름은 대소문자를 구분합니다. writeWrite는 다릅니다
  2. 명령 단독 실행: hook의 command를 복사해 터미널에서 직접 실행해보기
  3. Claude Code 출력 확인: hook의 stderr는 대화창에 표시됩니다
  4. 단순화 테스트: echo "hook triggered" >> /tmp/hook.log로 트리거 확인 후 복잡한 로직 추가

정리

Hooks의 핵심 가치는 예측 불가능한 AI 동작과 결정론적인 엔지니어링 표준을 연결하는 것입니다. Claude는 테스트 실행을 잊을 수 있지만 PostToolUse hook은 잊지 않습니다. Claude가 실수로 파일을 삭제하려 해도 PreToolUse 차단기가 막아줍니다.

hook 하나부터 시작하세요: 파일 쓰기 후 자동 lint. 그게 잘 돌아가면 나머지를 추가하면 됩니다.