클로드 코드 훅 실전 (저장 뒤 검사와 커밋 전 검사)
클로드 코드 훅은 도구를 쓰기 전후나 응답이 끝날 때처럼 정해진 시점에 자동으로 실행되는 명령으로, 모델의 판단과 관계없이 검사와 차단을 거는 장치입니다. 저장 뒤 검사는 PostToolUse로, 커밋 전 검사는 PreToolUse와 종료 코드 2로 만듭니다.
같은 말:클로드 코드 훅 예시PreToolUsePostToolUsehooks settings.json커밋 전 검사
목차
이 글은 앤트로픽이 운영하는 code.claude.com/docs의 Hooks 참고 문서와 Hooks 가이드를 한국어 사용자가 실무에 바로 옮길 수 있도록 정리한 글입니다. 이벤트 이름과 설정 키는 2026년 9월 28일 공식 문서 기준이고, 원문 링크는 글 끝 참고 자료에 모았습니다.
🤔 "저장하면 포맷 돌려 줘"를 매번 말하고 있을 때
CLAUDE.md에 "파일을 고친 뒤에는 포맷터를 돌린다", "테스트가 통과하지 않으면 커밋하지 않는다"라고 적어 두어도 클로드가 가끔 건너뜁니다. CLAUDE.md는 참고하는 맥락이라 반드시 지킨다는 보장이 없기 때문입니다. 공식 문서도 모델의 판단과 관계없이 막아야 하는 일은 훅으로 옮기라고 안내합니다.
훅이 무엇인지는 입문 코스의 클로드 코드 훅 알아보기에 있습니다. 지금부터는 실제로 설정 파일에 훅을 적는 방법, 종료 코드가 무엇을 뜻하는지, 저장 뒤 검사와 커밋 전 검사를 어떻게 만드는지 정리합니다.
🔑 클로드 코드 훅의 정의
클로드 코드 훅은 도구를 쓰기 전후나 응답이 끝날 때처럼 정해진 시점에 자동으로 실행되는 명령으로, 모델의 판단과 관계없이 검사와 차단을 거는 장치입니다.
훅으로 실행할 수 있는 것은 셸 명령(command)이 가장 흔하고, 그 밖에 HTTP 주소 호출(http), MCP 도구 호출(mcp_tool), 모델에게 묻는 프롬프트(prompt), 서브에이전트(agent, 실험 기능)가 있습니다. 공식 문서가 나열한 이벤트는 33개인데, 실무에서 자주 쓰는 것은 몇 개로 좁혀집니다.
| 이벤트 | 발동 시점 | 막을 수 있는지 |
|---|---|---|
SessionStart | 세션 시작, 재개, /clear 뒤 | 막지 못함 |
UserPromptSubmit | 사람이 프롬프트를 보냈을 때 | 막을 수 있음 |
PreToolUse | 도구를 실행하기 직전 | 막을 수 있음 |
PostToolUse | 도구 실행이 끝난 직후 | 막지 못함 (이미 실행됨) |
Stop | 클로드가 응답을 마칠 때마다 | 멈추지 못하게 할 수 있음 |
SessionEnd | 세션이 끝날 때 | 막지 못함 |
Stop은 세션이 끝날 때가 아니라 응답이 끝날 때마다 발동합니다. 세션 종료에 무언가를 걸고 싶다면 SessionEnd를 씁니다.
🧱 설정 구조와 두는 위치
훅은 설정 파일의 hooks 안에 이벤트, matcher 묶음, 핸들러 세 단계로 적습니다. matcher는 어떤 도구에 반응할지 거르는 조건입니다.
- matcher 쓰는 법:
"Edit|Write"처럼 글자와|만 쓰면 정확히 그 도구 이름들에 맞춥니다. 다른 기호가 섞이면 정규식으로 해석되므로Edit.*는NotebookEdit까지 잡습니다. 비우거나"*"를 쓰면 전부 맞춥니다 if필드: 권한 규칙 문법으로 도구 인자까지 거릅니다. 예를 들어"if": "Bash(git *)"는 git 명령에만 반응합니다. PreToolUse, PostToolUse 같은 도구 이벤트에서만 동작합니다- 두는 위치: 사용자 설정, 프로젝트 설정, 프로젝트 로컬 설정, 관리 정책, 플러그인, 스킬 frontmatter, 서브에이전트 frontmatter 일곱 곳입니다. 여러 곳의 훅은 덮어쓰지 않고 합쳐지며, 맞는 훅은 모두 동시에 실행됩니다
지금 등록된 훅은 /hooks로 봅니다. 이벤트별 개수와 matcher, 어느 설정 파일에서 왔는지 보여 주는 읽기 전용 화면이라, 추가와 수정은 설정 파일을 직접 고치거나 클로드에게 요청합니다.
📥 입력은 JSON, 결과는 종료 코드로
명령 훅은 표준 입력(stdin)으로 JSON을 받습니다. 공통으로 세션 ID, 작업 폴더(cwd), 권한 모드, 이벤트 이름이 들어 있고, 도구 이벤트에는 tool_name과 tool_input이 더해집니다. 셸 스크립트에서는 보통 jq로 필요한 값을 꺼냅니다.
훅의 결과는 종료 코드로 전합니다. 여기가 실무에서 가장 많이 틀리는 대목입니다.
| 종료 코드 | 뜻 | 결과 |
|---|---|---|
| 0 | 성공 | 작업 진행 |
| 2 | 차단 오류 | PreToolUse면 도구 호출을 막고, stderr 내용을 클로드에게 전함 |
| 1 등 그 밖의 값 | 비차단 오류 | 작업이 그대로 진행됨 |
공식 문서는 "대부분의 훅 이벤트에서 코드만으로 막는 종료 코드는 2뿐"이라고 경고합니다. 검사 스크립트가 실패해서 exit 1로 끝나면 막히지 않고 진행되고, 설정에 스크립트 경로를 잘못 적어 실행조차 되지 않아도 조용히 진행됩니다. 차단용 훅을 만들었다면 일부러 실패하는 경우를 한 번 만들어 정말 막히는지 확인해야 합니다.
종료 코드 대신 JSON을 출력해 결과를 전하는 방법도 있습니다. PreToolUse에서는 permissionDecision에 allow, deny, ask를 담아 돌려줍니다. 한 훅에서는 종료 코드 방식과 JSON 방식 가운데 하나만 쓰라고 문서가 권합니다.
💾 저장 뒤 검사: PostToolUse
파일을 고칠 때마다 포맷터를 돌리는 것은 공식 가이드의 첫 예시입니다. 프로젝트의 .claude/settings.json에 넣습니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}클로드가 Edit이나 Write로 파일을 고칠 때마다 그 파일 경로를 꺼내 prettier로 정리합니다. 성공하면 대화창에는 아무것도 뜨지 않으므로 파일이 바뀌었는지로 확인합니다. jq가 설치되어 있어야 합니다.
PostToolUse에서 알아 둘 점은 두 가지입니다. 도구는 이미 실행된 뒤라서 막을 수는 없고, 검사 결과를 클로드에게 알리려면 exit 2로 끝내 stderr를 전해야 합니다(exit 0의 stderr는 디버그 기록에만 남습니다). 또 Edit|Write matcher는 클로드가 셸 명령으로 바꾼 파일은 보지 못합니다. 모든 변경을 확인하려면 응답이 끝날 때마다 git status를 훑는 Stop 훅을 더합니다.
🔒 커밋 전 검사: PreToolUse와 exit 2
공식 문서에는 git 커밋을 가로채는 완성 예시가 없어서, 아래는 공식 문서의 규칙대로 준이아빠블로그가 만들어 표준 입력 예시로 동작을 확인한 예입니다. 먼저 .claude/hooks/pre-commit-check.sh를 만듭니다.
#!/bin/bash
# 클로드가 실행하려는 명령을 stdin JSON에서 꺼낸다
cmd=$(jq -r '.tool_input.command')
# git commit일 때만 검사한다
case "$cmd" in
*"git commit"*)
if ! npm run -s lint >&2; then
echo "lint가 실패해서 커밋을 막았습니다. 오류를 고친 뒤 다시 커밋하세요." >&2
exit 2
fi
;;
esac
exit 0chmod +x로 실행 권한을 준 뒤 설정 파일에 등록합니다. 경로는 공식 예시처럼 $CLAUDE_PROJECT_DIR로 적어야 작업 폴더가 바뀌어도 찾습니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git commit *)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-commit-check.sh"
}
]
}
]
}
}lint가 실패하면 exit 2로 커밋 명령이 막히고, stderr의 안내 문장이 클로드에게 전달되므로 클로드가 그 문장을 보고 오류를 고치는 쪽으로 작업을 이어 갈 수 있습니다. 이 훅은 클로드의 도구 호출에만 걸리므로, 사람이 터미널에서 직접 하는 커밋까지 막으려면 git 자체의 pre-commit 훅을 따로 둡니다. Git 작업 흐름 전체는 클로드 코드 Git 작업 흐름에서 이어집니다.
🛡️ 훅과 권한 규칙의 관계
PreToolUse 훅은 모든 권한 모드에서 권한 검사보다 먼저 실행됩니다. 훅이 deny를 돌려주면 bypassPermissions 모드에서도 막힙니다. 반대로 훅이 allow를 돌려줘도 설정의 deny 규칙은 넘지 못합니다. 공식 가이드의 표현대로 훅은 제한을 조일 수는 있어도 권한 규칙보다 느슨하게 풀 수는 없습니다.
if 필드의 거르기는 최선을 다하는 수준이라, 공식 문서는 반드시 허용하거나 막아야 하는 일에는 훅보다 권한 규칙을 쓰라고 권합니다. 두 가지를 함께 쓰는 조합이 흔합니다.
⚠️ 보안과 자주 하는 실수
- 남의 저장소 훅을 확인 없이 실행합니다: 명령 훅은 내 계정 권한으로 모든 파일에 닿습니다. 대화형 세션은 작업 공간 신뢰 창을 수락하기 전까지 훅을 보류하지만,
claude -p는 저장소에 커밋된 훅을 바로 실행합니다. 남의 저장소에서-p를 쓰기 전에는.claude/설정을 먼저 보거나--settings '{"disableAllHooks": true}'로 끕니다 - 차단 훅을 exit 1로 끝냅니다: 막히지 않습니다. exit 2를 씁니다
- 셸 변수를 따옴표 없이 씁니다: 공식 문서는
"$VAR"처럼 따옴표로 감싸고..경로를 막으라고 권합니다 - 훅을 너무 많이 겁니다: 모든 훅은 매번 실행되므로 느린 검사를 PostToolUse에 걸면 작업 전체가 느려집니다
❓ 자주 묻는 질문
훅 하나만 잠시 끌 수 있나요?
개별 훅만 끄는 설정은 없습니다. "disableAllHooks": true로 전부 끄거나, 설정 파일에서 그 훅을 지웁니다. 조직이 배포한 관리 정책 훅은 관리 정책 쪽 설정으로만 꺼집니다.
훅이 실행됐는지 어떻게 확인하나요?
성공한 훅은 대화창에 보이지 않는 경우가 많습니다. /hooks로 등록 상태를 보고, 실제 실행 여부는 결과물(바뀐 파일, 막힌 명령)이나 디버그 기록으로 확인합니다.
서브에이전트가 쓰는 도구에도 훅이 걸리나요?
걸립니다. 설정 파일의 훅은 서브에이전트 안의 도구 호출에도 발동하고, 입력에 어느 서브에이전트인지가 함께 들어옵니다.
📋 3줄 요약
-
클로드 코드 훅은 PreToolUse와 PostToolUse, Stop 같은 이벤트에 명령을 걸어 두는 기능이고 설정은 settings.json의 hooks에서 이벤트와 matcher, 핸들러 세 단계로 적습니다.
-
대부분의 이벤트에서 코드만으로 작업을 막는 종료 코드는 2뿐이고, exit 1이나 스크립트 경로 오타는 비차단 오류라 작업이 그대로 진행됩니다.
-
PostToolUse는 이미 실행된 뒤라 막지 못하므로 저장 뒤 검사는 결과를 알리는 용도로, 커밋처럼 막아야 하는 일은 PreToolUse에서 exit 2로 차단합니다.
📚 참고 자료
- 훅 참고 문서: https://code.claude.com/docs/en/hooks
- 훅 가이드: https://code.claude.com/docs/en/hooks-guide
- 권한 규칙 문서: https://code.claude.com/docs/en/permissions
- 메모리 문서(CLAUDE.md와 훅의 역할 구분): https://code.claude.com/docs/en/memory

제대로 이해했는지 한 문제로 확인해 볼까요?
답을 고르면 바로 풀이가 나와요.
커밋을 막으려고 만든 PreToolUse 훅 스크립트가 검사에 실패하자 exit 1로 끝났습니다. 공식 문서 기준으로 어떤 일이 일어날까요?

