SOURCE

Claude Code 훅 가이드

Claude Code 공식 문서 "Automate actions with hooks". 첫 훅 만들기, 설정 예 7가지, 이벤트·matcher·if·설정 위치, 종료 코드·JSON 출력, 여러 훅 합치기, prompt·agent·HTTP 훅, 권한 모드와의 관계, 한계·타임아웃, 문제 해결

확인 2026-10-18원본 자료 1건의 요약갱신 2026-10-21언급 22곳
이 페이지의 목차

한 줄 요약: Claude Code 공식 문서 "Automate actions with hooks". 첫 훅(데스크톱 알림)을 만드는 3단계, 바로 붙여 쓸 설정 예 7가지(알림, 편집 후 포맷, 보호 파일 편집 차단, 컴팩션 뒤 컨텍스트 다시 넣기, 설정 변경 감사, 디렉터리·파일이 바뀔 때 환경 다시 읽기, 특정 권한 확인 자동 승인), 훅이 도는 방식(이벤트 목록, 여러 훅의 결과 합치기, stdin 입력·종료 코드·JSON 출력, matcher와 if 필드, 설정 위치), prompt·agent·HTTP 훅, 한계와 문제 해결을 다룬다.

원본 정보#

  • 제목: Automate actions with hooks
  • 발행: Anthropic, Claude Code 공식 문서 (원본 날짜 미상)
  • URL: https://code.claude.com/docs/en/hooks-guide (새 창)
  • 수집(fetch)일: 2026-10-18. 본문의 이벤트 목록, matcher 값, 설정 키, 기본 타임아웃, 버전 조건(v2.1.210, v2.1.234, v2.1.246, v2.1.248)은 이 날짜 기준이다.
  • 범위: 원본은 스스로 "흔한 용도와 시작하는 법"을 다룬다고 밝히고, 전체 이벤트 스키마, JSON 입출력 형식, async 훅, MCP 도구 훅 같은 고급 기능은 Hooks reference로 넘긴다. 이 요약도 원본이 링크만 한 레퍼런스 내용(이벤트별 종료 코드 2 동작 전체 목록, decision control 요약표, 보안 고려 사항)은 담지 않는다. 그 레퍼런스는 2026-10-19에 Claude Code 훅 레퍼런스로 따로 수집했다.

