본문 바로가기
클로드 아카데미클로드 아카데미
이 글은 Anthropic의 공개 자료를 한국어로 옮긴 비공식 커뮤니티 번역입니다. 원문: Claude Code hooks

Claude Code 훅(Hooks)

Claude Code가 파일을 편집하거나, 작업을 끝내거나, 입력을 기다릴 때 셸 명령을 자동으로 실행하세요. 코드 포매팅, 알림 전송, 명령 검증, 프로젝트 규칙 강제를 할 수 있습니다.

훅(hook)은 Claude Code 라이프사이클의 특정 시점에 실행되는 사용자 정의 셸 명령입니다. Claude Code의 동작을 결정론적으로 제어할 수 있게 해 주며, 특정 동작을 LLM이 실행하기로 "선택"하기를 기다리는 대신 항상 일어나도록 보장합니다. 훅을 활용하면 프로젝트 규칙을 강제하고, 반복 작업을 자동화하며, Claude Code를 기존 도구와 통합할 수 있습니다.

결정론적 규칙이 아니라 판단이 필요한 결정의 경우에는, Claude 모델로 조건을 평가하는 프롬프트 기반 훅이나 에이전트 기반 훅을 사용할 수도 있습니다.

Claude Code를 확장하는 다른 방법으로는, Claude에게 추가 지침과 실행 가능한 명령을 제공하는 스킬, 격리된 컨텍스트에서 작업을 실행하는 서브에이전트, 확장 기능을 패키징해 프로젝트 간에 공유하는 플러그인 등이 있습니다.

이 가이드는 흔한 사용 사례와 시작 방법을 다룹니다. 전체 이벤트 스키마, JSON 입출력 형식, 비동기 훅이나 MCP 도구 훅 같은 고급 기능은 Hooks 레퍼런스를 참고하세요.

첫 번째 훅 설정하기

훅을 만들려면 설정 파일에 hooks 블록을 추가합니다. 이 안내에서는 데스크톱 알림 훅을 만들어, 터미널을 계속 지켜보지 않아도 Claude가 입력을 기다릴 때마다 알림을 받도록 합니다.

설정에 훅 추가하기

~/.claude/settings.json을 열고 Notification 훅을 추가하세요. 아래 예시는 macOS용 osascript를 사용합니다. Linux와 Windows 명령은 "Claude가 입력을 필요로 할 때 알림 받기" 절을 참고하세요.

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

설정 파일에 이미 hooks 키가 있다면, 전체 객체를 통째로 덮어쓰지 말고 Notification을 기존 이벤트 키들과 형제(sibling) 항목으로 추가하세요. 각 이벤트 이름은 하나의 hooks 객체 안에 들어가는 키입니다.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
      }
    ],
    "Notification": [
      {
        "matcher": "",
        "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'" }]
      }
    ]
  }
}

원하는 동작을 CLI에서 설명해 Claude에게 훅을 대신 작성하게 할 수도 있습니다.

설정 확인하기

/hooks를 입력하면 훅 브라우저가 열립니다. 사용 가능한 모든 훅 이벤트 목록과, 훅이 설정된 각 이벤트 옆에 개수가 표시됩니다. Notification을 선택해 새로 만든 훅이 목록에 나타나는지 확인하세요. 훅을 선택하면 이벤트, 매처(matcher), 타입, 소스 파일, 명령 등 상세 정보가 표시됩니다.

훅 테스트하기

Esc를 눌러 CLI로 돌아갑니다. Claude에게 권한이 필요한 작업을 시킨 다음 터미널에서 다른 창으로 전환해 보세요. 데스크톱 알림이 도착할 것입니다.

/hooks 메뉴는 읽기 전용입니다. 훅을 추가, 수정, 삭제하려면 설정 JSON을 직접 편집하거나 Claude에게 변경을 요청하세요.

자동화할 수 있는 것들

훅을 사용하면 Claude Code 라이프사이클의 핵심 지점에서 코드를 실행할 수 있습니다. 편집 후 파일 포매팅, 실행 전 명령 차단, Claude가 입력을 필요로 할 때 알림 전송, 세션 시작 시 컨텍스트 주입 등 다양한 일이 가능합니다. 전체 훅 이벤트 목록은 Hooks 레퍼런스를 참고하세요.

각 예시에는 설정 파일에 바로 추가할 수 있는 구성 블록이 포함되어 있습니다. 가장 흔한 패턴은 다음과 같습니다.

  • Claude가 입력을 필요로 할 때 알림 받기
  • 편집 후 코드 자동 포매팅
  • 보호된 파일에 대한 편집 차단
  • 압축(compaction) 이후 컨텍스트 다시 주입
  • 설정 변경 감사(audit)
  • 디렉터리나 파일이 바뀔 때 환경 다시 로드
  • 특정 권한 프롬프트 자동 승인

별도 모델 리뷰를 실행하고 그 결과를 세션에 다시 반영하는 훅의 실제 사례는, security-guidance 플러그인이 Claude Code와 통합되는 방식을 참고하세요.

Claude가 입력을 필요로 할 때 알림 받기

Claude가 작업을 마치고 입력을 필요로 할 때마다 데스크톱 알림을 받아, 터미널을 확인하지 않고도 다른 작업으로 전환할 수 있습니다.

이 훅은 Notification 이벤트를 사용합니다. 이 이벤트는 Claude가 입력이나 권한을 기다릴 때 발생합니다. 아래 각 탭은 플랫폼의 기본 알림 명령을 사용합니다. 이것을 ~/.claude/settings.json에 추가하세요.

macOS

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

알림이 나타나지 않는다면

