예제가 포함된 빠른 시작 가이드는 Claude Code hooks 시작하기를 참조하세요.

구성

Claude Code hooks는 설정 파일에서 구성됩니다:
  • ~/.claude/settings.json - 사용자 설정
  • .claude/settings.json - 프로젝트 설정
  • .claude/settings.local.json - 로컬 프로젝트 설정 (커밋되지 않음)
  • 엔터프라이즈 관리 정책 설정

구조

Hooks는 매처로 구성되며, 각 매처는 여러 hooks를 가질 수 있습니다:
{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here"
          }
        ]
      }
    ]
  }
}
  • matcher: 도구 이름을 일치시키는 패턴, 대소문자 구분 (PreToolUsePostToolUse에만 적용 가능)
    • 단순 문자열은 정확히 일치합니다: Write는 Write 도구만 일치합니다
    • 정규식을 지원합니다: Edit|Write 또는 Notebook.*
    • *를 사용하여 모든 도구를 일치시킵니다. 빈 문자열("")을 사용하거나 matcher를 비워둘 수도 있습니다.
  • hooks: 패턴이 일치할 때 실행할 명령 배열
    • type: 현재 "command"만 지원됩니다
    • command: 실행할 bash 명령 ($CLAUDE_PROJECT_DIR 환경 변수를 사용할 수 있음)
    • timeout: (선택 사항) 명령이 실행되어야 하는 시간(초 단위), 초과하면 해당 특정 명령이 취소됩니다.
UserPromptSubmit, Notification, Stop, SubagentStop과 같이 매처를 사용하지 않는 이벤트의 경우 matcher 필드를 생략할 수 있습니다:
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/prompt-validator.py"
          }
        ]
      }
    ]
  }
}

프로젝트별 Hook 스크립트

CLAUDE_PROJECT_DIR 환경 변수(Claude Code가 hook 명령을 생성할 때만 사용 가능)를 사용하여 프로젝트에 저장된 스크립트를 참조할 수 있으므로, Claude의 현재 디렉토리와 관계없이 작동합니다:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"
          }
        ]
      }
    ]
  }
}

Plugin hooks

Plugins는 사용자 및 프로젝트 hooks와 원활하게 통합되는 hooks를 제공할 수 있습니다. Plugin hooks는 plugins이 활성화될 때 자동으로 구성과 병합됩니다. Plugin hooks의 작동 방식:
  • Plugin hooks는 plugin의 hooks/hooks.json 파일 또는 hooks 필드에 대한 사용자 정의 경로로 지정된 파일에서 정의됩니다.
  • Plugin이 활성화되면 해당 hooks가 사용자 및 프로젝트 hooks와 병합됩니다
  • 다양한 소스의 여러 hooks가 동일한 이벤트에 응답할 수 있습니다
  • Plugin hooks는 ${CLAUDE_PLUGIN_ROOT} 환경 변수를 사용하여 plugin 파일을 참조합니다