핵심 주장#

  1. 훅은 결정론적 통제다. 훅은 사용자가 정의한 셸 명령으로, Claude Code가 생명주기의 정해진 지점에서 실행한다. 원본: "certain actions always happen rather than relying on the LLM to choose to run them." 규칙이 아니라 판단이 필요한 결정에는 Claude 모델이 조건을 평가하는 prompt 훅·agent 훅을 쓸 수 있다. → 훅
  2. 훅은 stdin·stdout·stderr·종료 코드로 말한다. 이벤트가 일어나면 Claude Code가 이벤트별 데이터를 JSON으로 stdin에 넘긴다. 종료 코드 0은 "종료 코드로는 이의 없음"이고(PreToolUse에서는 승인이 아니라 일반 권한 흐름이 그대로 적용), 종료 코드 2는 동작을 막으며 stderr에 이유를 쓴다. 그 밖의 종료 코드는 대부분의 이벤트에서 막지 않는 오류(non-blocking error)로 처리되고 동작은 진행된다. 더 세밀하게 통제하려면 종료 코드 0으로 끝내고 stdout에 JSON 객체를 쓴다. 원본은 한 훅에서 두 방식 중 하나만 고르라고 한다. → Claude Code 훅 설정 레시피
  3. Claude의 컨텍스트에 무언가 넣는 길은 이벤트와 출력 형식에 따라 다르다. 종료 코드 0에서 Claude Code가 일반 텍스트로 다루는 stdout은 UserPromptSubmit, UserPromptExpansion, SessionStart, PostModelSwitch 훅에서 Claude의 컨텍스트에 더해진다(원본이 이름으로 든 네 이벤트). UserPromptSubmit에서 텍스트를 넣으려면 hookSpecificOutput.additionalContext를 쓰고, 이 필드를 JSON 최상위에 두면 Claude Code가 조용히 무시한다. additionalContext로 돌려준 텍스트는 system reminder로 주입돼 Claude가 일반 텍스트로 읽는다. 여러 훅이 같은 이벤트에 맞으면 모든 훅의 additionalContext가 함께 전달된다. → 훅, 컨텍스트 비용
  4. PreToolUse의 거부는 권한 모드를 가리지 않지만, 허용은 권한 규칙보다 느슨하게 만들지 못한다. PreToolUse 훅은 어떤 권한 모드 검사보다 먼저, dontAsk를 포함한 모든 모드에서 실행되고, permissionDecision: "deny"를 돌려주면 bypassPermissions나 --dangerously-skip-permissions에서도 도구를 막는다. 반대로 "allow"는 settings의 deny 규칙을 넘지 못하고 몇몇 확인 창을 없애지 못한다. 조건과 예외가 붙는다: 설치한 mod가 tool.check를 다루면 managed settings에 있지 않은 훅이 막은 호출을 승인할 수 있고(plan 모드에서는 읽기 전용 호출만, 세부 기준은 권한 문서로 넘김. 위키는 권한 문서 기준으로 정리: 훅의 ‘원본끼리 달랐던 점’ 상자), matcher가 Edit|Write면 Claude가 셸 명령으로 고친 파일은 보지 못하며, if 필드 거르기는 최선 노력(best-effort)이라 확실한 허용·거부는 권한 시스템으로 하라고 원본이 말한다. → 권한 모드, Claude Code 관리자 권한 통제
  5. 훅 실패는 자주 조용하다. 성공한 훅은 대화에 아무것도 보이지 않는다. JSON 앞에 셸 프로필의 echo가 끼거나 필드를 잘못된 층에 두면 JSON이 무시되고 오류도 보이지 않는다. 그래서 /hooks 메뉴, Ctrl+O 트랜스크립트 보기, claude --debug·/debug 디버그 로그로 확인한다. → Claude Code 훅 문제 해결

원본이 든 설정 예 (2026-10-18 기준)#

원본은 각 예에 바로 쓸 수 있는 설정 블록을 붙인다. 설정 원문은 Claude Code 훅 설정 레시피에 그대로 옮겼다.

목적이벤트matcher원본이 넣은 위치
Claude가 입력을 기다릴 때 데스크톱 알림Notification""(모든 알림 종류)~/.claude/settings.json
편집한 파일에 Prettier 실행PostToolUseEdit|Write프로젝트 .claude/settings.json
.env·package-lock.json·.git/ 편집 차단PreToolUseEdit|Write프로젝트 .claude/settings.json + .claude/hooks/protect-files.sh
컴팩션 뒤 프로젝트 관례 다시 넣기SessionStartcompact프로젝트 .claude/settings.json
설정·스킬 파일 변경을 감사 로그에 기록ConfigChange""~/.claude/settings.json
디렉터리가 바뀔 때 direnv 환경 다시 읽기SessionStart + CwdChanged (또는 FileChanged)없음 (FileChanged는 .envrc|.env)~/.claude/settings.json
plan 끝의 ExitPlanMode 확인 자동 승인PermissionRequestExitPlanMode~/.claude/settings.json

원본이 밝힌 세부 동작 (2026-10-18 기준)#

이벤트