osascript는 내장 Script Editor 앱을 통해 알림을 전달합니다. Script Editor에 알림 권한이 없으면 명령은 조용히 실패하고, macOS는 권한을 부여하라는 안내를 표시하지 않습니다. Script Editor가 알림 설정에 나타나도록 터미널에서 다음을 한 번 실행하세요.

osascript -e 'display notification "test"'

아직 아무것도 나타나지 않습니다. 시스템 설정 > 알림을 열고 목록에서 Script Editor를 찾아 "알림 허용"을 켜세요. 다시 명령을 실행해 테스트 알림이 나타나는지 확인하세요.

Linux

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
          }
        ]
      }
    ]
  }
}

Windows (PowerShell)

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
          }
        ]
      }
    ]
  }
}

matcher는 모든 알림 유형에 대해 발생합니다. 특정 이벤트에만 발생시키려면 다음 값 중 하나로 설정하세요.

Matcher 발생 시점
permission_prompt Claude가 도구 사용 승인을 필요로 할 때
idle_prompt Claude가 작업을 마치고 다음 프롬프트를 기다릴 때
auth_success 인증이 완료될 때
elicitation_dialog MCP 서버가 요청(elicitation) 양식을 열 때
elicitation_complete MCP 요청 양식이 제출되거나 닫힐 때
elicitation_response MCP 요청 응답이 서버로 다시 전송될 때

/hooks를 입력하고 Notification을 선택해 훅이 등록되었는지 확인하세요. 전체 이벤트 스키마는 Notification 레퍼런스를 참고하세요.

편집 후 코드 자동 포매팅

Claude가 편집하는 모든 파일에 자동으로 Prettier를 실행해, 수동 개입 없이 포매팅을 일관되게 유지합니다.

이 훅은 Edit|Write 매처와 함께 PostToolUse 이벤트를 사용하므로, 파일 편집 도구 이후에만 실행됩니다. Claude Code v2.1.191 이상에서는 매처를 Edit,Write로 작성할 수도 있습니다. 해당 버전에서는 도구 이름 매처에 한해 |,가 서로 바꿔 쓸 수 있는 리스트 구분자이기 때문입니다. 이 명령은 jq로 편집된 파일 경로를 추출해 Prettier에 전달합니다. 이것을 프로젝트 루트의 .claude/settings.json에 추가하세요.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

이 페이지의 Bash 예시는 JSON 파싱에 jq를 사용합니다. brew install jq(macOS), apt-get install jq(Debian/Ubuntu)로 설치하거나 jq 다운로드 페이지를 참고하세요.

보호된 파일에 대한 편집 차단

Claude가 .env, package-lock.json, .git/ 내부 등 민감한 파일을 수정하지 못하도록 막습니다. Claude는 편집이 차단된 이유에 대한 피드백을 받으므로, 접근 방식을 조정할 수 있습니다.

이 예시는 훅이 호출하는 별도의 스크립트 파일을 사용합니다. 스크립트는 대상 파일 경로를 보호 패턴 목록과 대조하고, 일치하면 종료 코드 2로 종료해 편집을 차단합니다.

훅 스크립트 만들기

다음 내용을 .claude/hooks/protect-files.sh에 저장하세요.

#!/bin/bash
# protect-files.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

exit 0

스크립트를 실행 가능하게 만들기 (macOS/Linux)

Claude Code가 훅 스크립트를 실행하려면 스크립트가 실행 가능해야 합니다.

chmod +x .claude/hooks/protect-files.sh

훅 등록하기

모든 Edit 또는 Write 도구 호출 전에 스크립트를 실행하는 PreToolUse 훅을 .claude/settings.json에 추가하세요.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

압축 이후 컨텍스트 다시 주입하기

Claude의 컨텍스트 윈도가 가득 차면, 압축(compaction)이 대화를 요약해 공간을 확보합니다. 이 과정에서 중요한 세부 사항이 사라질 수 있습니다. compact 매처와 함께 SessionStart 훅을 사용하면, 매 압축 이후 핵심 컨텍스트를 다시 주입할 수 있습니다.

명령이 stdout으로 출력하는 텍스트는 모두 Claude의 컨텍스트에 추가됩니다. 아래 예시는 프로젝트 규약과 최근 작업을 Claude에게 다시 상기시킵니다. 이것을 프로젝트 루트의 .claude/settings.json에 추가하세요.

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
          }
        ]
      }
    ]
  }
}

echogit log --oneline -5처럼 동적 출력을 만들어 내는 다른 명령으로 바꿀 수 있습니다(예: 최근 커밋 표시). 모든 세션 시작 시점에 컨텍스트를 주입하려면 CLAUDE.md 사용을 고려하세요. 환경 변수에 대해서는 레퍼런스의 CLAUDE_ENV_FILE을 참고하세요.

설정 변경 감사하기

세션 중 설정이나 스킬 파일이 언제 변경되는지 추적합니다. ConfigChange 이벤트는 외부 프로세스나 에디터가 설정 파일을 수정할 때 발생하므로, 규정 준수를 위해 변경 사항을 기록하거나 허가되지 않은 수정을 차단할 수 있습니다.

아래 예시는 각 변경 사항을 감사 로그에 추가합니다. 이것을 ~/.claude/settings.json에 추가하세요.

{
  "hooks": {
    "ConfigChange": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
          }
        ]
      }
    ]
  }
}

매처는 설정 유형으로 필터링합니다. user_settings, project_settings, local_settings, policy_settings, skills 중 하나입니다. 변경이 적용되지 않게 차단하려면 종료 코드 2로 종료하거나 {"decision": "block"}을 반환하세요. 전체 입력 스키마는 ConfigChange 레퍼런스를 참고하세요.

디렉터리나 파일이 바뀔 때 환경 다시 로드하기

