Claude Code Hooks로 자동화하는 워크플로우 7가지
2026.04.13
매번 같은 말을 반복하는 게 지겨워졌다
"커밋 전에 lint 한 번 돌려줘", "긴 작업 끝나면 알림 보내줘", "파일 수정하면 prettier 돌려줘". 작업할 때마다 같은 부탁을 하고 있는 나를 발견한 게 시작이었다.
Claude Code Hooks는 이런 부탁을 명시적인 규칙으로 박아두는 시스템이다. 특정 이벤트(파일 수정, 도구 호출, 세션 시작 등)가 발생할 때 자동으로 쉘 명령을 실행한다. 한 번 설정하면 잊고 살 수 있다.
이 글은 내가 실제로 settings.json에 박아둔 7가지 훅이다.
Hook의 기본 구조
훅은 ~/.claude/settings.json 또는 프로젝트의 .claude/settings.json에 정의한다.
{
"hooks": {
"<event-name>": [
{
"matcher": "<tool-or-path-pattern>",
"hooks": [
{ "type": "command", "command": "<shell-command>" }
]
}
]
}
}이벤트 이름은 정해져 있다. PreToolUse, PostToolUse, SessionStart, UserPromptSubmit, Stop 등이다. matcher는 어떤 도구나 경로에 적용할지 필터다.
1. 파일 수정 후 자동 prettier
Edit, Write 도구로 코드 파일이 수정되면 prettier를 돌린다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "f=$(jq -r '.tool_input.file_path'); if echo \"$f\" | grep -E '\\.(ts|tsx|js|jsx)$' > /dev/null; then npx prettier --write \"$f\" 2>/dev/null; fi"
}
]
}
]
}
}훅은 stdin으로 JSON을 받는다. jq -r '.tool_input.file_path'로 수정된 파일 경로를 꺼낸다. 확장자가 JS/TS 계열일 때만 prettier를 돌린다. 효과는 즉시 체감된다 — AI가 만든 코드가 항상 컨벤션에 맞춰 정렬된 채로 들어온다.
2. 위험한 명령어 차단
PreToolUse로 Bash 호출을 검사해서 위험한 명령을 막는다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if jq -r '.tool_input.command' | grep -E '(rm -rf /|--no-verify|push --force.*main)' > /dev/null; then echo 'BLOCKED: dangerous command'; exit 2; fi"
}
]
}
]
}
}PreToolUse 훅이 종료 코드 2를 반환하면 도구 실행이 차단되고, stderr 메시지가 Claude에게 전달된다. 메인 브랜치 force push, 시스템 루트 삭제, hooks 우회 같은 패턴을 미리 잡는다. AI가 막힌다는 걸 알면 다른 방법을 찾는다.
3. 세션 시작 시 컨텍스트 주입
SessionStart 훅으로 매 세션마다 자동으로 환경 정보를 추가한다.
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo \"[ENV] node=$(node -v) | branch=$(git branch --show-current 2>/dev/null) | last-commit=$(git log -1 --format=%s 2>/dev/null)\""
}
]
}
]
}
}훅의 stdout이 시스템 메시지로 Claude에게 전달된다. 매번 "지금 브랜치가 뭐야?"를 물어볼 필요가 없다. 이미 컨텍스트에 있다.
4. 긴 작업 끝나면 데스크탑 알림
Stop 훅으로 응답이 끝날 때 macOS 알림을 띄운다.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Task complete\" with title \"Claude Code\" sound name \"Glass\"'"
}
]
}
]
}
}AI가 5분짜리 작업을 하는 동안 다른 일을 하다가, 알림이 뜨면 돌아오면 된다. Linux면 notify-send, Windows는 PowerShell BurntToast로 바꾸면 된다.
5. 커밋 전 자동 테스트
PreToolUse로 git commit Bash 호출을 가로채 테스트를 먼저 돌린다.
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if jq -r '.tool_input.command' | grep -E 'git commit' > /dev/null; then npm test --silent || (echo 'Tests failed'; exit 2); fi"
}
]
}테스트가 깨지면 커밋이 차단된다. AI가 "테스트는 다음에"를 못 하게 막는 강제 장치다. CI에서 잡힐 깨진 커밋을 로컬에서 미리 거른다.
6. 사용자 프롬프트에 자동 라벨 붙이기
UserPromptSubmit 훅으로 입력에 메타데이터를 붙인다.
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "echo \"[date=$(date +%F) | cwd=$(pwd | xargs basename)]\""
}
]
}
]
}
}훅의 stdout이 사용자 입력 앞에 추가 컨텍스트로 붙는다. 날짜와 작업 디렉토리를 매번 자동 주입하니까, "지금 날짜를 모르는 AI" 문제가 사라진다.
7. PostToolUse로 Notion에 작업 로그 적재
길게 협업하는 프로젝트에서는 어떤 변경이 있었는지 기록이 필요하다. PostToolUse에서 변경 정보를 외부로 보낸다.
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "f=$(jq -r '.tool_input.file_path'); curl -s -X POST https://my-logger.internal/log -H 'Content-Type: application/json' -d \"{\\\"file\\\":\\\"$f\\\",\\\"ts\\\":\\\"$(date +%s)\\\"}\" || true"
}
]
}|| true를 붙여서 외부 서버가 죽어도 작업이 멈추지 않게 한다. 훅의 실패가 작업 자체를 막으면 안 되는 케이스다.
훅 디버깅 팁
훅을 처음 짜면 안 동작하는 경우가 절반이다. 내가 만난 흔한 실수들이다.
settings.json은 strict JSON
주석을 못 단다. 마지막 콤마(trailing comma)도 안 된다. 한 글자 틀리면 통째로 무시된다. cat ~/.claude/settings.json | jq .으로 일단 파싱이 되는지 확인한다.
stdin JSON 필드 이름 정확히
훅 입력 데이터는 환경변수가 아니라 stdin으로 들어오는 JSON이다. tool_name, tool_input.file_path, tool_input.command 같은 필드를 jq로 꺼내 쓴다. $CLAUDE_PROJECT_DIR 정도가 모든 훅에서 쓸 수 있는 환경변수다. 어떤 이벤트에서 어떤 필드가 들어오는지는 공식 문서에서 확인한다.
훅이 stdin/stdout을 막으면 안 된다
대화형 명령(vim, read 등)을 훅에 박으면 Claude Code가 멈춘다. 훅은 항상 비대화형, 빠르게 끝나야 한다. 오래 걸리는 작업은 &로 백그라운드 처리하거나, 별도 큐에 넣는다.
훅 실행 로그
문제 진단할 때는 훅 안에서 stderr로 디버그 로그를 남긴다.
echo "[hook] file=$(jq -r '.tool_input.file_path') ts=$(date)" >&2stderr는 사용자에게 보이지 않지만, Claude Code 로그에는 남는다.
직접 겪은 일: 일곱 개를 늘어놨지만 살아남은 건 네 개다
위에 일곱 가지를 적었지만, 한참 쓰고 난 지금 내 ~/.claude/settings.json에 실제로 남아 있는 훅은 네 개다.
셋은 같은 명령 하나를 부른다. SessionStart, UserPromptSubmit, Stop이 전부 세션 상태를 밖으로 알리는 명령 하나를 5초 타임아웃으로 돌린다. 매 프롬프트마다 도는 훅이라 여기서 1초만 밀려도 체감이 확 나빠진다.
나머지 하나가 SessionEnd다. 세션이 끝나면 작업 요약을 Obsidian에 저장하는 169줄짜리 셸 스크립트를 돌린다. 이쪽은 타임아웃을 120초로 열어두고 async: true를 붙였다. 어차피 끝나는 길인데 막을 이유가 없다.
훅을 여러 개 붙여 보고 남은 기준은 이거였다. 훅의 성격은 무엇을 하느냐가 아니라 얼마나 자주 도느냐로 갈린다. 자주 도는 훅은 짧고 조용해야 하고, 한 번만 도는 훅은 느려도 된다. 둘에 같은 타임아웃을 주면 반드시 한쪽이 불편해진다.
정리
Hooks는 작은 시스템이지만 효과가 크다. 하루에 같은 부탁을 5번 하고 있다면, 그건 훅 후보다. 한 번 박아두면 그 부탁을 다시는 안 해도 된다.
내가 추천하는 시작점은 PostToolUse + prettier 하나다. 효과가 즉각적이라 훅이 무엇을 하는지 감이 빠르게 잡힌다. 거기서부터 알림, 차단, 컨텍스트 주입 순서로 늘려가면 된다.
설정 파일 한 개에 자동화가 박혀 있으니, 새 컴퓨터로 옮길 때도 그 파일만 복사하면 끝이다. dotfiles에 settings.json을 넣어둔 다음부터, 환경 세팅이 분 단위로 짧아졌다.
같이 읽으면 좋은 글
컨텍스트를 비우는 두 도구 — /compact와 /clear는 언제 다르게 쓰나
/context로 범인을 찾았다면 다음은 치우는 법이다. 대화를 요약해 이어가는 /compact와 아예 비우고 새로 시작하는 /clear를, 언제 어느 쪽을 써야 하는지 기준과 함께 정리한다.
Claude Code토큰관리생산성2026.05.01내 토큰을 누가 먹고 있나 — Claude Code /context로 컨텍스트 해부하기
컨텍스트가 꽉 차서 느려지고 자동 압축되는데 원인을 모른다면, /context 하나면 된다. 무엇이 토큰을 먹는지 카테고리별로 분해해서 보는 법과, 범인별 대응법을 정리한다.
Claude Code토큰관리생산성2026.04.30AI에게 '일단 만들어줘'라고 하지 마라 — brainstorming 스킬
코드부터 시키면 잘못된 걸 빠르게 만든다. 승인 전 구현을 막는 brainstorming 스킬을 HARD-GATE, 한 번에 한 질문, 2-3 접근법 제시까지 실전 사용법으로 정리한다.
AI설계Claude Code2026.04.29