소개

Claude Code 훅은 Claude Code의 라이프사이클의 다양한 지점에서 실행되는 사용자 정의 셸 명령입니다. 훅은 Claude Code의 동작에 대한 결정론적 제어를 제공하여, LLM이 실행을 선택하는 것에 의존하지 않고 특정 작업이 항상 발생하도록 보장합니다.

사용 사례 예시:

  • 알림: Claude Code가 사용자의 입력이나 실행 권한을 기다릴 때 알림을 받는 방법을 사용자 정의합니다.
  • 자동 포맷팅: 모든 파일 편집 후 .ts 파일에 prettier를, .go 파일에 gofmt를 실행합니다.
  • 로깅: 규정 준수나 디버깅을 위해 실행된 모든 명령을 추적하고 계산합니다.
  • 피드백: Claude Code가 코드베이스 규칙을 따르지 않는 코드를 생성할 때 자동화된 피드백을 제공합니다.
  • 사용자 정의 권한: 프로덕션 파일이나 민감한 디렉토리에 대한 수정을 차단합니다.

이러한 규칙을 프롬프트 지시사항이 아닌 훅으로 인코딩함으로써, 제안을 실행될 때마다 실행되는 앱 수준 코드로 변환합니다.

훅은 확인 없이 전체 사용자 권한으로 셸 명령을 실행합니다. 훅이 안전하고 보안이 유지되도록 하는 것은 사용자의 책임입니다. Anthropic은 훅 사용으로 인한 데이터 손실이나 시스템 손상에 대해 책임지지 않습니다. 보안 고려사항을 검토하세요.

빠른 시작

이 빠른 시작에서는 Claude Code가 실행하는 셸 명령을 로그하는 훅을 추가합니다.

빠른 시작 전제조건: 명령줄에서 JSON 처리를 위해 jq를 설치하세요.

1단계: 훅 구성 열기

/hooks 슬래시 명령을 실행하고 PreToolUse 훅 이벤트를 선택하세요.

PreToolUse 훅은 도구 호출 전에 실행되며, Claude에게 다르게 수행할 작업에 대한 피드백을 제공하면서 도구 호출을 차단할 수 있습니다.

2단계: 매처 추가

+ Add new matcher…를 선택하여 Bash 도구 호출에서만 훅을 실행하도록 하세요.

매처에 Bash를 입력하세요.

3단계: 훅 추가

+ Add new hook…을 선택하고 다음 명령을 입력하세요:

jq -r '"\(.tool_input.command) - \(.tool_input.description // "No description")"' >> ~/.claude/bash-command-log.txt

4단계: 구성 저장

저장 위치로 User settings를 선택하세요. 홈 디렉토리에 로그를 남기므로 이 훅은 현재 프로젝트뿐만 아니라 모든 프로젝트에 적용됩니다.

그런 다음 REPL로 돌아갈 때까지 Esc를 누르세요. 이제 훅이 등록되었습니다!

5단계: 훅 확인

/hooks를 다시 실행하거나 ~/.claude/settings.json을 확인하여 구성을 확인하세요:

"hooks": {
  "PreToolUse": [
    {
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \"No description\")\"' >> ~/.claude/bash-command-log.txt"
        }
      ]
    }
  ]
}

구성

Claude Code 훅은 설정 파일에서 구성됩니다:

  • ~/.claude/settings.json - 사용자 설정
  • .claude/settings.json - 프로젝트 설정
  • .claude/settings.local.json - 로컬 프로젝트 설정 (커밋되지 않음)
  • 엔터프라이즈 관리 정책 설정

구조

훅은 매처별로 구성되며, 각 매처는 여러 훅을 가질 수 있습니다:

{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here"
          }
        ]
      }
    ]
  }
}
  • matcher: 도구 이름과 일치시킬 패턴 (PreToolUsePostToolUse에만 적용)
    • 단순 문자열은 정확히 일치: Write는 Write 도구만 일치
    • 정규식 지원: Edit|Write 또는 Notebook.*
    • 생략하거나 빈 문자열인 경우, 일치하는 모든 이벤트에 대해 훅이 실행됩니다
  • hooks: 패턴이 일치할 때 실행할 명령 배열
    • type: 현재 "command"만 지원됩니다
    • command: 실행할 bash 명령
    • timeout: (선택사항) 진행 중인 모든 훅을 취소하기 전에 명령이 실행되어야 하는 시간(초)

훅 이벤트

PreToolUse

Claude가 도구 매개변수를 생성한 후, 도구 호출을 처리하기 전에 실행됩니다.

일반적인 매처:

  • Task - 에이전트 작업
  • Bash - 셸 명령
  • Glob - 파일 패턴 매칭
  • Grep - 콘텐츠 검색
  • Read - 파일 읽기
  • Edit, MultiEdit - 파일 편집
  • Write - 파일 쓰기
  • WebFetch, WebSearch - 웹 작업

PostToolUse

도구가 성공적으로 완료된 직후에 실행됩니다.

PreToolUse와 동일한 매처 값을 인식합니다.

Notification

Claude Code가 알림을 보낼 때 실행됩니다.

Stop

메인 Claude Code 에이전트가 응답을 완료했을 때 실행됩니다.

SubagentStop

Claude Code 서브에이전트(Task 도구 호출)가 응답을 완료했을 때 실행됩니다.

훅 입력

훅은 세션 정보와 이벤트별 데이터를 포함하는 JSON 데이터를 stdin을 통해 받습니다:

{
  // 공통 필드
  session_id: string
  transcript_path: string  // 대화 JSON 경로

  // 이벤트별 필드
  ...
}

PreToolUse 입력

tool_input의 정확한 스키마는 도구에 따라 다릅니다.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  }
}

PostToolUse 입력

tool_inputtool_response의 정확한 스키마는 도구에 따라 다릅니다.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  }
}

Notification 입력

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "message": "Task completed successfully",
  "title": "Claude Code"
}

Stop 및 SubagentStop 입력

stop_hook_active는 Claude Code가 이미 정지 훅의 결과로 계속 진행 중일 때 true입니다. Claude Code가 무한히 실행되는 것을 방지하기 위해 이 값을 확인하거나 트랜스크립트를 처리하세요.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "stop_hook_active": true
}

훅 출력

훅이 Claude Code로 출력을 반환하는 두 가지 방법이 있습니다. 출력은 차단 여부와 Claude 및 사용자에게 표시되어야 하는 피드백을 전달합니다.

간단한 방법: 종료 코드

훅은 종료 코드, stdout, stderr을 통해 상태를 전달합니다:

  • 종료 코드 0: 성공. stdout은 트랜스크립트 모드(CTRL-R)에서 사용자에게 표시됩니다.
  • 종료 코드 2: 차단 오류. stderr은 Claude에게 자동으로 처리되도록 피드백됩니다. 아래 훅 이벤트별 동작을 참조하세요.
  • 기타 종료 코드: 비차단 오류. stderr은 사용자에게 표시되고 실행이 계속됩니다.

주의: 종료 코드가 0인 경우 Claude Code는 stdout을 보지 않습니다.

종료 코드 2 동작

훅 이벤트동작
PreToolUse도구 호출을 차단하고 Claude에게 오류를 표시
PostToolUseClaude에게 오류를 표시 (도구는 이미 실행됨)
Notification해당 없음, 사용자에게만 stderr 표시
Stop정지를 차단하고 Claude에게 오류를 표시
SubagentStop정지를 차단하고 Claude 서브에이전트에게 오류를 표시

고급: JSON 출력

훅은 더 정교한 제어를 위해 stdout에서 구조화된 JSON을 반환할 수 있습니다:

공통 JSON 필드

모든 훅 유형은 다음 선택적 필드를 포함할 수 있습니다:

{
  "continue": true, // 훅 실행 후 Claude가 계속해야 하는지 여부 (기본값: true)
  "stopReason": "string" // continue가 false일 때 표시되는 메시지
  "suppressOutput": true, // 트랜스크립트 모드에서 stdout 숨기기 (기본값: false)
}

continue가 false인 경우, Claude는 훅 실행 후 처리를 중단합니다.

  • PreToolUse의 경우, 이는 특정 도구 호출만 차단하고 Claude에게 자동 피드백을 제공하는 "decision": "block"과 다릅니다.
  • PostToolUse의 경우, 이는 Claude에게 자동화된 피드백을 제공하는 "decision": "block"과 다릅니다.
  • StopSubagentStop의 경우, 이는 모든 "decision": "block" 출력보다 우선합니다.
  • 모든 경우에서 "continue" = false는 모든 "decision": "block" 출력보다 우선합니다.

stopReasoncontinue와 함께 사용자에게 표시되는 이유를 제공하며, Claude에게는 표시되지 않습니다.

PreToolUse 결정 제어

PreToolUse 훅은 도구 호출이 진행될지 여부를 제어할 수 있습니다.

  • “approve”는 권한 시스템을 우회합니다. reason은 사용자에게 표시되지만 Claude에게는 표시되지 않습니다.
  • “block”은 도구 호출이 실행되는 것을 방지합니다. reason은 Claude에게 표시됩니다.
  • undefined는 기존 권한 흐름으로 이어집니다. reason은 무시됩니다.
{
  "decision": "approve" | "block" | undefined,
  "reason": "결정에 대한 설명"
}

PostToolUse 결정 제어

PostToolUse 훅은 도구 호출이 진행될지 여부를 제어할 수 있습니다.

  • “block”은 자동으로 Claude에게 reason을 프롬프트합니다.
  • undefined는 아무것도 하지 않습니다. reason은 무시됩니다.
{
  "decision": "block" | undefined,
  "reason": "결정에 대한 설명"
}

Stop/SubagentStop 결정 제어

StopSubagentStop 훅은 Claude가 계속해야 하는지 여부를 제어할 수 있습니다.

  • “block”은 Claude가 정지하는 것을 방지합니다. Claude가 어떻게 진행해야 하는지 알 수 있도록 reason을 채워야 합니다.
  • undefined는 Claude가 정지하도록 허용합니다. reason은 무시됩니다.
{
  "decision": "block" | undefined,
  "reason": "Claude가 정지하는 것이 차단될 때 반드시 제공되어야 함"
}

JSON 출력 예시: Bash 명령 편집

#!/usr/bin/env python3
import json
import re
import sys

# 검증 규칙을 (정규식 패턴, 메시지) 튜플의 목록으로 정의
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "더 나은 성능과 기능을 위해 'grep' 대신 'rg' (ripgrep)를 사용하세요",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "더 나은 성능을 위해 'find -name' 대신 'rg --files | rg pattern' 또는 'rg --files -g pattern'을 사용하세요",
    ),
]


def validate_command(command: str) -> list[str]:
    issues = []
    for pattern, message in VALIDATION_RULES:
        if re.search(pattern, command):
            issues.append(message)
    return issues


try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"오류: 잘못된 JSON 입력: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")

if tool_name != "Bash" or not command:
    sys.exit(1)

# 명령 검증
issues = validate_command(command)

if issues:
    for message in issues:
        print(f"• {message}", file=sys.stderr)
    # 종료 코드 2는 도구 호출을 차단하고 stderr을 Claude에게 표시
    sys.exit(2)

MCP 도구와 함께 작업하기

Claude Code 훅은 Model Context Protocol (MCP) 도구와 원활하게 작동합니다. MCP 서버가 도구를 제공할 때, 훅에서 일치시킬 수 있는 특별한 명명 패턴으로 나타납니다.

MCP 도구 명명

MCP 도구는 mcp__<server>__<tool> 패턴을 따릅니다. 예를 들어:

  • mcp__memory__create_entities - Memory 서버의 엔티티 생성 도구
  • mcp__filesystem__read_file - Filesystem 서버의 파일 읽기 도구
  • mcp__github__search_repositories - GitHub 서버의 검색 도구

