Hooks 레퍼런스
Hooks는 Claude Code의 라이프사이클 이벤트에 쉘 명령어를 연결하는 기능입니다. 도구 실행 전후, 응답 생성 후, 세션 시작/종료 등 다양한 시점에 커스텀 로직을 실행할 수 있습니다.
개요
Claude Code hooks를 사용하면:
- 도구 실행을 가로채고 검증하거나 차단할 수 있음
- 자동화된 워크플로우를 트리거할 수 있음
- 모든 작업의 감사 로그를 유지할 수 있음
- 팀 전체에 일관된 동작을 강제할 수 있음
- 외부 시스템과 통합할 수 있음
설정
hooks는 settings.json의 hooks 섹션에서 구성합니다:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "validate-command.sh"
}
]
}
]
}
}
설정 파일 위치
| 범위 | 경로 |
|---|---|
| 사용자 전체 | ~/.claude/settings.json |
| 프로젝트 | .claude/settings.json |
| 프로젝트 공유 | .claude/settings.shared.json |
Hook 이벤트 유형
PreToolUse
도구가 실행되기 전에 실행됩니다. 종료 코드 2로 도구 실행을 차단할 수 있습니다.
입력 스키마:
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"hook_event_name": "PreToolUse",
"tool_name": "string",
"tool_input": {}
}
종료 코드 동작:
| 종료 코드 | 동작 |
|---|---|
0 |
도구 실행 계속 |
2 |
도구 실행 차단 (stdout이 Claude에게 피드백으로 전달됨) |
| 그 외 | 도구 실행 계속 (stderr는 Claude에게 전달됨) |
PostToolUse
도구가 실행된 후에 실행됩니다. 결과 검사 및 로깅에 활용합니다.
추가 입력 필드:
{
"tool_response": {}
}
Notification
Claude Code가 알림을 보낼 때 실행됩니다.
추가 입력 필드:
{
"message": "string"
}
Stop
Claude Code가 응답을 완료할 때 실행됩니다.
추가 입력 필드:
{
"stop_hook_active": true
}
중요:
stop_hook_active가true일 때 hook이 재진입되는 경우입니다. 무한 루프 방지를 위해 반드시 확인하세요.
SubagentStop
서브에이전트가 완료될 때 실행됩니다. Stop과 동일한 스키마를 사용합니다.
Matcher 설정
matcher 필드로 특정 도구에만 hook을 적용할 수 있습니다.
{ "matcher": "Bash" } // 정확한 이름
{ "matcher": "Bash|Write|Edit" } // 정규식
{ "matcher": "mcp__fs__write" } // MCP 도구
// matcher 생략 시 모든 도구에 적용
환경 변수
| 변수 | 설명 |
|---|---|
CLAUDE_TOOL_INPUT |
JSON 형식의 도구 입력 |
CLAUDE_TOOL_RESPONSE |
JSON 형식의 도구 응답 (PostToolUse) |
CLAUDE_NOTIFICATION_MESSAGE |
알림 메시지 (Notification) |
CLAUDE_STOP_HOOK_ACTIVE |
Stop hook 재진입 여부 |
CLAUDE_SESSION_ID |
현재 세션 ID |
CLAUDE_TRANSCRIPT_PATH |
트랜스크립트 파일 경로 |
stdin으로도 동일한 데이터를 JSON으로 받을 수 있습니다.
실용적인 예시
위험한 명령어 차단
#!/bin/bash
COMMAND=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.command // ""')
if echo "$COMMAND" | grep -qE "rm -rf /|rm -rf ~"; then
echo "보안: 위험한 명령어가 차단되었습니다."
exit 2
fi
자동 코드 포맷팅
#!/bin/bash
FILE=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path // ""')
[[ "$FILE" == *.ts || "$FILE" == *.tsx ]] && npx prettier --write "$FILE" 2>/dev/null
[[ "$FILE" == *.py ]] && black "$FILE" 2>/dev/null
감사 로깅
#!/bin/bash
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
TOOL=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.tool_name // "unknown"' 2>/dev/null)
echo "$TIMESTAMP | $TOOL | $PWD" >> "$HOME/claude-audit.log"
Stop hook으로 자동 테스트
#!/bin/bash
# 무한 루프 방지
[ "$CLAUDE_STOP_HOOK_ACTIVE" = "true" ] && exit 0
npm test 2>&1
[ $? -ne 0 ] && echo "테스트 실패 - 수정 필요" && exit 1
보안 고려사항
- 무한 루프 방지: Stop hook에서
stop_hook_active반드시 확인 - 명령어 주입 방지: hook 입력을
eval에 직접 전달하지 않기 - 최소 권한: hook 스크립트는 필요한 권한만 부여
- 민감 데이터: 트랜스크립트/로그에 비밀번호가 노출될 수 있음
디버깅
# 디버그 로그 활성화
echo "$(date): Hook triggered | Input: $CLAUDE_TOOL_INPUT" >> /tmp/hook-debug.log
| 문제 | 원인 | 해결 |
|---|---|---|
| Hook 미실행 | 실행 권한 없음 | chmod +x hook.sh |
| JSON 파싱 오류 | jq 미설치 |
brew install jq |
| 무한 루프 | stop_hook_active 미확인 |
Stop hook에서 확인 추가 |
