Claude Code Hooks 기본 정리

choi·2026년 3월 22일
post-thumbnail

Claude Code 실행 주기의 특정 시점에 자동으로 실행되는 사용자 정의 명령어.
LLM이 "할 수도 있고 안 할 수도 있는" 게 아니라, 조건이 맞으면 무조건 실행됨.


Hook 이벤트 종류

이벤트설명
PreToolUse도구 실행 (차단 가능)
PostToolUse도구 실행
PostToolUseFailure도구 실행 실패
PermissionRequest권한 요청 시 (자동 승인/거부 가능)
UserPromptSubmit프롬프트 제출 시
Notification알림 발생 시
SessionStart세션 시작/재개 시
SessionEnd세션 종료 시
StopClaude 응답 완료 시
StopFailureAPI 오류로 턴 종료 시
PreCompact컨텍스트 압축 전
PostCompact컨텍스트 압축 후
SubagentStartSubagent 생성 시
SubagentStopSubagent 완료 시
WorktreeCreateWorktree 생성 시
WorktreeRemoveWorktree 삭제 시
ConfigChange설정 파일 변경 시

설정 위치

~/.claude/settings.json          # 전역 (모든 프로젝트)
.claude/settings.json            # 프로젝트 공유
.claude/settings.local.json      # 프로젝트 로컬 (gitignored)

기본 구조

{
  "hooks": {
    "EventName": [
      {
        "matcher": "regex_pattern",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/script.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Hook 타입

타입설명
command셸 명령어 실행 (가장 일반적)
httpHTTP 엔드포인트 호출
promptLLM 프롬프트로 판단
agent서브에이전트 실행

Exit Code 동작

Exit Code동작
0성공, 액션 진행
2차단 — 액션 실행 안 함, stderr를 Claude에게 피드백으로 전달
1, 3+오류 (비차단) — 액션은 진행

Hook Input/Output

Input (stdin으로 전달되는 JSON)

{
  "session_id": "abc123",
  "cwd": "/Users/me/myproject",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  },
  "tool_use_id": "toolu_..."
}

Output (exit 0 + stdout JSON)

{
  "continue": true,
  "suppressOutput": false,
  "systemMessage": "optional message to Claude",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow|deny|ask",
    "permissionDecisionReason": "reason"
  }
}

Matcher 패턴

이벤트매칭 대상예시
PreToolUse / PostToolUse도구 이름"Bash", "Edit\|Write"
SessionStart / SessionEnd세션 이유"startup", "resume", "compact"
ConfigChange설정 소스"user_settings", "project_settings"
Notification알림 타입"permission_prompt", "idle_prompt"
MCP 도구도구 이름"mcp__github__.*", "mcp__.*__write.*"

실용 예시

1. 민감 파일 보호

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

FILE=$(cat | jq -r '.tool_input.file_path // empty')

for pattern in ".env" "package-lock.json" ".git/"; do
  if [[ "$FILE" == *"$pattern"* ]]; then
    echo "Blocked: $FILE matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

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

2. 파일 편집 후 자동 포맷팅

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

3. 권한 자동 승인

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

4. 작업 완료 시 macOS 알림

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

5. Linux 알림

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

6. MCP 도구 호출 로깅

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "mcp__github__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >> ~/.claude/tool-log.txt"
          }
        ]
      }
    ]
  }
}

HTTP Hook

{
  "type": "http",
  "url": "http://localhost:8080/hooks",
  "headers": {
    "Authorization": "Bearer $MY_TOKEN"
  },
  "allowedEnvVars": ["MY_TOKEN"],
  "timeout": 30
}

유용한 환경 변수

변수설명
$CLAUDE_PROJECT_DIR프로젝트 루트 디렉토리
$CLAUDE_PLUGIN_ROOT플러그인 디렉토리
$CLAUDE_PLUGIN_DATA플러그인 지속 데이터 디렉토리
$CLAUDE_ENV_FILE환경변수 저장 파일 (SessionStart 전용)

주의사항

  • 무한 루프 방지: Stop hook에서 stop_hook_active 값 확인 필요
    if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
      exit 0
    fi
  • jq 설치 필요: JSON 파싱에 필수 (brew install jq / apt-get install jq)
  • 실행 권한 설정: chmod +x .claude/hooks/my-hook.sh
  • Shell 프로필 주의: ~/.zshrc의 unconditional echo는 hook 오작동 유발 가능

관리 명령어

/hooks                  # 설정된 모든 hook 확인 (읽기 전용)

모든 hook 비활성화:

{
  "disableAllHooks": true
}
profile
늦게나마 정신을 차리려고 하는 개발 뭐시기하는 사람

0개의 댓글