예제 plugin hook 구성:
{
  "description": "Automatic code formatting",
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
Plugin hooks는 hook의 목적을 설명하는 선택적 description 필드가 있는 일반 hooks와 동일한 형식을 사용합니다.
Plugin hooks는 사용자 정의 hooks와 함께 실행됩니다. 여러 hooks가 이벤트와 일치하면 모두 병렬로 실행됩니다.
Plugins용 환경 변수:
  • ${CLAUDE_PLUGIN_ROOT}: Plugin 디렉토리의 절대 경로
  • ${CLAUDE_PROJECT_DIR}: 프로젝트 루트 디렉토리 (프로젝트 hooks와 동일)
  • 모든 표준 환경 변수를 사용할 수 있습니다
Plugin hooks 생성에 대한 자세한 내용은 plugin 구성 요소 참조를 참조하세요.

Hook 이벤트

PreToolUse

Claude가 도구 매개변수를 생성한 후 도구 호출을 처리하기 전에 실행됩니다. 일반적인 매처:
  • Task - Subagent 작업 (subagents 문서 참조)
  • Bash - 셸 명령
  • Glob - 파일 패턴 일치
  • Grep - 콘텐츠 검색
  • Read - 파일 읽기
  • Edit - 파일 편집
  • Write - 파일 쓰기
  • WebFetch, WebSearch - 웹 작업

PostToolUse

도구가 성공적으로 완료된 직후 실행됩니다. PreToolUse와 동일한 매처 값을 인식합니다.

Notification

Claude Code가 알림을 보낼 때 실행됩니다. 알림은 다음과 같은 경우에 전송됩니다:
  1. Claude가 도구를 사용할 수 있는 권한이 필요합니다. 예: “Claude needs your permission to use Bash”
  2. 프롬프트 입력이 최소 60초 동안 유휴 상태입니다. “Claude is waiting for your input”

UserPromptSubmit

사용자가 프롬프트를 제출할 때 Claude가 처리하기 전에 실행됩니다. 이를 통해 프롬프트/대화에 따라 추가 컨텍스트를 추가하거나, 프롬프트를 검증하거나, 특정 유형의 프롬프트를 차단할 수 있습니다.

Stop

주 Claude Code 에이전트가 응답을 완료했을 때 실행됩니다. 사용자 인터럽트로 인해 중지가 발생한 경우에는 실행되지 않습니다.

SubagentStop

Claude Code subagent (Task 도구 호출)가 응답을 완료했을 때 실행됩니다.

PreCompact

Claude Code가 compact 작업을 실행하려고 할 때 실행됩니다. 매처:
  • manual - /compact에서 호출됨
  • auto - 자동 compact에서 호출됨 (전체 컨텍스트 윈도우로 인해)

SessionStart

Claude Code가 새 세션을 시작하거나 기존 세션을 재개할 때 실행됩니다 (현재 내부적으로 새 세션을 시작합니다). 기존 문제나 코드베이스의 최근 변경 사항과 같은 개발 컨텍스트를 로드하거나, 종속성을 설치하거나, 환경 변수를 설정하는 데 유용합니다. 매처:
  • startup - 시작에서 호출됨
  • resume - --resume, --continue 또는 /resume에서 호출됨
  • clear - /clear에서 호출됨
  • compact - 자동 또는 수동 compact에서 호출됨.

환경 변수 유지

SessionStart hooks는 CLAUDE_ENV_FILE 환경 변수에 액세스할 수 있으며, 이는 후속 bash 명령에 대한 환경 변수를 유지할 수 있는 파일 경로를 제공합니다. 예제: 개별 환경 변수 설정
#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export API_KEY=your-api-key' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0
예제: Hook의 모든 환경 변경 유지 설정이 환경을 수정할 때 (예: nvm use), 환경을 비교하여 모든 변경 사항을 캡처하고 유지합니다:
#!/bin/bash

ENV_BEFORE=$(export -p | sort)

# Run your setup commands that modify the environment
source ~/.nvm/nvm.sh
nvm use 20

if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0
이 파일에 작성된 모든 변수는 세션 중에 Claude Code가 실행하는 모든 후속 bash 명령에서 사용할 수 있습니다.
CLAUDE_ENV_FILE은 SessionStart hooks에서만 사용 가능합니다. 다른 hook 유형은 이 변수에 액세스할 수 없습니다.

SessionEnd

Claude Code 세션이 종료될 때 실행됩니다. 정리 작업, 세션 통계 로깅 또는 세션 상태 저장에 유용합니다. Hook 입력의 reason 필드는 다음 중 하나입니다:
  • clear - /clear 명령으로 세션이 지워짐
  • logout - 사용자가 로그아웃함
  • prompt_input_exit - 프롬프트 입력이 표시되는 동안 사용자가 종료함
  • other - 기타 종료 이유

Hook 입력

Hooks는 stdin을 통해 세션 정보 및 이벤트별 데이터를 포함하는 JSON 데이터를 수신합니다:
{
  // Common fields
  session_id: string
  transcript_path: string  // Path to conversation JSON
  cwd: string              // The current working directory when the hook is invoked
  permission_mode: string  // Current permission mode: "default", "plan", "acceptEdits", or "bypassPermissions"

  // Event-specific fields
  hook_event_name: string
  ...
}

PreToolUse 입력

tool_input의 정확한 스키마는 도구에 따라 다릅니다.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  }
}

