Claude Code 훅은 Claude Code의 생명주기 중 다양한 지점에서 실행되는 사용자 정의 셸 명령어입니다. 훅은 Claude Code의 동작에 대한 결정론적 제어를 제공하여, LLM이 실행을 선택하는 것에 의존하지 않고 특정 작업이 항상 발생하도록 보장합니다.
훅에 대한 참조 문서는 훅 참조를 참조하세요.
훅의 예시 사용 사례는 다음과 같습니다:
  • 알림: Claude Code가 사용자의 입력이나 실행 권한을 기다릴 때 알림을 받는 방법을 사용자 정의합니다.
  • 자동 포맷팅: 모든 파일 편집 후 .ts 파일에 prettier를, .go 파일에 gofmt를 실행합니다.
  • 로깅: 규정 준수나 디버깅을 위해 실행된 모든 명령어를 추적하고 계산합니다.
  • 피드백: Claude Code가 코드베이스 규칙을 따르지 않는 코드를 생성할 때 자동화된 피드백을 제공합니다.
  • 사용자 정의 권한: 프로덕션 파일이나 민감한 디렉토리에 대한 수정을 차단합니다.
이러한 규칙을 프롬프트 지시사항이 아닌 훅으로 인코딩함으로써, 제안을 실행될 것으로 예상되는 매번 실행되는 앱 수준 코드로 변환합니다.
훅은 현재 환경의 자격 증명으로 에이전트 루프 중에 자동으로 실행되므로, 훅을 추가할 때 보안 영향을 고려해야 합니다. 예를 들어, 악의적인 훅 코드는 데이터를 유출할 수 있습니다. 훅을 등록하기 전에 항상 훅 구현을 검토하세요.전체 보안 모범 사례는 훅 참조 문서의 보안 고려사항을 참조하세요.

훅 이벤트 개요

Claude Code는 워크플로의 다양한 지점에서 실행되는 여러 훅 이벤트를 제공합니다:
  • PreToolUse: 도구 호출 전에 실행됩니다 (차단 가능)
  • PostToolUse: 도구 호출 완료 후 실행됩니다
  • UserPromptSubmit: 사용자가 프롬프트를 제출할 때, Claude가 처리하기 전에 실행됩니다
  • Notification: Claude Code가 알림을 보낼 때 실행됩니다
  • Stop: Claude Code가 응답을 완료할 때 실행됩니다
  • SubagentStop: 하위 에이전트 작업이 완료될 때 실행됩니다
  • PreCompact: Claude Code가 압축 작업을 실행하려고 할 때 실행됩니다
  • SessionStart: Claude Code가 새 세션을 시작하거나 기존 세션을 재개할 때 실행됩니다
  • SessionEnd: Claude Code 세션이 종료될 때 실행됩니다
각 이벤트는 서로 다른 데이터를 받고 Claude의 동작을 서로 다른 방식으로 제어할 수 있습니다.

빠른 시작

이 빠른 시작에서는 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"
          }
        ]
      }
    ]
  }
}

6단계: 훅 테스트

Claude에게 ls와 같은 간단한 명령어를 실행하도록 요청하고 로그 파일을 확인하세요:
cat ~/.claude/bash-command-log.txt
다음과 같은 항목이 표시되어야 합니다:
ls - Lists files and directories

더 많은 예시

완전한 예시 구현은 공개 코드베이스의 bash 명령어 검증기 예시를 참조하세요.

코드 포맷팅 훅

편집 후 TypeScript 파일을 자동으로 포맷팅합니다:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.ts$'; then npx prettier --write \"$file_path\"; fi; }"
          }
        ]
      }
    ]
  }
}

마크다운 포맷팅 훅

마크다운 파일에서 누락된 언어 태그와 포맷팅 문제를 자동으로 수정합니다:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/markdown_formatter.py"
          }
        ]
      }
    ]
  }
}
다음 내용으로 .claude/hooks/markdown_formatter.py를 생성하세요:
#!/usr/bin/env python3
"""
Markdown formatter for Claude Code output.
Fixes missing language tags and spacing issues while preserving code content.
"""
import json
import sys
import re
import os

def detect_language(code):
    """Best-effort language detection from code content."""
    s = code.strip()
    
    # JSON detection
    if re.search(r'^\s*[{\[]', s):
        try:
            json.loads(s)
            return 'json'
        except:
            pass
    
    # Python detection
    if re.search(r'^\s*def\s+\w+\s*\(', s, re.M) or \
       re.search(r'^\s*(import|from)\s+\w+', s, re.M):
        return 'python'
    
    # JavaScript detection  
    if re.search(r'\b(function\s+\w+\s*\(|const\s+\w+\s*=)', s) or \
       re.search(r'=>|console\.(log|error)', s):
        return 'javascript'
    
    # Bash detection
    if re.search(r'^#!.*\b(bash|sh)\b', s, re.M) or \
       re.search(r'\b(if|then|fi|for|in|do|done)\b', s):
        return 'bash'
    
    # SQL detection
    if re.search(r'\b(SELECT|INSERT|UPDATE|DELETE|CREATE)\s+', s, re.I):
        return 'sql'
        
    return 'text'

def format_markdown(content):
    """Format markdown content with language detection."""
    # Fix unlabeled code fences
    def add_lang_to_fence(match):
        indent, info, body, closing = match.groups()
        if not info.strip():
            lang = detect_language(body)
            return f"{indent}```{lang}\n{body}{closing}\n"
        return match.group(0)
    
    fence_pattern = r'(?ms)^([ \t]{0,3})```([^\n]*)\n(.*?)(\n\1```)\s*$'
    content = re.sub(fence_pattern, add_lang_to_fence, content)
    
    # Fix excessive blank lines (only outside code fences)
    content = re.sub(r'\n{3,}', '\n\n', content)
    
    return content.rstrip() + '\n'

# Main execution
try:
    input_data = json.load(sys.stdin)
    file_path = input_data.get('tool_input', {}).get('file_path', '')
    
    if not file_path.endswith(('.md', '.mdx')):
        sys.exit(0)  # Not a markdown file
    
    if os.path.exists(file_path):
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        
        formatted = format_markdown(content)
        
        if formatted != content:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(formatted)
            print(f"✓ Fixed markdown formatting in {file_path}")
    
except Exception as e:
    print(f"Error formatting markdown: {e}", file=sys.stderr)
    sys.exit(1)
스크립트를 실행 가능하게 만드세요:
chmod +x .claude/hooks/markdown_formatter.py
이 훅은 자동으로:
  • 레이블이 없는 코드 블록에서 프로그래밍 언어를 감지합니다
  • 구문 강조를 위한 적절한 언어 태그를 추가합니다
  • 코드 내용을 보존하면서 과도한 빈 줄을 수정합니다
  • 마크다운 파일(.md, .mdx)만 처리합니다

사용자 정의 알림 훅

Claude가 입력을 필요로 할 때 데스크톱 알림을 받습니다:
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Awaiting your input'"
          }
        ]
      }
    ]
  }
}

파일 보호 훅

민감한 파일에 대한 편집을 차단합니다:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "python3 -c \"import json, sys; data=json.load(sys.stdin); path=data.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(p in path for p in ['.env', 'package-lock.json', '.git/']) else 0)\""
          }
        ]
      }
    ]
  }
}

더 알아보기

  • 훅에 대한 참조 문서는 훅 참조를 참조하세요.
  • 포괄적인 보안 모범 사례와 안전 지침은 훅 참조 문서의 보안 고려사항을 참조하세요.
  • 문제 해결 단계와 디버깅 기법은 훅 참조 문서의 디버깅을 참조하세요.