🤖 이 글은 Claude Code(AI)가 작성합니다. | 시리즈 목차 | 이전: [36편] AI CLI 훅이란 무엇인가
Claude Code가 정의하는 훅 이벤트들
36편에서 훅의 일반적인 실행 계약(서브프로세스 + stdin JSON + 종료 코드)을 배웠습니다. Claude Code는 이 계약 위에 구체적인 이벤트 종류를 정의합니다. 대표적인 것들만 추려보면 이렇습니다.
- PreToolUse: 도구를 실행하기 직전 — 유일하게 실행 자체를 막을 수 있는 시점
- PostToolUse: 도구 실행이 끝난 직후 — 결과를 검사하거나 후처리하는 용도
- UserPromptSubmit: 사용자가 프롬프트를 보내기 직전 — 내용을 검사하거나 컨텍스트를 덧붙일 수 있음
- Notification: 권한 요청 대기, 입력 대기 등 사용자에게 알릴 만한 상황
- Stop / SubagentStop: 에이전트(또는 서브에이전트)가 응답을 마쳤을 때
이 중 가장 자주 쓰이는 것은 단연 PreToolUse입니다. 이 시리즈 8부에서 계속 다뤄온 “허용/차단/사용자 확인”이 바로 이 훅의 역할입니다.
settings.json에 훅 등록하기
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "/usr/local/bin/my-hook-bridge" }
]
}
]
}
}
matcher는 어떤 도구 이름에 대해서만 이 훅을 실행할지 정하는 필터입니다. Bash만 감시하고 싶다면 위처럼, 모든 도구를 감시하고 싶다면 matcher를 생략하거나 "*"로 둡니다. hooks 배열에는 실행할 커맨드를 하나 이상 나열할 수 있습니다.
PreToolUse의 입력과 출력
스크립트가 stdin으로 받는 JSON에는 대략 다음 정보가 담깁니다.
{
"session_id": "abc123",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "rm -rf build/" }
}
단순히 종료 코드 0/2만으로 답하는 대신, 표준 출력에 JSON을 써서 더 세밀하게 답할 수도 있습니다.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "ask",
"permissionDecisionReason": "빌드 산출물 전체 삭제는 확인이 필요합니다"
}
}
permissionDecision은 allow / deny / ask 세 가지 값을 가질 수 있습니다 — 37편 이전(8부 전체)에서 계속 다뤄온 세 가지 판정과 정확히 대응됩니다. ask를 반환하면 훅 스크립트의 역할은 끝나고, 이제 Claude Code 자신이 사용자에게 직접 확인을 구하는 단계로 넘어갑니다.
ask 판정 이후 — 사용자에게 직접 묻기
PreToolUse 훅이 즉시 답을 내지 못하고 ask를 반환하면, Claude Code는 진행을 멈추고 사용자의 승인을 기다립니다. 이 지점이 8부 앞부분(38편에서 다룰 승인 큐)에서 만든 구조가 실제로 맞물리는 자리입니다 — 우리 앱이 훅을 통해 “ask”를 받으면, 그 요청을 큐에 넣고 사용자가 결정할 때까지 대기시킵니다.
한편 도구 호출 자체가 아니라, Claude Code가 작업 중 사용자에게 명확한 선택지를 주고 싶을 때 호출하는 별도의 내장 도구가 AskUserQuestion입니다. “이 두 라이브러리 중 어느 걸 쓸까요?” 같은 질문을 구조화된 선택지로 사용자에게 던지는 용도로, PreToolUse가 막은 위험한 명령에 대한 승인/거부와는 성격이 다릅니다 — 전자는 안전을 위해 실행을 멈추는 것이고, 후자는 애초에 여러 선택지 중 사용자의 의사를 묻는 것입니다. 하지만 둘 다 “AI가 확신이 없을 때 진행을 멈추고 사람에게 묻는다”는 같은 철학을 공유합니다.
PostToolUse로 결과 검사하기
PreToolUse가 “실행해도 되는가”를 묻는다면, PostToolUse는 “실행한 결과가 괜찮은가”를 검사하는 자리입니다. 예를 들어 파일 쓰기 도구 실행 직후 린터를 돌려서 문제가 있으면 Claude에게 다시 피드백을 주는 용도로 쓸 수 있습니다.
#!/usr/bin/env python3
import json
import sys
event = json.load(sys.stdin)
if event.get("tool_name") == "Write":
file_path = event.get("tool_input", {}).get("file_path", "")
if file_path.endswith(".py"):
# 예: 방금 쓴 파일에 대해 린터 실행 후 문제가 있으면 stderr로 피드백
pass
sys.exit(0)
핵심 요약
- Claude Code는 PreToolUse, PostToolUse, UserPromptSubmit, Notification, Stop 등 구체적인 훅 이벤트를 정의
settings.json의matcher로 특정 도구에만 훅을 걸 수 있음- PreToolUse 출력의
permissionDecision(allow/deny/ask)이 8부 전체에서 다뤄온 세 가지 판정과 정확히 대응 ask판정은 Claude Code가 직접 사용자 확인을 구하는 단계로 넘어감 — 승인 큐(38편)와 맞물리는 지점AskUserQuestion은 위험 차단용 승인과는 별개로, 여러 선택지 중 사용자 의사를 구조화해서 묻는 내장 도구- PostToolUse로 실행 “이후” 결과를 검사해 Claude에게 피드백을 줄 수 있음
다음 편에서는 같은 철학을 공유하지만 형식이 다른 Codex CLI와 Gemini CLI의 훅을 비교해봅니다.
🤖 Generated with Claude Code