Programmer/AI

Claude Code Hooks: 자동화로 실수를 원천 차단하기

MoreLean 2026. 1. 21. 00:27
반응형

Claude Code의 Hooks 시스템은 정말 강력합니다. 특정 이벤트가 발생할 때 자동으로 실행되는 스크립트를 설정할 수 있어요.

파일을 편집할 때마다 자동으로 린트를 돌리거나, 위험한 명령어를 차단하거나, 하드코딩된 API 키가 있으면 경고를 띄울 수 있습니다. 한 번 설정해두면 매번 수동으로 할 필요 없이 자동으로 실행됩니다.


TL;DR

  • Hooks는 Claude Code의 이벤트에 반응하는 자동화 스크립트입니다
  • 주요 이벤트: PreToolUse(도구 실행 전), PostToolUse(도구 실행 후), Stop(응답 완료)
  • 파일 편집 후 자동 린트/포맷팅 → 코드 스타일 걱정 끝
  • 위험한 명령어 자동 차단 → 실수 방지
  • 하드코딩된 API 키 같은 보안 위험 감지 → 유출 사고 방지

Hooks란?

"Hooks are shell commands that execute automatically at predetermined points in Claude Code's workflow. Unlike ad-hoc model prompts, hooks guarantee that specific operations occur consistently without additional user intervention." — eesel AI

Hooks는 Claude Code가 특정 작업을 수행할 때 자동으로 실행되는 쉘 명령어입니다.

Git hooks(pre-commit, post-commit 등)와 비슷한 개념이지만, Claude Code의 도구 사용에 반응한다는 점이 다릅니다.

왜 필요한가?

Claude에게 "린트 돌려줘", "포맷팅 해줘"라고 매번 말할 수 있습니다. 하지만:

  • Claude가 까먹을 수 있음
  • 매번 요청하기 귀찮음
  • 일관성이 없음

"By encoding these rules as hooks rather than prompting instructions, you turn suggestions into app-level code that executes every time it is expected to run." — Claude Code Docs

Hooks는 **확정적(deterministic)**입니다. Claude의 판단에 의존하지 않고, 조건이 맞으면 항상 실행됩니다.


Hooks 이벤트 종류

Claude Code는 8가지 이벤트를 지원합니다:

이벤트 발생 시점 주요 용도

PreToolUse 도구 실행 전 위험한 명령 차단, 입력 검증
PostToolUse 도구 실행 후 자동 포맷팅, 린트, 테스트
PermissionRequest 권한 요청 시 특정 명령 자동 승인
UserPromptSubmit 프롬프트 제출 시 프롬프트 검증, 컨텍스트 주입
SessionStart 세션 시작 시 초기 컨텍스트 설정
SessionEnd 세션 종료 시 정리 작업, 로깅
Stop 에이전트 응답 완료 최종 검증, 알림
SubagentStop 서브에이전트 완료 서브에이전트 결과 처리

"Key events include 'PreToolUse' (before a tool runs), 'PostToolUse' (after a tool completes successfully), 'Notification' (when Claude sends an alert), 'Stop' (when the AI agent finishes its response), 'UserPromptSubmit', and 'SessionStart'." — eesel AI

가장 많이 쓰는 이벤트

  1. PostToolUse: 파일 편집 후 자동 포맷팅/린트
  2. PreToolUse: 위험한 명령어 차단
  3. PermissionRequest: 자주 쓰는 명령 자동 승인

Hooks 설정 방법

Hooks는 settings.json 파일에 정의합니다:

~/.claude/settings.json          ← 사용자 전역 (모든 프로젝트)
.claude/settings.json            ← 프로젝트 공유 (Git 커밋)
.claude/settings.local.json      ← 프로젝트 로컬 (Git 제외)

기본 구조

{
  "hooks": {
    "이벤트명": [
      {
        "matcher": "도구패턴",
        "hooks": [
          {
            "type": "command",
            "command": "실행할 명령어"
          }
        ]
      }
    ]
  }
}

Matcher 패턴

  • "Edit": Edit 도구만 매칭
  • "Edit|Write": Edit 또는 Write 매칭
  • "Edit|Write|MultiEdit": 여러 도구 매칭
  • "Bash": Bash 명령 매칭
  • "*": 모든 도구 매칭

실전 예시 1: 자동 포맷팅

파일을 편집할 때마다 Prettier로 자동 포맷팅:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
          }
        ]
      }
    ]
  }
}