MCP 도구에 대한 훅 구성

특정 MCP 도구나 전체 MCP 서버를 대상으로 할 수 있습니다:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

예시

코드 포맷팅

파일 수정 후 자동으로 코드를 포맷팅합니다:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/format-code.sh"
          }
        ]
      }
    ]
  }
}

알림

Claude Code가 권한을 요청하거나 프롬프트 입력이 유휴 상태가 되었을 때 전송되는 알림을 사용자 정의합니다.

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/my_custom_notifier.py"
          }
        ]
      }
    ]
  }
}

보안 고려사항

면책조항

본인 책임하에 사용: Claude Code 훅은 시스템에서 임의의 셸 명령을 자동으로 실행합니다. 훅을 사용함으로써 다음을 인정합니다:

  • 구성한 명령에 대해 전적으로 책임집니다
  • 훅은 사용자 계정이 액세스할 수 있는 모든 파일을 수정, 삭제 또는 액세스할 수 있습니다
  • 악의적이거나 잘못 작성된 훅은 데이터 손실이나 시스템 손상을 일으킬 수 있습니다
  • Anthropic은 어떠한 보증도 제공하지 않으며 훅 사용으로 인한 손해에 대해 책임을 지지 않습니다
  • 프로덕션 사용 전에 안전한 환경에서 훅을 철저히 테스트해야 합니다

구성에 추가하기 전에 항상 훅 명령을 검토하고 이해하세요.

보안 모범 사례

더 안전한 훅을 작성하기 위한 주요 관행들:

  1. 입력 검증 및 정제 - 입력 데이터를 맹목적으로 신뢰하지 마세요
  2. 항상 셸 변수를 인용 - $VAR가 아닌 "$VAR"를 사용하세요
  3. 경로 순회 차단 - 파일 경로에서 ..를 확인하세요
  4. 절대 경로 사용 - 스크립트에 전체 경로를 지정하세요
  5. 민감한 파일 건너뛰기 - .env, .git/, 키 등을 피하세요

구성 안전성

설정 파일에서 훅을 직접 편집해도 즉시 적용되지 않습니다. Claude Code는:

  1. 시작 시 훅의 스냅샷을 캡처합니다
  2. 세션 전체에서 이 스냅샷을 사용합니다
  3. 훅이 외부에서 수정되면 경고합니다
  4. 변경사항이 적용되려면 /hooks 메뉴에서 검토가 필요합니다

이는 악의적인 훅 수정이 현재 세션에 영향을 미치는 것을 방지합니다.

훅 실행 세부사항

  • 타임아웃: 기본적으로 60초 실행 제한, 명령별로 구성 가능.
    • 개별 명령이 타임아웃되면 진행 중인 모든 훅이 취소됩니다.
  • 병렬화: 일치하는 모든 훅이 병렬로 실행됩니다
  • 환경: Claude Code의 환경으로 현재 디렉토리에서 실행됩니다
  • 입력: stdin을 통한 JSON
  • 출력:
    • PreToolUse/PostToolUse/Stop: 트랜스크립트에 진행 상황 표시 (Ctrl-R)
    • Notification: 디버그에만 로그 (--debug)

디버깅

훅 문제 해결:

  1. /hooks 메뉴가 구성을 표시하는지 확인
  2. 설정 파일이 유효한 JSON인지 확인
  3. 명령을 수동으로 테스트
  4. 종료 코드 확인
  5. stdout 및 stderr 형식 기대치 검토
  6. 적절한 인용 이스케이프 확인
  7. 훅을 디버그하려면 claude --debug를 사용하세요. 성공한 훅의 출력은 아래와 같이 나타납니다.
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>

진행 메시지는 트랜스크립트 모드(Ctrl-R)에 다음을 표시합니다:

  • 실행 중인 훅
  • 실행되는 명령
  • 성공/실패 상태
  • 출력 또는 오류 메시지