프로그래밍 PROGRAMMING/인공지능 AI

Claude Code Hooks 란? - AI 코딩 워크플로우를 자동화하는 강력한 무기

매운할라피뇨 2026. 4. 8. 08:10
반응형

클로드 코드 Hooks 완전 가이드 - AI 코딩 워크플로우를 자동화하는 강력한 무기

AI 코딩 어시스턴트에게 "이건 항상 이렇게 해"라고 매번 말하는 게 지치지 않으셨나요? Claude Code의 Hooks 기능은 이 문제를 근본적으로 해결합니다. 파일을 수정하면 자동으로 포맷팅하고, 위험한 명령어는 실행 전에 차단하며, 작업이 끝나면 알림을 보내는 — 이 모든 것을 코드 한 줄 없이 설정 파일만으로 가능하게 합니다.
 

1. Hooks란 무엇인가?

Hooks는 Claude Code의 생명주기(lifecycle) 특정 시점에서 자동으로 실행되는 사용자 정의 명령어입니다.
핵심은 "결정론적 제어(deterministic control)"에 있습니다. AI에게 "포맷팅 해줘"라고 부탁하는 것이 아니라, 파일이 수정될 때마다 반드시 포맷터가 실행되도록 보장합니다. AI의 판단에 의존하지 않고, 규칙으로 강제하는 것입니다.

┌──────────────────────────────────────────────┐
│              Hooks의 핵심 개념                 │
│                                              │
│  ❌ AI에게 부탁: "코드 수정 후 prettier 실행해줘" │
│     → AI가 잊을 수 있음, 일관성 없음             │
│                                              │
│  ✅ Hook으로 강제: PostToolUse + Edit|Write     │
│     → 100% 실행 보장, 결정론적 동작              │
└──────────────────────────────────────────────┘

 

2. 4가지 Hook 타입

Hooks는 실행 방식에 따라 4가지 타입으로 나뉩니다.

Command Hook (셸 명령어)

가장 기본적인 타입입니다. 셸 스크립트를 실행하며, stdin으로 JSON 데이터를 받고 exit code와 stdout/stderr로 결과를 반환합니다.