원본 표의 이벤트는 다음과 같다: SessionStart, Setup, UserPromptSubmit, UserPromptExpansion, PreToolUse, PermissionRequest, PermissionDenied, PostToolUse, PostToolUseFailure, PostToolBatch, Notification, MessageDisplay, SubagentStart, SubagentStop, TaskCreated, TaskCompleted, Stop, StopFailure, TeammateIdle, InstructionsLoaded, ConfigChange, CwdChanged, DirectoryAdded, FileChanged, WorktreeCreate, WorktreeRemove, PreCompact, PostCompact, PreModelSwitch, PostModelSwitch, Elicitation, ElicitationResult, SessionEnd. 원본은 전체 목록을 Hooks reference로 넘긴다.

  • 원본 이벤트 표가 "막을 수 있다"고 적은 것: PreToolUse(도구 호출), UserPromptExpansion(명령 확장), PreModelSwitch(모델 전환). 표 밖 본문에서도 ConfigChange는 종료 코드 2나 {"decision": "block"}으로 변경을 막을 수 있고, PostToolUse·Stop은 decision: "block"을 쓴다. 이벤트별 전체 목록은 레퍼런스에 있다.
  • Stop은 Claude가 응답을 마칠 때마다 실행된다. 작업 완료 때만이 아니다. 사용자 중단에는 실행되지 않고, API 오류로 끝난 턴은 StopFailure가 받는다.
  • PermissionRequest는 Claude Code가 사용자에게 권한을 물으려 할 때, 또는 물을 수 없는 호출을 자동 거부하려 할 때 실행된다. -p 비대화형 모드에서도 dontAsk 모드가 아니면 실행되고, 어떤 훅도 정하지 않고 달리 답할 것도 없는 호출은 거부된다.
  • PostToolUse 훅은 도구가 이미 실행됐으므로 동작을 되돌리지 못한다.

종류

command(셸 명령, 가장 흔함) 외에 네 가지가 있다: http(이벤트 데이터를 URL로 POST), mcp_tool(설정된 MCP 서버의 도구 호출), prompt(단일 턴 LLM 평가), agent(도구를 쓰는 다중 턴 검증). 원본은 agent 훅이 실험 기능이라 바뀔 수 있으니 운영 워크플로에는 command 훅을 권한다.

여러 훅이 맞을 때

  • 같은 이벤트에 맞는 훅은 모두 병렬로 실행되고, 모두 끝난 뒤 결과를 합친다. 한 훅의 deny가 다른 훅의 실행을 멈추지 않으므로, 한 훅의 deny로 다른 훅의 부작용을 막으려 하지 말라고 원본이 경고한다.
  • PreToolUse 권한 결정은 가장 제한적인 답이 이긴다: deny, defer, ask, allow 순.
  • 여러 PreToolUse 훅이 updatedInput으로 도구 인자를 고쳐 쓰면 마지막에 끝난 것이 적용되고 순서는 비결정적이다. 한 도구의 입력을 둘 이상의 훅이 고치지 않게 하라고 한다.

종료 코드 2가 보여 주는 곳

이벤트마다 다르다. 어떤 이벤트는 stderr를 Claude에게 피드백으로 주고, 어떤 이벤트는 사용자에게 보여 주며, ConfigChange·Elicitation 같은 몇몇은 메시지를 보이지 않는다. SessionStart 등 막을 수 없는 이벤트에서는 종료 코드 2가 stderr를 사용자에게 보여 주고 실행은 계속된다. 전체 목록은 레퍼런스에 있다.

PreToolUse의 permissionDecision 값

  • "allow": 대화형 확인 창을 건너뛴다. 단 deny·ask 규칙(기업 managed deny 목록 포함)은 그대로 적용되고, 네트워크 경로 읽기, requiresUserInteraction 표시 MCP 도구, 조직이 ask로 정한 커넥터 도구(그 설정이 Claude Code에 닿는 세션에서)의 확인 창도 남는다. plan 모드에서 파일 편집을 "allow"해도 훅이 없을 때와 같은 확인·차단·승인을 받는다.
  • "deny": 도구 호출을 취소하고 permissionDecisionReason을 Claude에게 준다.
  • "ask": 평소처럼 사용자에게 확인 창을 띄운다.
  • "defer": -p 비대화형 모드에서만. 도구 호출을 보존한 채 프로세스를 끝내 Agent SDK 래퍼가 입력을 모은 뒤 재개하게 한다.

다른 이벤트는 결정 형식이 다르다: PostToolUse·Stop은 최상위 decision: "block", PermissionRequest는 hookSpecificOutput.decision.behavior를 쓴다. PreModelSwitch는 같은 permissionDecision을 쓰되 "ask"는 대화형 /model에서만 확인을 받고 그 밖에서는 거부로 다룬다.