PostToolUse 입력

tool_inputtool_response의 정확한 스키마는 도구에 따라 다릅니다.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "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": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Notification",
  "message": "Task completed successfully"
}

UserPromptSubmit 입력

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

Stop 및 SubagentStop 입력

stop_hook_active는 Claude Code가 이미 stop hook의 결과로 계속되고 있을 때 true입니다. 이 값을 확인하거나 트랜스크립트를 처리하여 Claude Code가 무한정 실행되지 않도록 합니다.
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}

PreCompact 입력

manual의 경우 custom_instructions는 사용자가 /compact에 전달하는 것에서 나옵니다. auto의 경우 custom_instructions는 비어 있습니다.
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "permission_mode": "default",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}

SessionStart 입력

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "permission_mode": "default",
  "hook_event_name": "SessionStart",
  "source": "startup"
}

SessionEnd 입력

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SessionEnd",
  "reason": "exit"
}

Hook 출력

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

단순: 종료 코드

Hooks는 종료 코드, stdout 및 stderr를 통해 상태를 전달합니다:
  • 종료 코드 0: 성공. stdout은 트랜스크립트 모드(CTRL-R)에서 사용자에게 표시되며, UserPromptSubmitSessionStart는 제외하고 stdout이 컨텍스트에 추가됩니다.
  • 종료 코드 2: 차단 오류. stderr는 Claude에 피드백되어 자동으로 처리됩니다. 아래의 hook 이벤트별 동작을 참조하세요.
  • 기타 종료 코드: 차단되지 않는 오류. stderr는 사용자에게 표시되고 실행이 계속됩니다.
알림: Claude Code는 종료 코드가 0인 경우 stdout을 보지 않습니다. 단, UserPromptSubmit hook은 stdout이 컨텍스트에 주입되는 경우는 제외합니다.

종료 코드 2 동작

Hook 이벤트동작
PreToolUse도구 호출을 차단하고 stderr를 Claude에 표시
PostToolUsestderr를 Claude에 표시 (도구가 이미 실행됨)
Notification해당 없음, stderr를 사용자에게만 표시
UserPromptSubmit프롬프트 처리를 차단하고 프롬프트를 지우며 stderr를 사용자에게만 표시
Stop중지를 차단하고 stderr를 Claude에 표시
SubagentStop중지를 차단하고 stderr를 Claude subagent에 표시
PreCompact해당 없음, stderr를 사용자에게만 표시
SessionStart해당 없음, stderr를 사용자에게만 표시
SessionEnd해당 없음, stderr를 사용자에게만 표시

고급: JSON 출력

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

공통 JSON 필드

모든 hook 유형은 다음 선택적 필드를 포함할 수 있습니다:
{
  "continue": true, // Whether Claude should continue after hook execution (default: true)
  "stopReason": "string", // Message shown when continue is false

  "suppressOutput": true, // Hide stdout from transcript mode (default: false)
  "systemMessage": "string" // Optional warning message shown to the user
}
continue가 false인 경우 Claude는 hooks 실행 후 처리를 중지합니다.
  • PreToolUse의 경우 이는 "permissionDecision": "deny"와 다릅니다. 이는 특정 도구 호출만 차단하고 Claude에 자동 피드백을 제공합니다.
  • PostToolUse의 경우 이는 "decision": "block"과 다릅니다. 이는 Claude에 자동 피드백을 제공합니다.
  • UserPromptSubmit의 경우 이는 프롬프트가 처리되지 않도록 합니다.
  • StopSubagentStop의 경우 이는 모든 "decision": "block" 출력보다 우선합니다.
  • 모든 경우에 "continue" = false는 모든 "decision": "block" 출력보다 우선합니다.
stopReasoncontinue와 함께 사용자에게 표시되는 이유를 제공하며 Claude에는 표시되지 않습니다.