어떤 프로젝트는 현재 디렉터리에 따라 서로 다른 환경 변수를 설정합니다. direnv 같은 도구는 셸에서 이를 자동으로 처리하지만, Claude의 Bash 도구는 그런 변경을 스스로 반영하지 못합니다.

SessionStart 훅과 CwdChanged 훅을 함께 쓰면 이 문제가 해결됩니다. SessionStart는 실행을 시작한 디렉터리의 변수를 로드하고, CwdChanged는 Claude가 디렉터리를 바꿀 때마다 변수를 다시 로드합니다. 둘 다 CLAUDE_ENV_FILE에 기록하는데, Claude Code는 매 Bash 명령 전에 이 파일을 스크립트 프리앰블(preamble)로 실행합니다. 이것을 ~/.claude/settings.json에 추가하세요.

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
          }
        ]
      }
    ],
    "CwdChanged": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
          }
        ]
      }
    ]
  }
}

.envrc가 있는 각 디렉터리에서 direnv allow를 한 번 실행해, direnv가 해당 파일을 로드하도록 허용하세요. direnv 대신 devbox나 nix를 사용한다면, direnv export bash 자리에 devbox shellenvdevbox global shellenv를 넣어 같은 패턴으로 동작시킬 수 있습니다.

모든 디렉터리 변경 대신 특정 파일에 반응하게 하려면, 감시할 파일 이름들을 |로 구분해 나열하는 matcher와 함께 FileChanged를 사용하세요. 감시 목록을 만들 때 이 값은 정규식으로 평가되지 않고 문자 그대로의 파일 이름으로 분리됩니다. 같은 값이 파일 변경 시 어떤 훅 그룹을 실행할지도 함께 필터링하는 방식은 FileChanged를 참고하세요. 아래 예시는 작업 디렉터리의 .envrc.env를 감시합니다.

{
  "hooks": {
    "FileChanged": [
      {
        "matcher": ".envrc|.env",
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
          }
        ]
      }
    ]
  }
}

입력 스키마, watchPaths 출력, CLAUDE_ENV_FILE 세부 사항은 CwdChanged 및 FileChanged 레퍼런스 항목을 참고하세요.

특정 권한 프롬프트 자동 승인하기

항상 허용하는 도구 호출에 대해 승인 대화 상자를 건너뜁니다. 아래 예시는 ExitPlanMode를 자동 승인합니다. 이 도구는 Claude가 계획 제시를 마치고 진행 승인을 요청할 때 호출하는 도구로, 계획이 준비될 때마다 매번 묻지 않도록 합니다.

위의 종료 코드 예시들과 달리, 자동 승인은 훅이 JSON 결정을 stdout으로 출력해야 합니다. PermissionRequest 훅은 Claude Code가 권한 대화 상자를 표시하려 할 때 발생하며, "behavior": "allow"를 반환하면 사용자를 대신해 그 대화 상자에 응답합니다.

매처가 훅 범위를 ExitPlanMode로만 한정하므로 다른 프롬프트에는 영향을 주지 않습니다. 이것을 ~/.claude/settings.json에 추가하세요.

{
  "hooks": {
    "PermissionRequest": [
      {
        "matcher": "ExitPlanMode",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
          }
        ]
      }
    ]
  }
}

훅이 승인하면, Claude Code는 계획 모드(plan mode)를 빠져나와 계획 모드에 들어가기 전에 활성화되어 있던 권한 모드를 복원합니다. 트랜스크립트에는 대화 상자가 표시되었을 자리에 "Allowed by PermissionRequest hook"이 표시됩니다. 훅 경로는 항상 현재 대화를 유지합니다. 대화 상자처럼 컨텍스트를 비우고 새 구현 세션을 시작하는 일은 할 수 없습니다.

대신 특정 권한 모드를 설정하려면, 훅의 출력에 setMode 항목이 든 updatedPermissions 배열을 포함할 수 있습니다. mode 값은 default, acceptEdits, bypassPermissions 같은 임의의 권한 모드이며, destination: "session"은 현재 세션에만 적용합니다.

bypassPermissions는 세션이 이미 바이패스 모드를 사용할 수 있는 상태로 실행된 경우에만 적용됩니다. 즉 --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions, 또는 설정의 permissions.defaultMode: "bypassPermissions"로 실행되었고, permissions.disableBypassPermissionsMode로 비활성화되지 않은 경우입니다. 또한 절대 defaultMode로 영구 저장되지 않습니다.

세션을 acceptEdits로 전환하려면, 훅이 다음 JSON을 stdout으로 출력합니다.

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedPermissions": [
        { "type": "setMode", "mode": "acceptEdits", "destination": "session" }
      ]
    }
  }
}

매처는 가능한 한 좁게 유지하세요. .*에 매칭하거나 매처를 비워 두면 파일 쓰기와 셸 명령을 포함한 모든 권한 프롬프트가 자동 승인됩니다. 전체 결정 필드는 PermissionRequest 레퍼런스를 참고하세요.

훅의 동작 방식

훅 이벤트는 Claude Code의 특정 라이프사이클 시점에 발생합니다. 이벤트가 발생하면 일치하는 모든 훅이 병렬로 실행되며, 동일한 훅 명령은 자동으로 중복 제거됩니다. 아래 표는 각 이벤트와 그 발생 시점을 보여 줍니다.

