본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

AI & Data

Claude Code 훅: 도구 호출 앞뒤에 규칙 걸기

추가
목차12개 항목
출처와 참고

Anthropic 강의 「Claude Code in Action」을 정리한 hello.gayuna의 「Claude Code in Action」(2025.09)을 바탕으로, 이벤트·설정·출력 형식은 훅 공식 문서로 2026년 기준에 맞춰 재구성한 노트다. 이벤트 목록과 활용 예(Stop 루프, 권한 요청 위임, 온디맨드 훅)는 shanraisshan의 「claude-code-best-practice」(MIT, 2026-11)에 모인 Claude Code 팀의 팁으로 보강했다. 토큰 절감 예는 Spotify Engineering의 「Portal by Spotify cut my Claude Code token usage by 90%」(2026.09)로 보강했다.

훅이란

훅(hook) 은 Claude Code가 일하는 특정 시점에 자동으로 실행되는 사용자 명령이다. CLAUDE.md에 「수정 후엔 포맷터를 돌려라」라고 적는 것은 부탁이라 모델이 잊을 수 있다. 훅은 모델의 판단과 무관하게 항상 실행된다. 반드시 지켜야 하는 규칙은 훅으로, 판단이 필요한 지침은 CLAUDE.md로 나눈다.

쓰임예
코드 포맷Claude가 파일을 고친 뒤 자동으로 포맷터 실행
테스트·품질파일이 바뀌면 테스트, 린터, 타입 검사를 돌리고 결과를 Claude에게 돌려준다
접근 통제특정 파일을 읽거나 고치지 못하게 막는다
기록Claude가 어떤 파일을 읽고 고쳤는지 남긴다
검증이름 규칙, 코딩 표준을 확인한다

주요 이벤트

묶음이벤트시점
도구PreToolUse도구 실행 전. 호출을 막거나 입력을 바꿀 수 있다
PostToolUse / PostToolUseFailure도구가 성공한 뒤 / 실패한 뒤. 이미 실행됐으므로 막지는 못하고 피드백을 준다
PermissionRequest도구 호출에 권한 결정이 필요할 때
턴UserPromptSubmit사용자가 프롬프트를 보낸 직후, Claude가 처리하기 전
StopClaude가 응답을 마쳤을 때. 일을 계속하게 할 수도 있다
세션SessionStart / SessionEnd세션 시작·재개 / 종료
서브에이전트SubagentStart / SubagentStop서브에이전트 시작 / 종료
맥락PreCompact / PostCompact대화 압축 전후
InstructionsLoadedCLAUDE.md나 규칙 파일이 읽힐 때
알림Notification권한 요청이 필요하거나 일정 시간 입력을 기다릴 때

이 밖에도 도구 묶음이 끝난 뒤(PostToolBatch), 프롬프트 확장(UserPromptExpansion), 응답 실패(StopFailure), 작업 디렉터리 변경(CwdChanged), 파일 변경 감시(FileChanged), 워크트리 생성·삭제(WorktreeCreate·WorktreeRemove), 권한 거부(PermissionDenied), 작업 생성(TaskCreated), MCP 입력 요청(Elicitation) 같은 이벤트가 있다(v2.1.322 기준 30개가 넘는다). 핸들러는 셸 명령 말고도 HTTP 요청과 MCP 도구 호출이 될 수 있다. 전체 목록은 공식 문서에 있다.

설정

훅은 설정 파일의 hooks 아래에 이벤트 → matcher 묶음 → 실행할 핸들러 세 단계로 적는다. 직접 편집하거나 /hooks 명령으로 추가한다.

위치범위
~/.claude/settings.json내 모든 프로젝트
.claude/settings.json이 프로젝트, git으로 팀과 공유
.claude/settings.local.json이 프로젝트, 나만 (gitignore)
조직 정책(managed settings)조직 전체, 관리자가 통제
플러그인 hooks/hooks.json, 스킬·서브에이전트 frontmatter해당 플러그인·스킬이 켜져 있는 동안
  • matcher: 도구 이벤트에서는 도구 이름을 거른다. Read|Grep처럼 |로 여러 개, mcp__playwright__.*처럼 정규식, *나 생략이면 전부다.
  • 핸들러 종류: 셸 명령(command)이 기본이다. JSON을 URL로 보내는 http, MCP 도구를 부르는 mcp_tool, 모델에게 한 번 판단을 맡기는 prompt도 있다.
  • 경로는 ${CLAUDE_PROJECT_DIR}로 프로젝트 기준 절대 경로를 쓰고, 인자는 args로 넘기면 따옴표·공백 문제가 없다.