matcher와 if

  • matcher가 없으면 그 이벤트가 일어날 때마다 실행된다. 이벤트마다 matcher가 거르는 필드가 다르다(도구 이벤트는 도구 이름, SessionStart는 시작 방식, Notification은 알림 종류, ConfigChange는 설정 출처 등). UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, CwdChanged, MessageDisplay는 matcher를 지원하지 않아 매번 실행된다.
  • matcher는 대소문자를 구분한다. "Edit|Write"와 "Edit, Write"는 같다. MCP 도구 이름은 mcp__<server>__<tool> 형식이라 mcp__github__.* 같은 정규식으로 맞춘다.
  • if 필드는 권한 규칙 문법("Bash(git *)", "Edit(*.ts)")으로 도구 이름과 인자를 함께 걸러, 맞을 때만 훅 프로세스를 띄운다. 도구 이벤트(PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied)에서만 동작하고 다른 이벤트에 붙이면 훅이 실행되지 않는다. Bash 명령을 어떤 명령으로 실행할지 판단할 수 없으면 패턴과 상관없이 훅을 실행한다. 원본은 이 거르기가 최선 노력이므로 확실한 허용·거부는 권한 시스템으로 하라고 한다.

설정 위치와 끄기

위치범위공유
~/.claude/settings.json내 모든 프로젝트아니오, 내 기기
.claude/settings.json프로젝트 하나예, 저장소에 커밋 가능
.claude/settings.local.json프로젝트 하나아니오, Claude Code가 설정을 저장할 때 gitignore
Managed policy settings조직 전체예, 관리자 통제
플러그인 hooks/hooks.json플러그인이 켜져 있을 때예, 플러그인에 묶임
스킬 frontmatter스킬을 부른 뒤 세션 끝까지예, 스킬 파일
서브에이전트 frontmatter그 서브에이전트가 도는 동안예, 서브에이전트 파일
  • "disableAllHooks": true로 훅을 끈다. 설정 우선순위를 적용한 뒤 남은 값을 읽으므로 프로젝트 설정이 사용자 설정을 덮을 수 있다. managed settings에 있는 훅은 managed settings에도 disableAllHooks가 있지 않으면 계속 실행된다.
  • 조직이 allowManagedHooksOnly를 켜면 /hooks 메뉴에 "Only hooks from managed settings run here"가 보이고, user·project·local 설정 파일의 훅은 실행되지 않고 목록에도 없다.
  • Claude Code가 실행 중일 때 설정 파일을 직접 고치면 파일 감시기가 보통 자동으로 반영한다.

기본 타임아웃 (훅마다 timeout 필드, 초 단위로 바꿀 수 있음)

  • command·http·mcp_tool: 10분. 단 UserPromptSubmit·PreModelSwitch·PostModelSwitch는 30초, MessageDisplay는 10초로 낮춘다.
  • prompt: 30초. agent: 60초(도구 사용 최대 50턴).
  • SessionEnd 훅은 종류와 상관없이 1.5초 예산을 나눈다. 훅별 timeout을 더 길게 두면 최대 60초까지 예산을 올린다.

버전 조건

  • Notification의 quota_auto_resume_fired·quota_auto_resume_stale·quota_auto_resume_disabled matcher: v2.1.234 이상.
  • 터미널 세션에서 샌드박스 명령의 네트워크 요청에 대한 permission_prompt: v2.1.246 이상.
  • 팀원의 터미널 설정 질문에 대한 agent_needs_input: v2.1.248 이상.
  • prompt 훅 PreToolUse의 "ok": false: v2.1.210 전에는 거부 이유가 도구 오류로 Claude에게 돌아가고 턴이 계속됐다. 지금은 기본으로 턴이 끝나고 경고 줄로 보이며, continueOnBlock: true면 이유를 Claude에게 돌려주고 계속한다.

ORIGINAL

원문

원문은 원 저작자 사이트에서 읽을 수 있습니다.
code.claude.com

원문 열기 (새 창)

BACKLINKS

14곳 더 보기접기