{
  "type": "command",
  "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}

HTTP Hook (웹 엔드포인트)

이벤트 데이터를 HTTP POST로 외부 서비스에 전송합니다. 팀 전체의 감사 로그나 중앙 집중식 검증에 적합합니다.

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

Prompt Hook (LLM 판단)

단순한 규칙으로는 판단이 어려운 경우, Claude 모델(기본 Haiku)에게 yes/no 결정을 위임합니다.

{
  "type": "prompt",
  "prompt": "모든 요청된 작업이 완료되었는지 확인하세요. 미완료라면 {\"ok\": false, \"reason\": \"남은 작업\"} 형식으로 응답하세요."
}

Agent Hook (서브에이전트)

파일을 읽고, 코드를 검색하고, 명령어를 실행할 수 있는 서브에이전트를 스폰합니다. 가장 강력하지만 비용이 높습니다.

{
  "type": "agent",
  "prompt": "모든 단위 테스트가 통과하는지 확인하세요. 테스트 스위트를 실행하고 결과를 확인하세요.",
  "timeout": 120
}
┌─────────────────────────────────────────────────────┐
│  Hook 타입별 비교                                     │
│                                                     │
│  Command  ██░░░░░░░░  비용 최저 / 결정론적 / 빠름      │
│  HTTP     ███░░░░░░░  외부 연동 / 팀 공유 / 중앙화     │
│  Prompt   ██████░░░░  판단력 필요 / 단일 호출 / 중간   │
│  Agent    ██████████  최고 능력 / 도구 사용 / 비용 최고  │
│                                                     │
│  원칙: 가능하면 Command, 판단이 필요하면 Prompt,       │
│        검증이 필요하면 Agent                           │
└─────────────────────────────────────────────────────┘
320x100

3. Hook 생명주기 — 25개 이벤트 총정리

Hooks는 Claude Code의 전체 생명주기에 걸쳐 25개의 이벤트 포인트를 제공합니다.

┌─────────────────────────────────────────────────────────┐
│                  Hook 생명주기 흐름도                      │
│                                                         │
│  세션 시작                                                │
│  ──────                                                  │
│  SessionStart ─→ InstructionsLoaded                      │
│       │                                                  │
│       ▼                                                  │
│  사용자 입력                                               │
│  ──────────                                               │
│  UserPromptSubmit                                        │
│       │                                                  │
│       ▼                                                  │
│  도구 실행 사이클 (반복)                                    │
│  ────────────────────                                     │
│  PreToolUse ─→ PermissionRequest ─→ PostToolUse          │
│       │                              │                   │
│       │                     PostToolUseFailure            │
│       │                                                  │
│       ▼                                                  │
│  에이전트 관련                                              │
│  ───────────                                              │
│  SubagentStart ─→ SubagentStop                           │
│  TaskCreated ─→ TaskCompleted                            │
│  TeammateIdle                                            │
│       │                                                  │
│       ▼                                                  │
│  세션 종료                                                │
│  ──────                                                  │
│  Stop / StopFailure ─→ SessionEnd                        │
│                                                         │
│  비동기 이벤트 (독립 발생)                                  │
│  ────────────────────                                     │
│  FileChanged, CwdChanged, ConfigChange                   │
│  WorktreeCreate/Remove, PreCompact/PostCompact           │
│  Notification, Elicitation/ElicitationResult             │
└─────────────────────────────────────────────────────────┘

자주 사용하는 핵심 이벤트

PreToolUse — 도구 실행 전 차단/허용 결정
- 위험한 명령어 차단, 보호 파일 수정 방지
- matcher로 특정 도구만 필터링 (Bash, Edit, Write 등)

PostToolUse — 도구 실행 후 자동 작업
- 자동 포맷팅, 린팅, 로깅
- 실행 후이므로 되돌리기는 불가
Stop — Claude가 응답을 마칠 때
- 작업 완료 여부 검증
- 테스트 통과 여부 확인
Notification — 알림 발생 시
- 데스크톱 알림으로 전환
- 권한 요청, 유휴 대기 등
SessionStart — 세션 시작/재개 시
- 환경 변수 설정
- 컴팩션 후 컨텍스트 재주입
UserPromptSubmit — 사용자 프롬프트 제출 시
- 입력 검증
- 추가 컨텍스트 주입
 

4. 설정 방법

설정 파일 위치

Hook의 적용 범위는 설정 파일 위치에 따라 결정됩니다:

~/.claude/settings.json          → 모든 프로젝트 (전역)
.claude/settings.json            → 현재 프로젝트 (공유 가능, 커밋)
.claude/settings.local.json      → 현재 프로젝트 (로컬 전용, gitignore)
관리 정책(Managed Policy)         → 조직 전체 (관리자 제어)

기본 설정 구조

{
  "hooks": {
    "이벤트명": [
      {
        "matcher": "필터 패턴",
        "hooks": [
          {
            "type": "command",
            "command": "실행할 명령어",
            "if": "조건부 필터",
            "timeout": 600,
            "async": false
          }
        ]
      }
    ]
  }
}

Exit Code의 의미

Exit 0  → 성공, 진행 허용 (stdout의 JSON 파싱)
Exit 2  → 차단! (stderr 내용이 Claude에게 피드백)
기타    → 비차단 오류 (verbose 모드에서만 표시)

 

5. 실전 활용 예시

예시 1: 데스크톱 알림 — "Claude가 기다리고 있어요"

Claude가 권한을 요청하거나 입력을 기다릴 때 데스크톱 알림을 보냅니다. 터미널을 계속 지켜볼 필요가 없어집니다.

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

Linux에서는 notify-send, Windows에서는 PowerShell의 MessageBox를 사용합니다.

 

예시 2: 자동 코드 포맷팅 — 수정할 때마다 Prettier 실행

Claude가 파일을 편집하거나 생성할 때마다 자동으로 Prettier가 실행됩니다.

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

파일 확장자별로 다른 도구를 적용할 수도 있습니다:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "if": "Edit|Write(*.py)",
            "command": "python -m black $(jq -r '.tool_input.file_path')"
          },
          {
            "type": "command",
            "if": "Edit|Write(*.ts|*.tsx)",
            "command": "jq -r '.tool_input.file_path' | xargs npx eslint --fix"
          }
        ]
      }
    ]
  }
}

 