입력과 종료 코드

훅 명령은 표준 입력으로 JSON을 받는다. PreToolUse라면 어떤 도구를 어떤 인자로 부르려는지가 들어 있다.

{
  "session_id": "2d6a1e4d-6...",
  "transcript_path": "/Users/sg/...",
  "hook_event_name": "PreToolUse",
  "tool_name": "Read",
  "tool_input": { "file_path": "/code/queries/.env" }
}
종료 코드의미
0성공. 표준 출력이 JSON 객체 하나면 결정·메시지로 해석한다
2막는다. 표준 오류(stderr)의 내용이 이유로 Claude에게 전달된다. 막을 수 없는 이벤트(PostToolUse 등)에서는 Claude에게 피드백만 준다
그 밖막지 않는 오류. 작업은 계속되고 사용자에게 알림만 뜬다

더 세밀하게 제어하려면 종료 코드 0과 함께 JSON을 출력한다. 예를 들어 PreToolUse에서 hookSpecificOutput.permissionDecision을 "deny"로 주면 이유와 함께 막고, additionalContext로 Claude에게 참고 정보를 더할 수 있다.

예제: .env 파일 읽기 막기

Claude가 .env를 읽지 못하게 하려면 PreToolUse에서 Read와 Grep을 감시한다. 개인용이라면 .claude/settings.local.json에 둔다.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Grep",
        "hooks": [
          { "type": "command", "command": "node \"${CLAUDE_PROJECT_DIR}/hooks/read_hook.js\"" }
        ]
      }
    ]
  }
}
// hooks/read_hook.js
async function main() {
  const chunks = [];
  for await (const chunk of process.stdin) chunks.push(chunk);
  const toolArgs = JSON.parse(Buffer.concat(chunks).toString());
 
  // Claude가 읽으려는 경로
  const readPath = toolArgs.tool_input?.file_path || toolArgs.tool_input?.path || "";
 
  if (readPath.includes(".env")) {
    console.error("You cannot read the .env file");
    process.exit(2); // 막고, stderr를 이유로 전달
  }
}
main();

단순한 경로 차단은 훅 대신 권한 규칙(permissions.deny에 Read(./.env))으로도 걸 수 있다. 공식 문서도 확실한 허용·차단은 권한 체계에 맡기고, 훅은 판단 로직이 필요한 곳에 쓰라고 권한다.

유용한 훅

훅동작
타입 검사.ts 파일이 수정될 때마다(PostToolUse, matcher Edit|Write) tsc --noEmit을 돌리고, 오류가 나면 종료 코드 2로 오류 내용을 Claude에게 돌려준다. Claude가 바로 고친다
쿼리 중복 방지새 쿼리 파일을 만들려 할 때 별도의 Claude(claude -p)를 띄워 비슷한 쿼리가 이미 있는지 묻고, 있으면 막고 기존 것을 쓰게 한다
포맷터편집 뒤 Prettier·Black 등을 자동 실행
알림Notification·Stop에서 데스크톱 알림을 띄워 자리를 비워도 끝난 것을 안다
끝날 때까지 계속Stop에서 테스트를 돌려 실패하면 「계속하라」를 돌려준다. 비결정적 모델이라도 결정적인 종료 조건을 얻는다. claude -p·SDK와 묶으면 무인 루프가 된다
권한 요청 위임PermissionRequest에서 요청을 강한 모델에게 넘겨 공격 여부를 보고 안전한 것만 자동 승인하거나, Slack·메신저로 보내 자리를 비워도 원격으로 승인한다
맥락 주입·기록SessionStart에서 매번 필요한 맥락을 동적으로 넣고(클라우드 세션의 환경 구성에도 쓴다), PreToolUse에서 모든 bash 명령과 스킬 호출을 로깅해 자주 쓰는·안 쓰는 스킬을 찾는다
온디맨드 훅스킬 프론트매터의 hooks로 그 스킬을 부를 때만 켜지고 세션 동안 유지되는 훅. /careful은 rm -rf·DROP TABLE·force push·kubectl delete를 막고, /freeze는 특정 폴더 밖 편집을 막는다

훅이라는 기능 자체가 「권한 요청이 떴는데 자리를 비워서 몰랐다, Slack으로 알려 달라」 같은 요청에 통합 기능을 하나씩 만드는 대신 열어 둔 확장점이다.

예: 큰 파일 읽기를 싼 모델로 돌려 토큰 줄이기