PreToolUse 결정 제어

PreToolUse hooks는 도구 호출 진행 여부를 제어할 수 있습니다.
  • "allow"는 권한 시스템을 우회합니다. permissionDecisionReason은 사용자에게 표시되지만 Claude에는 표시되지 않습니다.
  • "deny"는 도구 호출 실행을 방지합니다. permissionDecisionReason은 Claude에 표시됩니다.
  • "ask"는 사용자에게 UI에서 도구 호출을 확인하도록 요청합니다. permissionDecisionReason은 사용자에게 표시되지만 Claude에는 표시되지 않습니다.
또한 hooks는 updatedInput을 사용하여 실행 전에 도구 입력을 수정할 수 있습니다:
  • updatedInput을 사용하면 도구가 실행되기 전에 도구의 입력 매개변수를 수정할 수 있습니다. 이는 변경하거나 추가할 필드를 포함하는 Record<string, unknown> 객체입니다.
  • 이는 "permissionDecision": "allow"와 함께 도구 호출을 수정하고 승인하는 데 가장 유용합니다.
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow"
    "permissionDecisionReason": "My reason here",
    "updatedInput": {
      "field_to_modify": "new value"
    }
  }
}
decisionreason 필드는 PreToolUse hooks에서 더 이상 사용되지 않습니다. 대신 hookSpecificOutput.permissionDecisionhookSpecificOutput.permissionDecisionReason을 사용하세요. 더 이상 사용되지 않는 필드 "approve""block"은 각각 "allow""deny"로 매핑됩니다.

PostToolUse 결정 제어

PostToolUse hooks는 도구 실행 후 Claude에 피드백을 제공할 수 있습니다.
  • "block"은 자동으로 Claude에 reason을 프롬프트합니다.
  • undefined는 아무것도 하지 않습니다. reason은 무시됩니다.
  • "hookSpecificOutput.additionalContext"는 Claude가 고려할 컨텍스트를 추가합니다.
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude"
  }
}

UserPromptSubmit 결정 제어

UserPromptSubmit hooks는 사용자 프롬프트 처리 여부를 제어할 수 있습니다.
  • "block"은 프롬프트가 처리되지 않도록 합니다. 제출된 프롬프트는 컨텍스트에서 지워집니다. "reason"은 사용자에게 표시되지만 컨텍스트에 추가되지 않습니다.
  • undefined는 프롬프트가 정상적으로 진행되도록 합니다. "reason"은 무시됩니다.
  • "hookSpecificOutput.additionalContext"는 차단되지 않은 경우 컨텍스트에 문자열을 추가합니다.
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here"
  }
}

Stop/SubagentStop 결정 제어

StopSubagentStop hooks는 Claude가 계속해야 하는지 여부를 제어할 수 있습니다.
  • "block"은 Claude가 중지되지 않도록 합니다. Claude가 진행 방법을 알 수 있도록 reason을 채워야 합니다.
  • undefined는 Claude가 중지되도록 합니다. reason은 무시됩니다.
{
  "decision": "block" | undefined,
  "reason": "Must be provided when Claude is blocked from stopping"
}

SessionStart 결정 제어

SessionStart hooks를 사용하면 세션 시작 시 컨텍스트를 로드할 수 있습니다.
  • "hookSpecificOutput.additionalContext"는 컨텍스트에 문자열을 추가합니다.
  • 여러 hooks의 additionalContext 값이 연결됩니다.
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "My additional context here"
  }
}

SessionEnd 결정 제어

SessionEnd hooks는 세션이 종료될 때 실행됩니다. 세션 종료를 차단할 수 없지만 정리 작업을 수행할 수 있습니다.

종료 코드 예제: Bash 명령 검증

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

# Define validation rules as a list of (regex pattern, message) tuples
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "Use 'rg' (ripgrep) instead of 'grep' for better performance and features",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance",
    ),
]


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"Error: Invalid JSON input: {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)

# Validate the command
issues = validate_command(command)

