메인 콘텐츠로 건너뛰기
예제가 포함된 빠른 시작 가이드는 Claude Code hooks 시작하기를 참조하세요.

구성

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

구조

Hooks는 matchers로 구성되며, 각 matcher는 여러 hooks를 가질 수 있습니다:
{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here"
          }
        ]
      }
    ]
  }
}
  • matcher: 도구 이름을 일치시키는 패턴, 대소문자 구분 (PreToolUsePostToolUse에만 적용 가능)
    • 단순 문자열은 정확히 일치: Write는 Write 도구만 일치
    • 정규식 지원: Edit|Write 또는 Notebook.*
    • *를 사용하여 모든 도구를 일치시킵니다. 빈 문자열("")을 사용하거나 matcher를 비워둘 수도 있습니다.
  • hooks: 패턴이 일치할 때 실행할 hooks의 배열
    • type: Hook 실행 유형 - bash 명령의 경우 "command" 또는 LLM 기반 평가의 경우 "prompt"
    • command: (type: "command"의 경우) 실행할 bash 명령 ($CLAUDE_PROJECT_DIR 환경 변수 사용 가능)
    • prompt: (type: "prompt"의 경우) LLM 평가를 위해 보낼 프롬프트
    • timeout: (선택 사항) hook이 실행되어야 하는 시간(초 단위), 그 후 특정 hook이 취소됨
UserPromptSubmit, Notification, Stop, SubagentStop과 같이 matchers를 사용하지 않는 이벤트의 경우 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 구성 요소 참조를 참조하세요.

프롬프트 기반 Hooks

bash 명령 hooks (type: "command") 외에도 Claude Code는 프롬프트 기반 hooks (type: "prompt")를 지원하며, 이는 LLM을 사용하여 작업을 허용하거나 차단할지 여부를 평가합니다. 프롬프트 기반 hooks는 현재 StopSubagentStop hooks에만 지원되며, 이를 통해 지능형 상황 인식 결정이 가능합니다.

프롬프트 기반 hooks의 작동 방식

bash 명령을 실행하는 대신 프롬프트 기반 hooks는:
  1. Hook 입력 및 프롬프트를 빠른 LLM (Haiku)으로 보냅니다
  2. LLM은 결정을 포함하는 구조화된 JSON으로 응답합니다
  3. Claude Code는 결정을 자동으로 처리합니다

구성

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
          }
        ]
      }
    ]
  }
}
필드:
  • type: "prompt"이어야 합니다
  • prompt: LLM으로 보낼 프롬프트 텍스트
    • Hook 입력 JSON에 대한 자리 표시자로 $ARGUMENTS를 사용합니다
    • $ARGUMENTS가 없으면 입력 JSON이 프롬프트에 추가됩니다
  • timeout: (선택 사항) 시간 초과(초 단위) (기본값: 30초)

응답 스키마

LLM은 다음을 포함하는 JSON으로 응답해야 합니다:
{
  "decision": "approve" | "block",
  "reason": "Explanation for the decision",
  "continue": false,  // Optional: stops Claude entirely
  "stopReason": "Message shown to user",  // Optional: custom stop message
  "systemMessage": "Warning or context"  // Optional: shown to user
}
응답 필드:
  • decision: "approve"는 작업을 허용하고, "block"은 이를 방지합니다
  • reason: 결정이 "block"일 때 Claude에 표시되는 설명
  • continue: (선택 사항) false이면 Claude의 실행을 완전히 중지합니다
  • stopReason: (선택 사항) continue가 false일 때 표시되는 메시지
  • systemMessage: (선택 사항) 사용자에게 표시되는 추가 메시지

지원되는 hook 이벤트

프롬프트 기반 hooks는 모든 hook 이벤트와 함께 작동하지만 다음에 가장 유용합니다:
  • Stop: Claude가 계속 작업해야 하는지 지능형으로 결정
  • SubagentStop: subagent가 작업을 완료했는지 평가
  • UserPromptSubmit: LLM 지원으로 사용자 프롬프트 검증
  • PreToolUse: 상황 인식 권한 결정

예제: 지능형 Stop hook

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"decision\": \"approve\" or \"block\", \"reason\": \"your explanation\"}",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

예제: 사용자 정의 로직이 있는 SubagentStop