파일을 통째로 읽거나 테스트·설정·타입 스텁 같은 정형 코드를 쓰는 일은 토큰은 많이 들지만 판단은 적게 든다. 이런 I/O성 작업을 저비용 모델에 넘기고 메인 모델은 추론에만 쓰면 토큰이 크게 준다. Spotify 사내 실험에서는 큰 파일 읽기를 위임해 읽기 토큰을 평균 약 90% 줄였다.

계층하는 일
PreToolUse 훅Read가 임계값(예: 350줄)을 넘는 파일을 통째로 읽으려 하면 막고 위임 스킬을 쓰라고 알려 준다. offset·limit을 준 부분 읽기는 통과시킨다. Bash의 cat·head·tail·less로 큰 파일을 여는 것도 같은 식으로 막는다
래퍼 스크립트파일들과 질문을 저비용 모델(예: Gemini Flash 계열, 낮은 temperature)에 한 번에 보내 글머리표 요약만 받는다. 코드 생성은 명세와 참고 파일을 보내 결과를 바로 디스크에 쓴다
스킬언제 어떤 스크립트를 부를지 Claude에게 알려 준다
  • CLAUDE.md 규칙만으로는 지켜지지 않는다. 「큰 파일은 위임하라」는 지시는 권고라서 모델이 자주 건너뛴다. 훅이 막아야 스킬을 무시해도 비싼 읽기가 일어나지 않는다. 훅을 플러그인으로 묶으면 프로젝트마다 규칙을 복사하지 않아도 된다.
  • 편집과 추론은 위임하지 않는다. 요약에는 정확한 줄 번호가 없으니 고칠 부분은 메인 모델이 직접 읽는다. 저비용 모델은 미묘한 동시성 버그 같은 것을 놓치므로 디버깅, 설계 판단, 안전에 중요한 코드는 메인 모델이 맡는다.
  • 작은 파일은 직접 읽는 편이 빠르다. 위임 한 번에 10~30초가 걸리므로 임계값 아래는 그냥 읽게 둔다.

주의점

보안

훅은 내 사용자 권한 그대로 실행된다. 남이 만든 저장소의 프로젝트 훅은 실행 전에 내용을 확인한다.

  • 입력 JSON을 믿지 말고 검증한다.
  • 셸 변수는 항상 따옴표로 감싼다($VAR가 아니라 "$VAR").
  • 경로에 ..가 있는지 확인해 경로 순회를 막는다.
  • 스크립트는 절대 경로로 지정한다. 경로 가로채기·바이너리 심기 공격을 줄인다.
  • .env, .git/, 키 파일 같은 민감한 파일은 건너뛴다.
  • 훅이 시간 초과되면 기본적으로 작업이 그대로 진행된다(fail-open). 막는 용도라면 onFailure: "block"을 준다.

절대 경로와 공유의 충돌

절대 경로는 머신마다 달라 설정 파일을 공유하기 어렵다. ${CLAUDE_PROJECT_DIR} 변수로 프로젝트 기준 절대 경로를 만들면 같은 설정을 팀이 그대로 쓴다. 과거에는 자리표시자를 넣은 예제 설정을 커밋하고 초기화 스크립트로 머신별 경로를 채워 넣었다.

입력 형식 확인하기

표준 입력의 모양은 이벤트마다, tool_input은 도구마다 다르다. 훅을 짜기 전에 실제 입력을 파일로 떨궈 본다.

"PostToolUse": [
  { "matcher": "*", "hooks": [ { "type": "command", "command": "jq . > post-log.json" } ] }
]

정리

  • 훅은 모델에게 부탁하는 대신 시스템이 항상 실행하는 규칙이다.
  • PreToolUse는 막을 수 있고, PostToolUse는 결과를 보고 피드백을 준다.
  • 표준 입력으로 JSON을 받고, 종료 코드 2와 stderr로 막고 이유를 전한다.
  • 타입 검사·포맷터·민감 파일 차단처럼 확인 가능한 규칙에 쓴다. 단순 차단은 권한 규칙으로도 된다.
  • CLAUDE.md의 권고를 강제 규칙으로 바꿀 때도 훅을 쓴다. 큰 파일 읽기를 막고 저비용 모델로 돌리면 토큰이 크게 준다.
  • 훅은 내 권한으로 돈다. 입력 검증, 절대 경로, 따옴표를 지킨다.

관련 노트: Claude Code 활용 가이드, Claude Code 설정과 권한 규칙, Claude Code 실전 팁, AI 코딩 실무 가드레일, 프롬프트에서 하네스로, 내 일을 줄이는 하네스 만들기

원본 기록6

댓글

GitHub 계정으로 댓글을 남길 수 있어요.