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
가장 많이 쓰는 이벤트
- PostToolUse: 파일 편집 후 자동 포맷팅/린트
- PreToolUse: 위험한 명령어 차단
- 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 공식
- Get Started with Claude Code Hooks — Official Docs
- Hooks Reference — Official Docs
- How to Configure Hooks — Claude Blog
커뮤니티
- claude-code-hooks-mastery — GitHub
- claude-code-typescript-hooks — GitHub
- awesome-claude-code — Hooks 컬렉션
'Programmer > AI' 카테고리의 다른 글
| Claude Code 커스텀 명령어: 혼자서도 팀처럼 개발하기 (0) | 2026.01.21 |
|---|---|
| Claude Code Skills & Agents: 전문가 팀을 내 손안에 (0) | 2026.01.21 |
| Claude Code 환경 설정: CLAUDE.md로 10분 투자해서 수 시간 절약하기 (0) | 2026.01.21 |
| Claude Code 컨텍스트 관리: 채워질수록 썩는 작업 기억 (1) | 2026.01.20 |
| Claude Code의 게임체인저: Plan Mode vs YOLO Mode (1) | 2026.01.20 |