이벤트 발생 시점
SessionStart 세션이 시작되거나 재개될 때
Setup Claude Code를 --init-only로 시작하거나, -p 모드에서 --init 또는 --maintenance로 시작할 때. CI나 스크립트에서의 일회성 준비용
UserPromptSubmit 프롬프트를 제출할 때, Claude가 처리하기 전
UserPromptExpansion 사용자가 입력한 명령이 프롬프트로 확장될 때, Claude에 도달하기 전. 확장을 차단할 수 있음
PreToolUse 도구 호출이 실행되기 전. 차단할 수 있음
PermissionRequest 권한 대화 상자가 나타날 때
PermissionDenied 자동 모드 분류기가 도구 호출을 거부할 때. {retry: true}를 반환하면 모델에게 거부된 도구 호출을 재시도해도 된다고 알림
PostToolUse 도구 호출이 성공한 후
PostToolUseFailure 도구 호출이 실패한 후
PostToolBatch 병렬 도구 호출 전체 배치가 해소된 후, 다음 모델 호출 전
Notification Claude Code가 알림을 보낼 때
MessageDisplay 어시스턴트 메시지 텍스트가 표시되는 동안
SubagentStart 서브에이전트가 생성될 때
SubagentStop 서브에이전트가 종료될 때
TaskCreated TaskCreate로 작업이 생성될 때
TaskCompleted 작업이 완료로 표시될 때
Stop Claude가 응답을 마칠 때
StopFailure API 오류로 턴이 끝날 때. 출력과 종료 코드는 무시됨
TeammateIdle 에이전트 팀의 팀메이트가 유휴 상태로 들어가려 할 때
InstructionsLoaded CLAUDE.md 또는 .claude/rules/*.md 파일이 컨텍스트에 로드될 때. 세션 시작 시점과, 세션 중 파일이 지연 로드될 때 발생
ConfigChange 세션 중 설정 파일이 변경될 때
CwdChanged 작업 디렉터리가 바뀔 때(예: Claude가 cd 명령을 실행할 때). direnv 같은 도구로 반응형 환경 관리를 할 때 유용
FileChanged 감시 중인 파일이 디스크에서 변경될 때. matcher 필드로 감시할 파일 이름을 지정
WorktreeCreate --worktree 또는 isolation: "worktree"로 워크트리가 생성될 때. 기본 git 동작을 대체
WorktreeRemove 워크트리가 제거될 때(세션 종료 시 또는 서브에이전트 종료 시)
PreCompact 컨텍스트 압축 전
PostCompact 컨텍스트 압축이 완료된 후
Elicitation MCP 서버가 도구 호출 중 사용자 입력을 요청할 때
ElicitationResult 사용자가 MCP 요청에 응답한 후, 응답이 서버로 다시 전송되기 전
SessionEnd 세션이 종료될 때

각 훅에는 실행 방식을 결정하는 type이 있습니다. 대부분의 훅은 셸 명령을 실행하는 "type": "command"를 사용합니다. 그 외에 네 가지 타입을 더 사용할 수 있습니다.

  • "type": "http": 이벤트 데이터를 URL로 POST합니다. HTTP 훅을 참고하세요.
  • "type": "mcp_tool": 이미 연결된 MCP 서버의 도구를 호출합니다. MCP 도구 훅을 참고하세요.
  • "type": "prompt": 단일 턴 LLM 평가입니다. 프롬프트 기반 훅을 참고하세요.
  • "type": "agent": 도구 접근이 가능한 다중 턴 검증입니다. 에이전트 훅은 실험적이며 변경될 수 있습니다. 에이전트 기반 훅을 참고하세요.

여러 훅의 결과 결합하기

여러 훅이 같은 이벤트에 일치하면, 모든 훅의 명령이 끝까지 실행된 다음 Claude Code가 결과를 병합합니다. 한 훅이 deny를 반환해도 다른 형제 훅의 실행이 멈추지는 않습니다. 한 훅의 deny로 다른 훅의 부수 효과(side effect)를 억제하려고 하지 마세요.

일치하는 모든 훅이 끝나면, Claude Code는 그 출력들을 결합합니다. PreToolUse의 권한 결정에서는 가장 제한적인 답이 우선하며, 그 순서는 deny, defer, ask, allow입니다. additionalContext의 텍스트는 모든 훅에서 보존되어 함께 Claude에 전달됩니다.

아래 예시는 Bash에 두 개의 PreToolUse 훅을 등록합니다. 첫 번째는 모든 명령을 로그 파일에 추가하고 0으로 종료합니다. 두 번째는 명령에 rm -rf가 포함되면 2로 종료해 거부하는 스크립트를 실행합니다.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r .tool_input.command >> ~/.claude/bash.log"
          },
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm-rf.sh"
          }
        ]
      }
    ]
  }
}

Claude가 rm -rf /tmp/build를 실행하려고 하면, 두 훅이 병렬로 실행됩니다. 로깅 훅은 명령을 ~/.claude/bash.log에 기록하고 0으로 종료해 아무런 결정도 보고하지 않습니다. 가드레일 훅은 2로 종료해 도구 호출을 거부합니다. 거부가 우선하므로 Claude Code는 명령을 차단하고 Claude에게 가드레일의 stderr를 보여 줍니다. 로깅 훅은 이미 실행되었으므로 로그 항목은 그대로 기록됩니다.

입력 읽기와 출력 반환하기

훅은 stdin, stdout, stderr, 종료 코드를 통해 Claude Code와 통신합니다. 이벤트가 발생하면 Claude Code는 이벤트별 데이터를 JSON으로 스크립트의 stdin에 전달합니다. 스크립트는 그 데이터를 읽어 작업을 수행한 뒤, 종료 코드를 통해 Claude Code에 다음에 무엇을 할지 알립니다.

훅 입력

모든 이벤트에는 session_id, cwd 같은 공통 필드가 포함되지만, 이벤트 유형마다 서로 다른 데이터가 추가됩니다. 예를 들어 Claude가 Bash 명령을 실행할 때, PreToolUse 훅은 stdin으로 다음과 같은 데이터를 받습니다.

{
  "session_id": "abc123",          // 이 세션의 고유 ID
  "cwd": "/Users/sarah/myproject", // 이벤트 발생 시 작업 디렉터리
  "hook_event_name": "PreToolUse", // 이 훅을 발생시킨 이벤트
  "tool_name": "Bash",             // Claude가 사용하려는 도구
  "tool_input": {                  // Claude가 도구에 전달한 인자
    "command": "npm test"          // Bash의 경우 셸 명령
  }
}

스크립트는 이 JSON을 파싱해 어떤 필드든 활용할 수 있습니다. UserPromptSubmit 훅은 대신 prompt 텍스트를 받고, SessionStart 훅은 source(startup, resume, clear, compact)를 받는 식입니다. 공통 필드는 레퍼런스의 "Common input fields"를, 이벤트별 스키마는 각 이벤트의 절을 참고하세요.

훅 출력

스크립트는 stdout이나 stderr에 출력하고 특정 종료 코드로 종료함으로써 Claude Code에 다음에 무엇을 할지 알립니다. 예를 들어 명령을 차단하려는 PreToolUse 훅은 다음과 같습니다.

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q "drop table"; then
  echo "Blocked: dropping tables is not allowed" >&2  # stderr가 Claude의 피드백이 됨
  exit 2                                               # exit 2 = 동작 차단
fi

exit 0  # exit 0 = 결정 없음, 일반 권한 흐름이 적용됨

종료 코드에 따라 이후 동작이 결정됩니다.

  • 종료 코드 0: 훅은 이의 없음을 보고하고 동작은 정상적으로 진행됩니다. PreToolUse 훅의 경우 이것이 도구 호출을 승인하는 것은 아닙니다. 일반 권한 흐름이 그대로 적용됩니다. UserPromptSubmit, UserPromptExpansion, SessionStart 훅의 경우 stdout으로 출력한 내용은 Claude의 컨텍스트에 추가됩니다.
  • 종료 코드 2: 동작이 차단됩니다. 이유를 stderr에 쓰면 Claude가 피드백으로 받아 조정할 수 있습니다. 일부 이벤트는 차단할 수 없습니다. SessionStart, Setup, Notification 등의 경우 종료 코드 2는 stderr를 사용자에게 보여 주고 실행은 계속됩니다. 전체 목록은 이벤트별 "종료 코드 2 동작"을 참고하세요.
  • 그 외 종료 코드: 동작은 진행됩니다. 트랜스크립트에는 "hook error" 안내와 함께 stderr의 첫 줄이 표시되고, 전체 stderr는 디버그 로그로 갑니다.

구조화된 JSON 출력

종료 코드만으로는 차단하거나 침묵하는 것만 가능합니다. 더 세밀하게 제어하려면 0으로 종료하고 대신 JSON 객체를 stdout으로 출력하세요.

stderr 메시지와 함께 차단하려면 종료 코드 2를, 구조화된 제어를 위해서는 종료 코드 0과 JSON을 사용하세요. 둘을 섞지 마세요. 종료 코드 2로 종료하면 Claude Code는 JSON을 무시합니다.

예를 들어 PreToolUse 훅은 도구 호출을 거부하면서 그 이유를 Claude에게 알리거나, 사용자 승인을 위해 에스컬레이션할 수 있습니다.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use rg instead of grep for better performance"
  }
}

"deny"를 사용하면 Claude Code는 도구 호출을 취소하고 permissionDecisionReason을 Claude에게 다시 전달합니다. 다음 permissionDecision 값들은 PreToolUse에 한정됩니다.

  • "allow": 대화형 권한 프롬프트를 건너뜁니다. 단, 엔터프라이즈 관리형 거부 목록을 포함한 deny 및 ask 규칙은 여전히 적용됩니다.
  • "deny": 도구 호출을 취소하고 이유를 Claude에게 전달합니다.
  • "ask": 평소처럼 사용자에게 권한 프롬프트를 표시합니다.

네 번째 값인 "defer"-p 플래그를 쓰는 비대화형 모드에서 사용할 수 있습니다. 도구 호출을 보존한 채 프로세스를 종료하므로, Agent SDK 래퍼가 입력을 수집한 뒤 재개할 수 있습니다. 레퍼런스의 "Defer a tool call for later"를 참고하세요.

"allow"를 반환하면 대화형 프롬프트는 건너뛰지만 권한 규칙을 무시하지는 않습니다. deny 규칙이 도구 호출과 일치하면, 훅이 "allow"를 반환해도 호출은 차단됩니다. ask 규칙이 일치하면 사용자는 여전히 프롬프트를 받습니다. 즉, 관리형 설정을 포함한 모든 설정 범위의 deny 규칙은 항상 훅 승인보다 우선합니다.

다른 이벤트는 서로 다른 결정 패턴을 사용합니다. 예를 들어 PostToolUseStop 훅은 최상위 decision: "block" 필드를, PermissionRequesthookSpecificOutput.decision.behavior를 사용합니다. 이벤트별 전체 정리는 레퍼런스의 요약 표를 참고하세요.

UserPromptSubmit 훅의 경우, 대신 additionalContext를 사용해 Claude의 컨텍스트에 텍스트를 주입합니다. 프롬프트 기반 훅(type: "prompt")은 출력을 다르게 처리합니다. 프롬프트 기반 훅을 참고하세요.

매처로 훅 필터링하기

매처가 없으면 훅은 해당 이벤트가 발생할 때마다 매번 실행됩니다. 매처를 쓰면 그 범위를 좁힐 수 있습니다. 예를 들어 모든 도구 호출 후가 아니라 파일 편집 후에만 포매터를 실행하려면, PostToolUse 훅에 매처를 추가하세요.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write ..." }
        ]
      }
    ]
  }
}

"Edit|Write" 매처는 Claude가 Edit 또는 Write 도구를 사용할 때만 발생하고, Bash, Read 등 다른 도구를 사용할 때는 발생하지 않습니다. 일반 이름과 정규식이 어떻게 평가되는지는 "Matcher patterns"를 참고하세요.

Claude는 Bash 도구로 셸 명령을 실행해 파일을 만들거나 수정할 수도 있습니다. 훅이 모든 파일 변경을 반드시 봐야 한다면(예: 규정 준수 스캔이나 감사 로깅), 턴마다 작업 트리를 한 번 스캔하는 Stop 훅을 추가하세요. 대신 호출 단위 커버리지가 필요하다면 Bash도 매칭하고, 스크립트가 git status --porcelain으로 수정·미추적 파일을 나열하게 하세요.

각 이벤트 유형은 특정 필드를 기준으로 매칭합니다.

이벤트 매처가 필터링하는 대상 매처 값 예시
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied 도구 이름 Bash, `Edit
SessionStart 세션이 시작된 방식 startup, resume, clear, compact
Setup 셋업을 트리거한 CLI 플래그 init, maintenance
SessionEnd 세션이 종료된 이유 clear, resume, logout, prompt_input_exit, bypass_permissions_disabled, other
Notification 알림 유형 permission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_complete, elicitation_response
SubagentStart 에이전트 유형 general-purpose, Explore, Plan 또는 커스텀 에이전트 이름
PreCompact, PostCompact 압축을 트리거한 것 manual, auto
SubagentStop 에이전트 유형 SubagentStart와 동일한 값
ConfigChange 설정 소스 user_settings, project_settings, local_settings, policy_settings, skills
StopFailure 오류 유형 rate_limit, overloaded, authentication_failed, oauth_org_not_allowed, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, unknown
InstructionsLoaded 로드 이유 session_start, nested_traversal, path_glob_match, include, compact
Elicitation MCP 서버 이름 설정된 MCP 서버 이름
ElicitationResult MCP 서버 이름 Elicitation과 동일한 값
FileChanged 감시할 문자 그대로의 파일 이름(FileChanged 참고) `.envrc
UserPromptExpansion 명령 이름 사용자의 스킬 또는 명령 이름
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, CwdChanged, MessageDisplay 매처 미지원 항상 매 발생 시 실행됨

다른 이벤트 유형에서 매처를 사용하는 추가 예시 몇 가지를 살펴봅니다.

모든 Bash 명령 로깅하기

Bash 도구 호출만 매칭해 각 명령을 파일에 기록합니다. PostToolUse 이벤트는 명령이 완료된 후 발생하므로 tool_input.command에는 실행된 내용이 담깁니다. 훅은 이벤트 데이터를 JSON으로 stdin에서 받고, jq -r '.tool_input.command'가 명령 문자열만 추출하며, >>가 이를 로그 파일에 추가합니다.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
          }
        ]
      }
    ]
  }
}

MCP 도구 매칭하기

MCP 도구는 내장 도구와 다른 이름 규칙을 사용합니다. 형식은 mcp__<서버 이름>__<도구 이름>으로, <서버 이름>은 MCP 서버 이름이고 <도구 이름>은 그 서버가 제공하는 도구입니다. 예를 들어 mcp__github__search_repositoriesmcp__filesystem__read_file처럼 표기합니다. 특정 서버의 모든 도구를 대상으로 하려면 정규식 매처를 쓰고, 여러 서버에 걸쳐 매칭하려면 mcp__.*__write.* 같은 패턴을 사용하세요. 전체 예시 목록은 레퍼런스의 "Match MCP tools"를 참고하세요.

아래 명령은 jq로 훅의 JSON 입력에서 도구 이름을 추출해 stderr에 씁니다. stderr에 쓰면 stdout은 JSON 출력용으로 깨끗하게 유지되고 메시지는 디버그 로그로 전달됩니다.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >&2"
          }
        ]
      }
    ]
  }
}

세션 종료 시 정리하기

SessionEnd 이벤트는 세션이 종료된 이유에 대한 매처를 지원합니다. 아래 훅은 clear(/clear 실행 시)에만 발생하고 정상 종료 시에는 발생하지 않습니다.

{
  "hooks": {
    "SessionEnd": [
      {
        "matcher": "clear",
        "hooks": [
          {
            "type": "command",
            "command": "rm -f /tmp/claude-scratch-*.txt"
          }
        ]
      }
    ]
  }
}

전체 매처 문법은 Hooks 레퍼런스를 참고하세요.

if 필드로 도구 이름과 인자 함께 필터링하기

if 필드는 Claude Code v2.1.85 이상이 필요합니다. 이전 버전은 이 필드를 무시하고 매칭된 모든 호출에서 훅을 실행합니다.

if 필드는 권한 규칙 문법을 사용해 도구 이름과 인자를 함께 기준으로 훅을 필터링하므로, 도구 호출이 일치할 때만 훅 프로세스가 생성됩니다. 이는 도구 이름만으로 그룹 수준에서 필터링하는 matcher보다 한 단계 더 나아간 것입니다.

예를 들어 모든 Bash 명령이 아니라 Claude가 git 명령을 사용할 때만 훅을 실행하려면 다음과 같이 합니다.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(git *)",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-git-policy.sh"
          }
        ]
      }
    ]
  }
}

훅 명령이 실행되는지는 if 패턴의 형태와 Claude가 실행하려는 Bash 명령에 따라 달라집니다.

if 패턴 Bash 명령 훅 실행 여부 이유
Bash(git *) git push 명령 이름이 일치함
Bash(git *) npm test && git push 각 하위 명령을 검사하며 git push가 일치함
Bash(git *) echo $(git log) $()와 백틱 안의 명령도 검사하며 git log가 일치함
Bash(git *) echo $(date) 아니요 git *에 일치하는 하위 명령이 없음
Bash(git push *) echo $(date) 명령 이름 이상을 지정한 패턴은 $(), 백틱, $VAR에 대해 어쨌든 훅을 실행함

또한 Bash 명령을 파싱할 수 없을 때, 이 필터는 패턴과 무관하게 훅을 실행하는 방식으로 "안전하게 열린(fail open)" 상태가 됩니다. 이 필터는 최선의 노력(best-effort) 방식이므로, 강력한 허용 또는 거부를 강제하려면 훅이 아니라 권한 시스템을 사용하세요.

if 필드는 권한 규칙과 동일한 패턴("Bash(git *)", "Edit(*.ts)" 등)을 받습니다. 여러 도구 이름을 매칭하려면 각각 고유한 if 값을 가진 별도의 핸들러를 사용하거나, 파이프 교차(alternation)가 지원되는 matcher 수준에서 매칭하세요.

if는 도구 이벤트(PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied)에서만 동작합니다. 다른 이벤트에 추가하면 훅이 실행되지 않습니다.

훅 위치 설정하기

훅을 어디에 추가하느냐에 따라 그 범위가 결정됩니다.

위치 범위 공유 가능 여부
~/.claude/settings.json 사용자의 모든 프로젝트 아니요, 사용자 기기에 로컬
.claude/settings.json 단일 프로젝트 예, 레포에 커밋 가능
.claude/settings.local.json 단일 프로젝트 아니요, Claude Code가 생성할 때 gitignore됨
관리형 정책 설정 조직 전체 예, 관리자가 제어
플러그인 hooks/hooks.json 플러그인이 활성화된 동안 예, 플러그인에 번들됨
스킬 또는 에이전트 프런트매터 스킬 또는 에이전트가 활성화된 동안 예, 컴포넌트 파일에 정의됨

Claude Code에서 /hooks를 실행하면 설정된 모든 훅을 이벤트별로 묶어서 볼 수 있습니다. 훅을 비활성화하려면 설정 파일에 "disableAllHooks": true를 지정하세요. 관리형 설정에 구성된 훅은 거기에도 disableAllHooks가 설정되지 않는 한 계속 실행됩니다.

Claude Code가 실행 중인 동안 설정 파일을 직접 편집하면, 파일 워처(watcher)가 보통 훅 변경 사항을 자동으로 반영합니다.

프롬프트 기반 훅

결정론적 규칙이 아니라 판단이 필요한 결정의 경우 type: "prompt" 훅을 사용하세요. 셸 명령을 실행하는 대신, Claude Code는 사용자의 프롬프트와 훅의 입력 데이터를 Claude 모델(기본값 Haiku)에 보내 결정을 내립니다. 더 높은 성능이 필요하면 model 필드로 다른 모델을 지정할 수 있습니다.

모델의 유일한 역할은 예/아니요 결정을 JSON으로 반환하는 것입니다.

  • "ok": true: 동작이 진행됩니다.
  • "ok": false: 이후 동작은 이벤트에 따라 달라집니다.
    • StopSubagentStop: reason이 Claude에게 다시 전달되어 작업을 계속합니다.
    • PreToolUse: 도구 호출이 거부되고 reason이 도구 오류로 Claude에게 반환되어, Claude가 조정하고 계속할 수 있습니다.
    • PostToolUse, PostToolBatch, UserPromptSubmit, UserPromptExpansion: 턴이 끝나고 reason이 채팅에 경고 줄로 표시됩니다.

아래 예시는 Stop 훅을 사용해 요청된 모든 작업이 완료되었는지 모델에게 묻습니다. 모델이 "ok": false를 반환하면 Claude는 작업을 계속하고 reason을 다음 지시로 사용합니다.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}

전체 구성 옵션은 레퍼런스의 "Prompt-based hooks"를 참고하세요.

에이전트 기반 훅

에이전트 훅은 실험적입니다. 동작과 구성은 향후 릴리스에서 변경될 수 있습니다. 프로덕션 워크플로에는 명령 훅을 권장합니다.

검증을 위해 파일을 살펴보거나 명령을 실행해야 한다면 type: "agent" 훅을 사용하세요. 단일 LLM 호출을 하는 프롬프트 훅과 달리, 에이전트 훅은 파일을 읽고 코드를 검색하며 다른 도구를 사용해 조건을 검증한 뒤 결정을 반환하는 서브에이전트를 생성합니다.

에이전트 훅은 프롬프트 훅과 동일한 "ok" / "reason" 응답 형식을 사용하되, 기본 타임아웃이 60초로 더 길고 최대 50회의 도구 사용 턴을 사용합니다.

아래 예시는 Claude가 멈추기 전에 테스트가 통과하는지 검증합니다.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

훅 입력 데이터만으로 결정을 내릴 수 있으면 프롬프트 훅을, 코드베이스의 실제 상태와 대조해 무언가를 검증해야 하면 에이전트 훅을 사용하세요.

전체 구성 옵션은 레퍼런스의 "Agent-based hooks"를 참고하세요.

HTTP 훅

셸 명령을 실행하는 대신 이벤트 데이터를 HTTP 엔드포인트로 POST하려면 type: "http" 훅을 사용하세요. 엔드포인트는 명령 훅이 stdin으로 받았을 것과 동일한 JSON을 받고, 동일한 JSON 형식으로 HTTP 응답 본문을 통해 결과를 반환합니다.

HTTP 훅은 웹 서버, 클라우드 함수, 외부 서비스가 훅 로직을 처리하기를 원할 때 유용합니다. 예를 들어 팀 전반의 도구 사용 이벤트를 기록하는 공유 감사 서비스가 그렇습니다.

아래 예시는 모든 도구 사용을 로컬 로깅 서비스로 POST합니다.

{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/tool-use",
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

엔드포인트는 명령 훅과 동일한 출력 형식의 JSON 응답 본문을 반환해야 합니다. 도구 호출을 차단하려면 적절한 hookSpecificOutput 필드를 담아 2xx 응답을 반환하세요. HTTP 상태 코드만으로는 동작을 차단할 수 없습니다.

헤더 값은 $VAR_NAME 또는 ${VAR_NAME} 문법을 사용한 환경 변수 보간(interpolation)을 지원합니다. allowedEnvVars 배열에 나열된 변수만 해석되며, 그 외 모든 $VAR 참조는 빈 값으로 남습니다.

전체 구성 옵션과 응답 처리는 레퍼런스의 "HTTP hooks"를 참고하세요.

제한 사항 및 문제 해결

제한 사항

  • 명령 훅은 stdout, stderr, 종료 코드만으로 통신합니다. / 명령이나 도구 호출을 트리거할 수 없습니다. additionalContext로 반환된 텍스트는 시스템 리마인더로 주입되어 Claude가 일반 텍스트로 읽습니다. HTTP 훅은 대신 응답 본문으로 통신합니다.
  • 훅 타임아웃은 타입에 따라 다릅니다. timeout 필드(초 단위)로 훅마다 재정의할 수 있습니다.
  • command, http, mcp_tool: 10분. UserPromptSubmit은 이를 30초로 낮추고, MessageDisplay는 10초로 낮춥니다.
  • prompt: 30초.
  • agent: 60초.
  • PostToolUse 훅은 도구가 이미 실행된 뒤이므로 동작을 되돌릴 수 없습니다.
  • PermissionRequest 훅은 비대화형 모드(-p)에서 발생하지 않습니다. 자동 권한 결정에는 PreToolUse 훅을 사용하세요.
  • Stop 훅은 작업 완료 시점만이 아니라 Claude가 응답을 마칠 때마다 발생합니다. 사용자 인터럽트에는 발생하지 않습니다. API 오류 시에는 대신 StopFailure가 발생합니다.
  • 여러 PreToolUse 훅이 도구의 인자를 다시 쓰기 위해 updatedInput을 반환하면, 마지막으로 끝난 훅이 적용됩니다. 훅은 병렬로 실행되므로 그 순서는 비결정적입니다. 같은 도구의 입력을 둘 이상의 훅이 수정하는 일은 피하세요.

훅과 권한 모드

PreToolUse 훅은 어떤 권한 모드 검사보다도 먼저 발생합니다. permissionDecision: "deny"를 반환하는 훅은 bypassPermissions 모드에서나 --dangerously-skip-permissions를 쓰더라도 도구를 차단합니다. 이를 통해 사용자가 권한 모드를 바꾸어 우회할 수 없는 정책을 강제할 수 있습니다.

반대로는 성립하지 않습니다. "allow"를 반환하는 훅은 설정의 deny 규칙을 우회하지 못합니다. 훅은 제한을 더 엄격하게 할 수는 있어도, 권한 규칙이 허용하는 범위를 넘어 느슨하게 풀 수는 없습니다.

훅이 발생하지 않음

훅은 설정되어 있지만 한 번도 실행되지 않는 경우입니다.

  • /hooks를 실행해 훅이 올바른 이벤트 아래에 나타나는지 확인하세요.
  • 매처 패턴이 도구 이름과 정확히 일치하는지 확인하세요(매처는 대소문자를 구분합니다).
  • 올바른 이벤트 유형을 트리거하고 있는지 확인하세요(예: PreToolUse는 도구 실행 전, PostToolUse는 도구 실행 후에 발생).
  • 비대화형 모드(-p)에서 PermissionRequest 훅을 쓰고 있다면 대신 PreToolUse로 전환하세요.

출력에 훅 오류가 나타남

트랜스크립트에 "PreToolUse hook error: ..." 같은 메시지가 보이는 경우입니다.

  • 스크립트가 예기치 않게 0이 아닌 코드로 종료한 것입니다. 샘플 JSON을 파이프로 넣어 직접 테스트해 보세요.
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo $?  # 종료 코드 확인
  • "command not found"가 보이면, 스크립트를 절대 경로나 ${CLAUDE_PROJECT_DIR}로 참조하세요. 셸 쿼팅을 아예 피하려면 "args": []를 추가해 exec 형식으로 전환하세요. 그러면 셸 없이 스크립트가 직접 실행됩니다.
  • "jq: command not found"가 보이면, jq를 설치하거나 Python/Node.js로 JSON을 파싱하세요.
  • 스크립트가 아예 실행되지 않으면, 실행 가능하게 만드세요: chmod +x ./my-hook.sh.

/hooks에 설정된 훅이 없다고 표시됨

설정 파일을 편집했는데 메뉴에 훅이 나타나지 않는 경우입니다.

  • 파일 편집은 보통 자동으로 반영됩니다. 몇 초가 지나도 나타나지 않으면 파일 워처가 변경을 놓쳤을 수 있습니다. 세션을 재시작해 강제로 다시 로드하세요.
  • JSON이 유효한지 확인하세요(후행 쉼표와 주석은 허용되지 않습니다).
  • 설정 파일이 올바른 위치에 있는지 확인하세요. 프로젝트 훅은 .claude/settings.json, 전역 훅은 ~/.claude/settings.json입니다.

Stop 훅이 차단 한도에 도달함

Claude가 멈추지 않고 계속 작업하다가, Stop 훅이 너무 여러 번 연속으로 차단했다는 경고와 함께 턴을 끝내는 경우입니다.

Claude Code는 Stop 훅이 진전 없이 연속 8회 차단하면 그 훅을 무시합니다. 훅 스크립트는 자신이 이미 차단한 적이 있는지 확인하도록 작성해야 합니다.