Claude Code 훅 시스템의 개념과 라이프사이클을 이해하고, 코드 품질 게이트와 자동화 파이프라인을 구축하는 방법을 다룹니다.
훅(Hooks)은 Claude Code의 라이프사이클 특정 지점에서 자동으로 실행되는 사용자 정의 동작입니다. CLAUDE.md가 Claude에게 "무엇을 해야 하는지" 알려주는 정적 지시서라면, 훅은 "언제 무엇을 자동으로 할지" 정의하는 동적 자동화 메커니즘입니다.
훅의 핵심 가치는 결정론적 실행에 있습니다. 프롬프트의 표현이나 모델의 판단에 관계없이, 조건이 충족되면 반드시 실행됩니다. 예를 들어 "파일을 수정한 후에는 항상 린트를 돌려 줘"라는 CLAUDE.md 지시는 Claude가 잊거나 무시할 수 있지만, PostToolUse 훅으로 설정하면 100% 실행됩니다.
Claude Code는 다양한 라이프사이클 지점에서 훅을 실행할 수 있습니다. 주요 이벤트를 실행 순서대로 살펴보겠습니다.
| 이벤트 | 실행 시점 | 주요 용도 |
|---|---|---|
SessionStart | 세션 시작 시 | 환경 검증, 동적 컨텍스트 로드 |
UserPromptSubmit | 사용자 프롬프트 제출 시 | 입력 검증, 프롬프트 변환 |
PreToolUse | 도구 실행 직전 | 위험 명령어 차단, 입력 검증 |
PostToolUse | 도구 실행 직후 | 린트 자동 실행, 결과 검증 |
Stop | Claude 응답 완료 시 | 최종 검증, 자동 후처리 |
SubagentStop | 서브에이전트 완료 시 | 서브에이전트 결과 검증 |
Notification | 알림 발생 시 | 외부 알림 전송 |
PreCompact | 컨텍스트 압축 직전 | 중요 정보 보존 |
PostCompact | 컨텍스트 압축 직후 | 압축 후 컨텍스트 보강 |
SessionEnd | 세션 종료 시 | 정리 작업, 로그 기록 |
훅은 .claude/settings.json 또는 ~/.claude/settings.json에 정의합니다. 기본 구조는 다음과 같습니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "npx eslint --fix $CLAUDE_FILE_PATH",
"timeout": 30000
}
]
}
}| 필드 | 설명 | 필수 |
|---|---|---|
matcher | 훅을 실행할 조건 (도구 이름 등) | 선택 |
command | 실행할 셸 명령어 | 필수 |
timeout | 실행 제한 시간 (ms) | 선택 |
matcher는 정규식 패턴을 지원합니다. Write|Edit은 Write 또는 Edit 도구가 사용될 때 매칭됩니다. matcher를 생략하면 해당 이벤트의 모든 호출에서 실행됩니다.
훅 명령어에서 사용할 수 있는 환경 변수가 제공됩니다.
| 변수 | 설명 |
|---|---|
$CLAUDE_FILE_PATH | 도구가 조작한 파일 경로 |
$CLAUDE_TOOL_NAME | 실행된 도구 이름 |
$CLAUDE_TOOL_INPUT | 도구 입력 (JSON) |
$CLAUDE_TOOL_OUTPUT | 도구 출력 (JSON) |
$CLAUDE_SESSION_ID | 현재 세션 ID |
$CLAUDE_PROJECT_DIR | 프로젝트 루트 경로 |
가장 일반적인 훅입니다. 파일을 수정할 때마다 자동으로 ESLint와 Prettier를 실행합니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "npx eslint --fix \"$CLAUDE_FILE_PATH\" 2>/dev/null; npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null",
"timeout": 15000
}
]
}
}2>/dev/null로 에러 출력을 억제하면, 린트 대상이 아닌 파일(예: .md, .json) 수정 시 불필요한 에러 메시지가 Claude의 컨텍스트를 오염시키는 것을 방지합니다.
프로덕션 데이터베이스에 대한 위험한 명령어를 사전에 차단합니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | python3 -c \"import sys,json; cmd=json.load(sys.stdin).get('command',''); exit(1 if any(w in cmd for w in ['DROP TABLE','DELETE FROM','--force','rm -rf /']) else 0)\"",
"timeout": 5000
}
]
}
}PreToolUse 훅이 비정상 종료(exit code != 0)를 반환하면 해당 도구 실행이 차단됩니다.
세션이 시작될 때 필수 도구와 환경 변수가 올바르게 설정되어 있는지 확인합니다.
{
"hooks": {
"SessionStart": [
{
"command": "node --version >/dev/null 2>&1 && pnpm --version >/dev/null 2>&1 && echo 'Environment OK' || echo 'WARNING: Required tools missing'",
"timeout": 10000
}
]
}
}파일 수정 후 TypeScript 타입 에러를 자동으로 감지합니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '\\.(ts|tsx)$'; then npx tsc --noEmit --pretty 2>&1 | head -20; fi",
"timeout": 30000
}
]
}
}Git 커밋 시 Conventional Commits 형식을 강제합니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | python3 -c \"import sys,json; cmd=json.load(sys.stdin).get('command',''); exit(1 if 'git commit' in cmd and not any(cmd.find(p)>0 for p in ['feat:','fix:','refactor:','docs:','chore:','test:','style:','perf:']) else 0)\"",
"timeout": 5000
}
]
}
}Claude Code 작업이 완료되면 Slack이나 Discord로 알림을 보냅니다.
{
"hooks": {
"Notification": [
{
"command": "curl -s -X POST \"$SLACK_WEBHOOK_URL\" -H 'Content-Type: application/json' -d \"{\\\"text\\\": \\\"Claude Code: $CLAUDE_TOOL_OUTPUT\\\"}\"",
"timeout": 10000
}
]
}
}JSON을 직접 편집하는 대신 /hooks 명령어로 대화형 인터페이스를 사용할 수도 있습니다.
/hooks메뉴에서 이벤트를 선택하고, 명령어와 매처를 입력하면 자동으로 settings.json에 추가됩니다. JSON 문법 오류를 방지할 수 있어 초보자에게 유용합니다.
훅이 예상대로 동작하지 않을 때 확인할 사항입니다.
훅 명령어에 로그를 추가하여 실행 여부를 확인합니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"command": "echo \"[HOOK] File written: $CLAUDE_FILE_PATH\" >> /tmp/claude-hooks.log && npx eslint --fix \"$CLAUDE_FILE_PATH\"",
"timeout": 15000
}
]
}
}훅이 실행되지 않는 경우:
matcher 패턴이 도구 이름과 일치하는지 확인합니다settings.json의 JSON 문법이 올바른지 확인합니다훅이 타임아웃되는 경우:
timeout 값을 늘립니다훅이 도구 실행을 차단하는 경우:
훅은 매 도구 호출마다 실행되므로 속도가 중요합니다. 무거운 작업은 백그라운드로 실행하거나, 특정 파일 패턴에만 매칭되도록 조건을 좁힙니다.
{
"matcher": "Write|Edit",
"command": "npx tsc --noEmit"
}{
"matcher": "Write|Edit",
"command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '\\.(ts|tsx)$'; then npx tsc --noEmit 2>&1 | head -5; fi"
}훅의 exit code와 출력은 Claude의 컨텍스트에 반영됩니다. 실패 시 명확한 메시지를 출력하면 Claude가 문제를 이해하고 스스로 수정할 수 있습니다.
여러 훅을 조합하면 완전한 품질 파이프라인을 구축할 수 있습니다.
{
"hooks": {
"SessionStart": [
{
"command": "pnpm install --frozen-lockfile 2>/dev/null; echo 'Dependencies verified'",
"timeout": 30000
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '\\.(ts|tsx)$'; then npx eslint --fix \"$CLAUDE_FILE_PATH\" 2>/dev/null; fi",
"timeout": 15000
}
],
"Stop": [
{
"command": "npx tsc --noEmit 2>&1 | tail -5",
"timeout": 60000
}
]
}
}이 설정은 다음과 같은 파이프라인을 구성합니다:
훅은 Claude Code의 자동화 핵심 메커니즘입니다.
다음 장에서는 훅과 함께 Claude Code를 확장하는 또 다른 축인 커스텀 슬래시 명령어와 스킬 시스템을 다룹니다.
이 글이 도움이 되셨나요?
Claude Code의 스킬 시스템을 이해하고, 반복 작업을 자동화하는 커스텀 슬래시 명령어를 설계하고 구현하는 방법을 다룹니다.
Claude Code로 코드를 작성하고, 리뷰하고, 리팩터링하는 핵심 워크플로우를 실전 예제와 함께 익힙니다.
Model Context Protocol의 개념을 이해하고, Claude Code에 MCP 서버를 연결하여 데이터베이스, API, 외부 서비스를 통합하는 방법을 다룹니다.