"Automatic Formatting: Run tools like Prettier on JavaScript and TypeScript, or gofmt on Go files immediately after AI edits." — CometAPI

TypeScript 파일만 포맷팅

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|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; }"
          }
        ]
      }
    ]
  }
}

실전 예시 2: 자동 린트 + 포맷팅

포맷팅과 린트를 동시에 실행:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
          },
          {
            "type": "command",
            "command": "npx eslint --fix \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
          }
        ]
      }
    ]
  }
}

"Multiple hooks run in parallel. Your code is formatted AND linted before Claude's response appears." — Claude Fast

React/TypeScript 프로젝트용 완전판

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "prettier --write $CLAUDE_FILE_PATHS && eslint $CLAUDE_FILE_PATHS --fix && if [ -f 'tsconfig.json' ]; then npx tsc --noEmit; fi"
          }
        ]
      }
    ]
  }
}

실전 예시 3: 위험한 명령어 차단

rm -rf / 같은 위험한 명령어 자동 차단:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_COMMAND\" | grep -E '(rm -rf|sudo|chmod 777)' > /dev/null; then echo '❌ Dangerous command blocked' && exit 1; fi"
          }
        ]
      }
    ]
  }
}

"Security enhancements - Blocks dangerous commands and sensitive file access at multiple levels." — disler/claude-code-hooks-mastery

.env 파일 접근 차단

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_TOOL_INPUT_FILE_PATH\" | grep -q '\\.env'; then echo '❌ .env file access blocked' && exit 1; fi"
          }
        ]
      }
    ]
  }
}

실전 예시 4: 하드코딩된 시크릿 감지

API 키나 비밀번호가 코드에 하드코딩되면 경고:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | { read f; if grep -qE '(api_key|apikey|secret|password|token)\\s*=\\s*[\"'\\'][^\"'\\']+(\"'\\']' \"$f\" 2>/dev/null; then echo '🚨 WARNING: Possible hardcoded secret in '$f; fi; }"
          }
        ]
      }
    ]
  }
}

이건 console.log 남기는 것보다 훨씬 치명적인 실수입니다. API 키가 Git에 커밋되면:

  • 키 유출로 인한 보안 사고
  • AWS 요금 폭탄
  • 서비스 해킹

더 강력한 버전: 차단까지

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "if grep -rqE '(sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[a-zA-Z0-9]{36})' \"$CLAUDE_PROJECT_DIR/src\"; then echo '❌ Hardcoded API key detected! Use environment variables.' && exit 1; fi"
          }
        ]
      }
    ]
  }
}

이 패턴은 실제 API 키 형식을 감지합니다:

  • sk-... — OpenAI API 키
  • AKIA... — AWS Access Key
  • ghp_... — GitHub Personal Token

"Security enhancements - Blocks dangerous commands and sensitive file access at multiple levels." — disler/claude-code-hooks-mastery


실전 예시 5: 테스트 자동 실행

파일 편집 후 테스트 자동 실행:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_TOOL_INPUT_FILE_PATH\" | grep -qE '\\.(ts|tsx|js|jsx)$'; then npm test -- --findRelatedTests \"$CLAUDE_TOOL_INPUT_FILE_PATH\" --passWithNoTests; fi"
          }
        ]
      }
    ]
  }
}

백그라운드에서 전체 테스트 실행

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "npm test &"
          }
        ]
      }
    ]
  }
}

실전 예시 6: 자주 쓰는 명령 자동 승인

npm test 매번 승인하기 귀찮다면:

{
  "hooks": {
    "PermissionRequest": [
      {
        "matcher": "Bash(npm test*)",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"decision\": \"approve\"}'"
          }
        ]
      }
    ]
  }
}

"Tired of approving npm test every time? Use PermissionRequest." — Claude Fast

린트와 빌드 명령도 자동 승인

{
  "hooks": {
    "PermissionRequest": [
      {
        "matcher": "Bash(npm run lint*)|Bash(npm run build*)|Bash(npm test*)",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"decision\": \"approve\"}'"
          }
        ]
      }
    ]
  }
}

실전 예시 7: 세션 시작 시 컨텍스트 주입

세션 시작할 때 Git 상태와 TODO 목록 자동 표시:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo '## Current State' && git status --short && echo '## Active TODOs' && grep -r 'TODO:' src/ --include='*.ts' | head -5"
          }
        ]
      }
    ]
  }
}