if issues:
    for message in issues:
        print(f"• {message}", file=sys.stderr)
    # Exit code 2 blocks tool call and shows stderr to Claude
    sys.exit(2)

JSON 출력 예제: 컨텍스트 추가 및 검증을 위한 UserPromptSubmit

UserPromptSubmit hooks의 경우 다음 두 가지 방법 중 하나를 사용하여 컨텍스트를 주입할 수 있습니다:
  • 종료 코드 0 및 stdout: Claude가 컨텍스트를 봅니다 (UserPromptSubmit의 특수한 경우)
  • JSON 출력: 동작에 대한 더 많은 제어를 제공합니다
#!/usr/bin/env python3
import json
import sys
import re
import datetime

# Load input from stdin
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

prompt = input_data.get("prompt", "")

# Check for sensitive patterns
sensitive_patterns = [
    (r"(?i)\b(password|secret|key|token)\s*[:=]", "Prompt contains potential secrets"),
]

for pattern, message in sensitive_patterns:
    if re.search(pattern, prompt):
        # Use JSON output to block with a specific reason
        output = {
            "decision": "block",
            "reason": f"Security policy violation: {message}. Please rephrase your request without sensitive information."
        }
        print(json.dumps(output))
        sys.exit(0)

# Add current time to context
context = f"Current time: {datetime.datetime.now()}"
print(context)

"""
The following is also equivalent:
print(json.dumps({
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": context,
  },
}))
"""

# Allow the prompt to proceed with the additional context
sys.exit(0)

JSON 출력 예제: 승인이 있는 PreToolUse

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

# Load input from stdin
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

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

# Example: Auto-approve file reads for documentation files
if tool_name == "Read":
    file_path = tool_input.get("file_path", "")
    if file_path.endswith((".md", ".mdx", ".txt", ".json")):
        # Use JSON output to auto-approve the tool call
        output = {
            "decision": "approve",
            "reason": "Documentation file auto-approved",
            "suppressOutput": True  # Don't show in transcript mode
        }
        print(json.dumps(output))
        sys.exit(0)

# For other cases, let the normal permission flow proceed
sys.exit(0)

MCP 도구 작업

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

MCP 도구 명명

MCP 도구는 mcp__<server>__<tool> 패턴을 따릅니다. 예를 들어:
  • mcp__memory__create_entities - Memory 서버의 create entities 도구
  • mcp__filesystem__read_file - Filesystem 서버의 read file 도구
  • mcp__github__search_repositories - GitHub 서버의 search 도구

MCP 도구에 대한 Hooks 구성

특정 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"
          }
        ]
      }
    ]
  }
}

예제

코드 포맷팅, 알림 및 파일 보호를 포함한 실제 예제는 시작 가이드의 더 많은 예제를 참조하세요.

보안 고려 사항

면책 조항

자신의 책임하에 사용: Claude Code hooks는 시스템에서 자동으로 임의의 셸 명령을 실행합니다. Hooks를 사용함으로써 다음을 인정합니다:
  • 구성한 명령에 대해 전적으로 책임이 있습니다
  • Hooks는 사용자 계정이 액세스할 수 있는 모든 파일을 수정, 삭제 또는 액세스할 수 있습니다
  • 악의적이거나 잘못 작성된 hooks는 데이터 손실이나 시스템 손상을 초래할 수 있습니다
  • Anthropic은 어떤 보증도 제공하지 않으며 hook 사용으로 인한 손상에 대해 책임을 지지 않습니다
  • 프로덕션 사용 전에 안전한 환경에서 hooks를 철저히 테스트해야 합니다
구성에 추가하기 전에 모든 hook 명령을 검토하고 이해하세요.

보안 모범 사례

더 안전한 hooks를 작성하기 위한 주요 사례는 다음과 같습니다:
  1. 입력 검증 및 살균 - 입력 데이터를 맹목적으로 신뢰하지 마세요
  2. 항상 셸 변수를 인용하세요 - $VAR이 아닌 "$VAR"을 사용하세요
  3. 경로 순회 차단 - 파일 경로에서 ..를 확인하세요
  4. 절대 경로 사용 - 스크립트의 전체 경로를 지정하세요 (프로젝트 경로에는 “$CLAUDE_PROJECT_DIR” 사용)
  5. 민감한 파일 건너뛰기 - .env, .git/, 키 등을 피하세요