{
  "hooks": {
    "SubagentStop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if this subagent should stop. Input: $ARGUMENTS\n\nCheck if:\n- The subagent completed its assigned task\n- Any errors occurred that need fixing\n- Additional context gathering is needed\n\nReturn: {\"decision\": \"approve\" or \"block\", \"reason\": \"explanation\"}"
          }
        ]
      }
    ]
  }
}

bash 명령 hooks와의 비교

기능Bash 명령 Hooks프롬프트 기반 Hooks
실행bash 스크립트 실행LLM 쿼리
결정 로직코드에서 구현LLM이 상황 평가
설정 복잡성스크립트 파일 필요프롬프트만 구성
상황 인식스크립트 로직으로 제한자연어 이해
성능빠름 (로컬 실행)느림 (API 호출)
사용 사례결정론적 규칙상황 인식 결정

모범 사례

  • 프롬프트에서 구체적으로: LLM이 평가해야 할 사항을 명확하게 명시합니다
  • 결정 기준 포함: LLM이 고려해야 할 요소를 나열합니다
  • 프롬프트 테스트: LLM이 사용 사례에 대해 올바른 결정을 내리는지 확인합니다
  • 적절한 시간 초과 설정: 기본값은 30초이며, 필요에 따라 조정합니다
  • 복잡한 결정에 사용: bash hooks는 간단하고 결정론적인 규칙에 더 적합합니다
Plugin hooks 생성에 대한 자세한 내용은 plugin 구성 요소 참조를 참조하세요.

Hook 이벤트

PreToolUse

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

PostToolUse

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

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 작업을 실행하려고 할 때 실행됩니다. Matchers:
  • manual - /compact에서 호출됨
  • auto - 자동 compact에서 호출됨 (컨텍스트 창이 가득 찼기 때문)

SessionStart

Claude Code가 새 세션을 시작하거나 기존 세션을 재개할 때 실행됩니다 (현재 내부적으로 새 세션을 시작합니다). 기존 이슈나 코드베이스의 최근 변경 사항과 같은 개발 컨텍스트를 로드하거나, 종속성을 설치하거나, 환경 변수를 설정하는 데 유용합니다. Matchers:
  • 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는 사용자에게 표시되고 실행이 계속됩니다.
알림: 종료 코드가 0이면 Claude Code는 stdout을 보지 못합니다. UserPromptSubmit hook은 예외이며, 여기서 stdout은 컨텍스트에 주입됩니다.

종료 코드 2 동작

Hook 이벤트동작
PreToolUse도구 호출을 차단하고 stderr를 Claude에 표시
PostToolUsestderr를 Claude에 표시 (도구가 이미 실행됨)
NotificationN/A, stderr를 사용자에게만 표시
UserPromptSubmit프롬프트 처리를 차단하고, 프롬프트를 지우고, stderr를 사용자에게만 표시
Stop중지를 차단하고 stderr를 Claude에 표시
SubagentStop중지를 차단하고 stderr를 Claude subagent에 표시
PreCompactN/A, stderr를 사용자에게만 표시
SessionStartN/A, stderr를 사용자에게만 표시
SessionEndN/A, stderr를 사용자에게만 표시

고급: JSON 출력

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

공통 JSON 필드

모든 hook 유형은 다음 선택적 필드를 포함할 수 있습니다:
{
  "continue": true, // Hook 실행 후 Claude가 계속해야 하는지 여부 (기본값: true)
  "stopReason": "string", // continue가 false일 때 표시되는 메시지

  "suppressOutput": true, // 트랜스크립트 모드에서 stdout 숨기기 (기본값: false)
  "systemMessage": "string" // 사용자에게 표시되는 선택적 경고 메시지
}
continue가 false이면 hooks 실행 후 Claude는 처리를 중지합니다.
  • 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를 철저히 테스트해야 합니다
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 문자열 내에서 \"를 사용하세요
  • 잘못된 matcher - 도구 이름이 정확히 일치하는지 확인 (대소문자 구분)
  • 명령을 찾을 수 없음 - 스크립트의 전체 경로를 사용하세요

고급 디버깅

복잡한 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
  • 실행 중인 명령
  • 성공/실패 상태
  • 출력 또는 오류 메시지