실전 Hooks 패턴 모음
Claude Code Hooks 시스템을 활용한 실전 자동화 패턴입니다. 각 패턴에 전체 설정 JSON과 필요한 스크립트를 포함합니다.
Hooks 템플릿:
01-dev-rules-system/02-project-template/.claude/hooks-template.json
패턴 목록
| # | 패턴 | Event | Type | 용도 |
|---|---|---|---|---|
| 1 | .env 커밋 방지 | PreToolUse | command | git add .env 차단 |
| 2 | 자동 코드 포맷팅 | PostToolUse | command | prettier/eslint 자동 실행 |
| 3 | 코드 변경 후 테스트 제안 | Stop | prompt | 변경 파일 관련 테스트 안내 |
| 4 | 커밋 메시지 검증 | PreToolUse | command | conventional commits 준수 |
| 5 | 세션 시작 컨텍스트 로드 | SessionStart | command | current.md + handover 자동 로드 |
| 6 | 대용량 출력 경고 | PostToolUse | prompt | 출력이 너무 클 때 요약 제안 |
| 7 | 위험 명령 차단 | PreToolUse | command | rm -rf, force push 등 차단 |
1. .env 커밋 방지
Event: PreToolUse | Matcher: Bash | Type: command
.env, .env.local 등 환경변수 파일이 git에 커밋되는 것을 차단합니다.
설정
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/prevent-env-commit.sh \"$TOOL_INPUT\"",
"timeout": 5
}
]
}
]
}
}
스크립트 (.claude/hooks/prevent-env-commit.sh)
#!/bin/bash
TOOL_INPUT="$1"
# git add 명령에서 .env 파일 포함 여부 확인
if echo "$TOOL_INPUT" | grep -qE 'git\s+add.*\.env'; then
cat <<'EOF'
{
"decision": "block",
"reason": "🚫 .env 파일은 커밋할 수 없습니다. .gitignore에 추가하세요."
}
EOF
exit 2
fi
# staged 파일 중 .env 확인
if echo "$TOOL_INPUT" | grep -qE 'git\s+commit'; then
STAGED_ENV=$(git diff --cached --name-only 2>/dev/null | grep -E '\.env' || true)
if [ -n "$STAGED_ENV" ]; then
echo '{"decision":"block","reason":"🚫 staged 파일에 .env가 포함되어 있습니다."}'
exit 2
fi
fi
exit 0
동작 원리
exit 2→ 도구 실행 차단 (block)exit 0→ 정상 통과 (allow)- JSON
decision필드로 차단 이유 전달
2. 자동 코드 포맷팅
Event: PostToolUse | Matcher: Write|Edit | Type: command
파일 작성/편집 후 prettier, eslint, ruff 등으로 자동 포맷팅합니다.
설정
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/auto-format.sh \"$TOOL_INPUT\"",
"timeout": 30
}
]
}
]
}
}
스크립트 (.claude/hooks/auto-format.sh)
#!/bin/bash
TOOL_INPUT="$1"
FILE_PATH=$(echo "$TOOL_INPUT" | grep -oP '"file_path"\s*:\s*"([^"]+)"' | sed 's/.*"file_path"\s*:\s*"//;s/"$//')
[ -z "$FILE_PATH" ] && exit 0
EXT="${FILE_PATH##*.}"
case "$EXT" in
js|jsx|ts|tsx|css|json|md|html)
[ -f "node_modules/.bin/prettier" ] && npx prettier --write "$FILE_PATH" 2>/dev/null
case "$EXT" in js|jsx|ts|tsx)
[ -f "node_modules/.bin/eslint" ] && npx eslint --fix "$FILE_PATH" 2>/dev/null || true
;; esac
;;
py)
command -v ruff &>/dev/null && ruff format "$FILE_PATH" 2>/dev/null
;;
esac
exit 0
3. 코드 변경 후 테스트 제안
Event: Stop | Matcher: (없음) | Type: prompt
Claude가 응답을 완료할 때, 코드 변경이 있었다면 관련 테스트 실행을 제안합니다.
설정
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "prompt",
"prompt": "방금 코드 변경사항이 있다면, 변경된 파일과 관련된 테스트 파일을 찾아 실행을 제안하세요. 변경이 없거나 테스트 파일이 없으면 아무 것도 하지 마세요."
}
]
}
]
}
}
동작 원리
prompt타입은 Claude에게 추가 지시를 주입- Stop 이벤트는 Claude 응답 완료 시 발생
- 불필요한 동작을 방지하기 위해 조건문 명시
4. 커밋 메시지 검증
Event: PreToolUse | Matcher: Bash | Type: command
Conventional Commits 형식을 준수하는지 검증합니다.
설정
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/validate-commit-msg.sh \"$TOOL_INPUT\"",
"timeout": 5
}
]
}
]
}
}
스크립트 (.claude/hooks/validate-commit-msg.sh)
#!/bin/bash
TOOL_INPUT="$1"
# git commit 명령인지 확인
if ! echo "$TOOL_INPUT" | grep -qE 'git\s+commit'; then
exit 0
fi
# 커밋 메시지 추출
COMMIT_MSG=$(echo "$TOOL_INPUT" | grep -oP '(?<=-m\s["\x27])[^"\x27]+' || true)
if [ -z "$COMMIT_MSG" ]; then
exit 0
fi
# Conventional Commits 패턴 검증
# feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert
if ! echo "$COMMIT_MSG" | grep -qE '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?!?:'; then
cat <<'EOF'
{
"decision": "block",
"reason": "🚫 커밋 메시지가 Conventional Commits 형식이 아닙니다.\n예시: feat(auth): 로그인 기능 추가\n형식: <type>(<scope>): <description>"
}
EOF
exit 2
fi
exit 0
5. 세션 시작 컨텍스트 로드
Event: SessionStart | Matcher: (없음) | Type: command
세션 시작 시 프로젝트 상태 문서를 자동으로 Claude 컨텍스트에 로드합니다.
설정
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/load-context.sh"
}
]
}
]
}
}
스크립트
01-dev-rules-system/02-project-template/.claude/hooks/load-context.sh 참조.
동작 원리
SessionStart는 Claude Code 세션이 열릴 때 1회 실행- stdout의 JSON
content필드가 시스템 프롬프트에 주입 docs/status/current.md+ 최신docs/handover/*.md로드
6. 대용량 출력 경고
Event: PostToolUse | Matcher: Bash | Type: prompt
Bash 명령 출력이 너무 클 때 요약을 제안합니다.
설정
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "prompt",
"prompt": "방금 실행된 명령의 출력이 200줄을 초과하면, 출력의 핵심 내용만 요약하고 전체 출력을 파일로 저장할 것을 제안하세요. 200줄 이하면 아무 것도 하지 마세요."
}
]
}
]
}
}
7. 위험 명령 차단
Event: PreToolUse | Matcher: Bash | Type: command
rm -rf, git push --force, DROP TABLE 등 위험 명령을 차단합니다.
설정
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-dangerous.sh \"$TOOL_INPUT\"",
"timeout": 5
}
]
}
]
}
}
스크립트 (.claude/hooks/block-dangerous.sh)
#!/bin/bash
TOOL_INPUT="$1"
# 위험 패턴 목록
DANGEROUS_PATTERNS=(
'rm\s+-rf\s+/'
'rm\s+-rf\s+\.'
'git\s+push.*--force'
'git\s+reset\s+--hard'
'DROP\s+TABLE'
'DROP\s+DATABASE'
'truncate\s+table'
'git\s+clean\s+-fd'
)
for pattern in "${DANGEROUS_PATTERNS[@]}"; do
if echo "$TOOL_INPUT" | grep -qEi "$pattern"; then
cat <<EOF
{
"decision": "block",
"reason": "⚠️ 위험한 명령이 감지되었습니다. 사용자에게 확인을 요청하세요.\n감지된 패턴: $pattern"
}
EOF
exit 2
fi
done
exit 0
Hooks 설정 병합 가이드
여러 패턴을 조합할 때, 같은 이벤트의 hooks는 배열로 병합합니다:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": ".claude/hooks/prevent-env-commit.sh \"$TOOL_INPUT\"" },
{ "type": "command", "command": ".claude/hooks/block-dangerous.sh \"$TOOL_INPUT\"" },
{ "type": "command", "command": ".claude/hooks/validate-commit-msg.sh \"$TOOL_INPUT\"" }
]
}
]
}
}
실행 순서
- 같은 matcher의 hooks는 배열 순서대로 실행
- 하나라도
exit 2(block)를 반환하면 이후 hooks 중단 exit 1은 에러(무시 가능),exit 0은 정상 통과