구성 안전

설정 파일의 hooks에 대한 직접 편집은 즉시 적용되지 않습니다. Claude Code는:
  1. 시작 시 hooks의 스냅샷을 캡처합니다
  2. 세션 전체에서 이 스냅샷을 사용합니다
  3. Hooks가 외부에서 수정되면 경고합니다
  4. 변경 사항을 적용하려면 /hooks 메뉴에서 검토가 필요합니다
이는 악의적인 hook 수정이 현재 세션에 영향을 미치지 않도록 합니다.

Hook 실행 세부 사항

  • 시간 제한: 기본적으로 60초 실행 제한, 명령당 구성 가능합니다.
    • 개별 명령의 시간 제한은 다른 명령에 영향을 주지 않습니다.
  • 병렬화: 모든 일치하는 hooks가 병렬로 실행됩니다
  • 중복 제거: 동일한 hook 명령이 자동으로 중복 제거됩니다
  • 환경: 현재 디렉토리에서 Claude Code의 환경으로 실행됩니다
    • CLAUDE_PROJECT_DIR 환경 변수를 사용할 수 있으며 프로젝트 루트 디렉토리의 절대 경로를 포함합니다 (Claude Code가 시작된 위치)
    • CLAUDE_CODE_REMOTE 환경 변수는 hook이 원격 (웹) 환경에서 실행 중인지 ("true") 또는 로컬 CLI 환경에서 실행 중인지 (설정되지 않음 또는 비어 있음)를 나타냅니다. 이를 사용하여 실행 컨텍스트에 따라 다른 로직을 실행하세요.
  • 입력: stdin을 통한 JSON
  • 출력:
    • PreToolUse/PostToolUse/Stop/SubagentStop: 트랜스크립트에 표시되는 진행 상황 (Ctrl-R)
    • Notification/SessionEnd: 디버그에만 로깅됨 (--debug)
    • UserPromptSubmit/SessionStart: Claude의 컨텍스트로 추가된 stdout

디버깅

기본 문제 해결

Hooks가 작동하지 않는 경우:
  1. 구성 확인 - /hooks를 실행하여 hook이 등록되었는지 확인하세요
  2. 구문 확인 - JSON 설정이 유효한지 확인하세요
  3. 명령 테스트 - 먼저 hook 명령을 수동으로 실행하세요
  4. 권한 확인 - 스크립트가 실행 가능한지 확인하세요
  5. 로그 검토 - claude --debug를 사용하여 hook 실행 세부 사항을 확인하세요
일반적인 문제:
  • 이스케이프되지 않은 따옴표 - JSON 문자열 내에서 \"를 사용하세요
  • 잘못된 매처 - 도구 이름이 정확히 일치하는지 확인하세요 (대소문자 구분)
  • 명령을 찾을 수 없음 - 스크립트의 전체 경로를 사용하세요

고급 디버깅

복잡한 hook 문제의 경우:
  1. Hook 실행 검사 - claude --debug를 사용하여 자세한 hook 실행을 확인하세요
  2. JSON 스키마 검증 - 외부 도구로 hook 입력/출력을 테스트하세요
  3. 환경 변수 확인 - Claude Code의 환경이 올바른지 확인하세요
  4. 엣지 케이스 테스트 - 비정상적인 파일 경로 또는 입력으로 hooks를 시도하세요
  5. 시스템 리소스 모니터링 - Hook 실행 중 리소스 소진을 확인하세요
  6. 구조화된 로깅 사용 - Hook 스크립트에 로깅을 구현하세요

디버그 출력 예제

claude --debug를 사용하여 hook 실행 세부 사항을 확인하세요:
[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)에 나타나며 다음을 표시합니다:
  • 실행 중인 hook
  • 실행 중인 명령
  • 성공/실패 상태
  • 출력 또는 오류 메시지