"Claude now sees uncommitted changes and pending TODOs before you say anything." — Claude Fast


환경 변수

Hooks에서 사용 가능한 환경 변수:

변수 설명

$CLAUDE_PROJECT_DIR 프로젝트 루트 경로
$CLAUDE_FILE_PATHS 작업 대상 파일 경로
$CLAUDE_TOOL_NAME 사용된 도구 이름
$CLAUDE_TOOL_INPUT_FILE_PATH 도구 입력 파일 경로
$CLAUDE_WORKING_DIR 현재 작업 디렉토리
$CLAUDE_SESSION_ID 세션 ID

완전한 설정 예시

프로덕션에서 쓸 수 있는 종합 설정:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_COMMAND\" | grep -E '(rm -rf /|sudo rm|chmod 777)' > /dev/null; then echo '❌ Dangerous command blocked' && exit 1; fi"
          }
        ]
      },
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "if [ \"$(git branch --show-current)\" = 'main' ]; then echo '❌ Cannot edit on main branch' && exit 1; fi"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\" 2>/dev/null || true"
          },
          {
            "type": "command",
            "command": "npx eslint --fix \"$CLAUDE_TOOL_INPUT_FILE_PATH\" 2>/dev/null || true"
          }
        ]
      }
    ],
    "PermissionRequest": [
      {
        "matcher": "Bash(npm test*)|Bash(npm run lint*)|Bash(pnpm test*)",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"decision\": \"approve\"}'"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo '📍 Branch:' $(git branch --show-current) && echo '📊 Status:' && git status --short | head -5"
          }
        ]
      }
    ]
  }
}

주의사항

보안

"You must consider the security implication of hooks as you add them, because hooks run automatically during the agent loop with your current environment's credentials." — Claude Code Docs

  • Hooks는 당신의 권한으로 실행됩니다
  • 악의적인 hooks는 데이터를 유출할 수 있습니다
  • 항상 hooks 코드를 검토하세요

성능

  • 너무 많은 hooks는 속도를 늦춥니다
  • 무거운 작업은 백그라운드로 (& 사용)
  • 불필요하게 넓은 matcher 피하기

"If hooks are unnecessarily firing you will have an extremely slowed down Agent." — ClaudeLog

디버깅

# Hook 설정 확인
cat .claude/settings.json | jq '.hooks'

# 수동으로 명령어 테스트
npx prettier --write test.ts

# 환경 변수 확인
echo $CLAUDE_PROJECT_DIR

Git Hooks와의 차이점

구분 Claude Code Hooks Git Hooks

트리거 Claude 도구 사용 시 Git 명령 실행 시
이벤트 PreToolUse, PostToolUse 등 pre-commit, post-commit 등
목적 Claude 작업 자동화 Git 워크플로우 자동화
설정 위치 .claude/settings.json .git/hooks/

둘 다 사용하면 더 강력합니다:

  • Claude Code Hooks: 파일 편집 시 즉시 포맷팅
  • Git Hooks: 커밋 전 최종 검증

체크리스트

기본 설정

  • [ ] 자동 포맷팅 (PostToolUse + Prettier)
  • [ ] 자동 린트 (PostToolUse + ESLint)
  • [ ] 위험 명령어 차단 (PreToolUse)

권장 설정

  • [ ] console.log 감지
  • [ ] main 브랜치 직접 편집 차단
  • [ ] 자주 쓰는 명령 자동 승인 (PermissionRequest)

고급 설정

  • [ ] 세션 시작 시 컨텍스트 주입
  • [ ] 테스트 자동 실행
  • [ ] 커스텀 검증 스크립트

마무리

Hooks는 Claude Code를 당신의 워크플로우에 맞게 커스터마이징하는 강력한 도구입니다.

기억하세요:

  • 한 번 설정하면 매번 자동 실행
  • 포맷팅, 린트는 PostToolUse
  • 위험한 명령 차단은 PreToolUse
  • 자주 쓰는 명령 승인은 PermissionRequest
  • 성능과 보안을 항상 고려하세요

"Hooks let you shape Claude Code to match your workflow rather than adapting your workflow to the tool." — Claude Blog

설정하는 데 10분이면 됩니다. 그 10분이 앞으로 수많은 실수를 방지하고, 반복 작업을 자동화해줄 겁니다.


참고 자료

Anthropic 공식

커뮤니티


 

반응형