예시 3: 위험한 명령어 차단 — rm -rf, DROP TABLE 방지

PreToolUse 훅으로 위험한 셸 명령어를 실행 전에 차단합니다.

.claude/hooks/protect-commands.sh:

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

DANGEROUS_PATTERNS=("rm -rf /" "DROP TABLE" "--force" "format c:")

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -qi "$pattern"; then
    echo "차단됨: 위험한 패턴 '$pattern' 감지" >&2
    exit 2
  fi
done

exit 0
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-commands.sh"
          }
        ]
      }
    ]
  }
}

 

예시 4: 민감한 파일 보호 — .env, lock 파일 수정 차단

.claude/hooks/protect-files.sh:

#!/bin/bash
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 "차단됨: $FILE_PATH는 보호된 파일입니다 ('$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"
          }
        ]
      }
    ]
  }
}

 

예시 5: 컴팩션 후 컨텍스트 재주입

Claude의 컨텍스트 윈도우가 가득 차면 대화를 요약하는 컴팩션이 발생합니다. 이때 중요한 프로젝트 규칙이 손실될 수 있습니다. SessionStart + compact 매처로 해결합니다.

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo '리마인더: Bun 사용 (npm 아님). 커밋 전 bun test 실행. 현재 스프린트: 인증 리팩토링.'"
          }
        ]
      }
    ]
  }
}

동적으로 최근 커밋 정보를 주입할 수도 있습니다:

{
  "type": "command",
  "command": "echo '최근 커밋:' && git log --oneline -5"
}

 

예시 6: Stop 훅으로 작업 완료 검증

Claude가 "다 했어요"라고 할 때, 정말로 모든 작업이 완료되었는지 확인합니다.
Prompt 타입 (LLM 판단):

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "모든 요청된 작업이 완료되었는지 확인하세요. 미완료 항목이 있으면 {\"ok\": false, \"reason\": \"남은 작업 설명\"} 으로 응답하세요."
          }
        ]
      }
    ]
  }
}

Agent 타입 (실제 테스트 실행):

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "모든 단위 테스트가 통과하는지 확인하세요. 테스트 스위트를 실행하고 결과를 확인하세요. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

주의 — 무한 루프 방지:

Stop 훅이 "아직 안 끝났어"를 계속 반환하면 무한 루프에 빠집니다. 반드시 stop_hook_active를 확인하세요:

#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # 이미 Stop 훅이 활성화된 상태 → 종료 허용
fi
# ... 나머지 검증 로직

 

예시 7: 특정 권한 자동 승인

매번 "허용하시겠습니까?" 다이얼로그를 승인하는 것이 번거롭다면, 안전한 작업에 대해 자동 승인을 설정할 수 있습니다.

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

주의: matcher를 .*이나 빈 문자열로 설정하면 모든 권한 요청이 자동 승인됩니다. 파일 쓰기와 셸 명령 포함이므로 매우 위험합니다. 항상 matcher를 가능한 좁게 지정하세요.

 

예시 8: 에이전트 팀의 품질 게이트

에이전트 팀과 함께 사용하면 더욱 강력합니다.

#!/bin/bash
# TaskCompleted 훅: 테스트 통과 확인
if ! npm test 2>&1; then
  echo "테스트를 통과해야 태스크를 완료할 수 있습니다" >&2
  exit 2
fi
exit 0
{
  "hooks": {
    "TaskCompleted": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-tests.sh"
          }
        ]
      }
    ]
  }
}

이 설정으로 팀원 에이전트는 테스트가 통과하기 전까지 태스크를 완료 처리할 수 없습니다.
 

예시 9: 비동기 감사 로깅

모든 Bash 명령어를 감사 로그에 기록합니다. async: true로 실행하면 Claude의 작업 흐름을 방해하지 않습니다.

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

 

예시 10: 환경 변수 자동 설정

세션이 시작될 때마다 필요한 환경 변수를 자동으로 설정합니다.

#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export API_BASE_URL=https://api.example.com' >> "$CLAUDE_ENV_FILE"
fi
exit 0

디렉토리 변경 시 direnv와 연동하여 환경을 자동 전환할 수도 있습니다:

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

 

6. 디버깅 & 트러블슈팅

Hook이 실행되지 않을 때

  • /hooks 명령으로 훅이 올바른 이벤트에 등록되었는지 확인
  • matcher가 도구 이름과 정확히 일치하는지 확인 (대소문자 구분)
  • 올바른 이벤트 타입인지 확인 (PreToolUse는 실행 전, PostToolUse는 실행 후)

JSON 파싱 에러

셸 프로파일(.zshrc, .bashrc)에 echo 문이 있으면 Hook의 JSON 출력 앞에 불필요한 텍스트가 삽입됩니다.

# .zshrc에서 인터랙티브 셸만 echo 실행
if [[ $- == *i* ]]; then
  echo "Shell ready"
fi

수동 테스트

echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo "Exit code: $?"

디버그 모드

Ctrl+O로 verbose 모드를 켜거나, claude --debug로 상세 실행 로그를 확인할 수 있습니다.

 

7. 모범 사례 정리

┌──────────────────────────────────────────────────────┐
│  Hooks 모범 사례 체크리스트                              │
│                                                      │
│  ✅ Hook은 빠르게: SessionStart 훅은 매 세션마다 실행됨   │
│  ✅ Matcher를 좁게: 필요한 도구/이벤트만 필터링           │
│  ✅ Stop 훅에서 stop_hook_active 체크: 무한루프 방지     │
│  ✅ Exit 2로 차단: stderr에 이유 작성                    │
│  ✅ JSON 출력 시 셸 프로파일 echo 주의                   │
│  ✅ CLAUDE_ENV_FILE로 환경 변수 영속                     │
│  ✅ async: true로 비차단 작업 처리                       │
│  ✅ if 필드로 세밀한 조건 필터링 (v2.1.85+)              │
│  ✅ 가능하면 Command, 판단 필요시만 Prompt/Agent          │
│  ✅ /hooks 메뉴로 등록 상태 확인                         │
└──────────────────────────────────────────────────────┘

 

마무리

Claude Code Hooks는 단순한 자동화 기능을 넘어서, AI 코딩 어시스턴트를 여러분의 개발 워크플로우에 완벽하게 통합하는 접착제 역할을 합니다. 포맷팅, 보안, 테스트, 알림, 환경 관리까지 — AI의 판단에 맡기지 않고 결정론적으로 보장할 수 있습니다.
시작은 간단합니다. 데스크톱 알림 훅 하나부터 설정해 보세요. Claude가 기다리고 있을 때 알림을 받는 것만으로도 작업 효율이 크게 달라집니다. 거기서부터 자동 포맷팅, 파일 보호, 테스트 검증으로 점진적으로 확장하면 됩니다.
 
참고 자료:
- Claude Code 공식 문서 - Hooks Reference
- Claude Code 공식 문서 - Hooks Guide
- aiorg.dev - Claude Code Hooks: 20+ Ready-to-Use Examples
- DataCamp - Claude Code Hooks: A Practical Guide

반응형