포스트

Claude Code의 Hook 알아보기

Claude Code의 Hook은 무엇이고, Skill이나 Agent와는 어떻게 다른지 알아본다

Claude Code의 Hook 알아보기

지금까지 Skill과 Agent를 다뤘다.
둘 다 Claude의 판단에 개입하는 방식이었다면, Hook은 성격이 다르다.
Hook은 Claude의 판단을 거치지 않고 정해진 시점에 무조건 실행되는 규칙이다.

Hook이란?

Hook은 Claude Code의 세션 생명주기 중 특정 시점에 자동으로 실행되는 사용자 정의 셸 명령어, HTTP 엔드포인트, 또는 LLM 프롬프트다.
파일을 수정하기 직전, 프롬프트를 제출한 직후, 세션이 시작될 때처럼 정해진 이벤트가 발생하면 Claude Code가 등록된 Hook을 실행하고, Hook은 그 결과에 따라 작업을 허용하거나 막을 수 있다.
가장 중요한 특징은, Hook은 Claude의 판단이 아니라 결정론적인 규칙이라는 점이다.
Skill이 ‘이런 상황이면 이렇게 해라’는 지침을 Claude에게 주는 것이라면, Hook은 조건만 맞으면 무조건 실행된다.

탄생 배경

Skill이나 CLAUDE.md에 아무리 좋은 지침을 적어놔도, Claude가 그 지침을 놓치거나 다르게 판단하면 실행되지 않을 수 있다.
이런 문제를 해결하려면 Claude의 판단과 무관하게 항상 실행되는 규칙이 필요하다.
위험한 명령어를 원천 차단하거나, 파일을 수정할 때마다 자동으로 린트를 돌리거나, 세션이 시작될 때마다 최신 브랜치 정보를 주입하는 것처럼 확률이 아니라 확실성이 필요한 작업에는 Hook이 적합하다.

기본 동작 원리

설정 구조

Hook 설정은 3단계로 구성된다.

1. 이벤트 선택: PreToolUse, Stop 같은 생명주기 이벤트를 고른다.

2. Matcher로 필터링: “Bash 도구에만 적용” 같은 조건으로 범위를 좁힌다.

3. Handler 정의: 조건이 맞으면 실제로 실행할 동작을 정의한다.

Hook 생명주기 (이벤트)

Hook이 발생하는 시점(이벤트)은 아래처럼 다양하다.

구분이벤트발생 시점
세션 단위SessionStart세션이 시작되거나 재개될 때
 Setup--init-only 등으로 사전 준비 작업을 실행할 때
 SessionEnd세션이 종료될 때
턴(turn) 단위UserPromptSubmit프롬프트를 제출한 직후, Claude가 처리하기 전
 UserPromptExpansion슬래시 커맨드 등이 프롬프트로 확장될 때
 StopClaude가 응답을 마쳤을 때
 StopFailureAPI 오류로 턴이 종료됐을 때
도구 호출 단위PreToolUse도구 호출 직전(차단 가능)
 PermissionRequest권한 승인 다이얼로그가 뜰 때
 PermissionDeniedAuto Mode가 도구 호출을 거부했을 때
 PostToolUse도구 호출이 성공한 직후
 PostToolUseFailure도구 호출이 실패한 직후
 PostToolBatch병렬 도구 호출 묶음이 모두 끝난 직후
Subagent 단위SubagentStartSubagent가 새로 생성될 때
 SubagentStopSubagent 작업이 끝났을 때
Task 단위TaskCreatedTaskCreate로 Task가 생성될 때
 TaskCompletedTask가 완료 처리될 때
Agent Team 단위TeammateIdleAgent Team의 팀원이 유휴 상태로 전환되기 직전
워크트리 단위WorktreeCreateGit worktree가 생성될 때
 WorktreeRemoveGit worktree가 제거될 때
컨텍스트 압축 단위PreCompact컨텍스트 압축 직전
 PostCompact컨텍스트 압축 직후
MCP 상호작용 단위ElicitationMCP 서버가 도구 호출 중 사용자 입력을 요청할 때
 ElicitationResult사용자가 Elicitation에 응답한 직후
독립 비동기 이벤트NotificationClaude Code가 알림을 보낼 때
 MessageDisplayClaude의 응답 텍스트가 화면에 출력되는 동안
 InstructionsLoadedCLAUDE.md 등 지침 파일이 컨텍스트에 로드될 때
 ConfigChange세션 도중 설정 파일이 변경될 때
 CwdChanged작업 디렉토리가 변경될 때(예: cd 실행)
 FileChanged감시 중인 파일이 디스크에서 변경될 때

Matcher 패턴

matcher 필드는 어떤 상황에서 Hook을 실행할지 필터링한다.

Matcher 형태동작 방식예시
*, "", 또는 생략모든 경우에 매칭이벤트가 발생할 때마다 실행
문자·숫자·,·|만 포함정확히 일치하는 문자열(복수 지정 가능)Bash는 Bash 도구만, Edit|Write는 둘 중 하나
그 외 특수문자 포함정규식으로 해석mcp__memory__.*는 memory 서버의 모든 도구

각 이벤트는 서로 다른 필드를 기준으로 matcher를 검사한다.
PreToolUse/PostToolUse는 도구 이름을, SubagentStart는 Agent 타입을, SessionStart는 세션 시작 방식(startup, resume 등)을 기준으로 매칭한다.

Handler 종류

Hook이 실제로 실행하는 동작(Handler)은 5가지 타입이 있다.

타입설명
command셸 명령어를 실행 (가장 널리 쓰이는 방식)
httpJSON 데이터를 POST 요청으로 지정된 URL에 전송
mcp_tool이미 연결된 MCP 서버의 도구를 호출
promptClaude 모델에게 단일 턴 프롬프트를 보냄 (yes/no)
agentSubagent를 띄워서 도구를 활용한 검증을 거친 뒤 판단을 받음

command 타입이 가장 단순하고 많이 쓰이지만, 판단 자체를 LLM에 맡기고 싶을 때는 prompt나 agent 타입을 쓸 수 있다.
다만 이 두 타입은 Hook임에도 내부적으로 LLM 판단을 다시 거친다는 점에서 일반적인 Hook의 결정론적 성격과는 다르다.

입력과 출력

Command Hook은 이벤트 정보를 JSON으로 stdin에 받고, 실행 결과는 종료 코드(exit code)와 표준 출력(stdout)으로 전달한다.

종료 코드의미
0성공, stdout에 출력한 JSON을 읽어서 세부 동작을 결정
2차단, stderr 내용이 Claude에게 오류 메시지로 전달됨
그 외대부분 이벤트에서 비차단 오류로 처리되고 작업은 계속됨

예를 들어 PreToolUse에서 exit 2를 반환하면 해당 도구 호출 자체가 차단된다.
반대로 PostToolUse처럼 이미 실행이 끝난 뒤 발생하는 이벤트는 exit 2를 반환해도 작업을 되돌릴 수는 없고, Claude에게 오류 메시지만 전달된다.
exit 코드만으로는 “차단” 또는 “무시” 두 가지밖에 표현할 수 없기 때문에, 더 세밀한 제어가 필요하면 exit 0으로 종료하면서 JSON을 출력하는 방식을 쓴다.
continue, systemMessage, hookSpecificOutput 같은 필드로 대화를 완전히 중단시키거나, 사용자에게 경고 메시지를 보여주거나, 특정 이벤트에 특화된 결정을 반환할 수 있다.

예시: rm 명령어 차단

예를 들어 위험한 rm 명령어를 차단하는 Hook은 다음과 같이 구성된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}

PreToolUse 이벤트가 발생했을 때, matcher가 Bash 도구 호출인지 확인하고, 조건이 맞으면 block-rm.sh 스크립트를 실행하는 구조다.
이 Handler 스크립트는 이벤트 정보를 표준 입력(stdin)으로 받아 위험한 명령어인지 검사한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
else
  exit 0  # 결정 없음: 평소 권한 흐름 그대로 진행
fi

결과는 rm -rf가 포함된 명령이면 exit 0과 함께 permissionDecision: "deny" JSON을 출력해서 도구 호출 자체를 막는다.
안전한 명령이면 아무 JSON 없이 exit 0만 반환하고, 이 경우 평소 권한 흐름(사용자 승인 등)을 그대로 따른다.

위치별 Hook의 종류

Hook도 Skill이나 Agent와 마찬가지로 정의 위치에 따라 범위와 공유 여부가 달라진다.

위치범위공유 여부
~/.claude/settings.json모든 프로젝트개인 로컬 전용
.claude/settings.json현재 프로젝트Git으로 팀과 공유 가능
.claude/settings.local.json현재 프로젝트공유 안 됨(자동으로 gitignore 처리)
관리자 배포 설정조직 전체관리자가 통제
Plugin의 hooks/hooks.jsonPlugin이 활성화된 동안Plugin과 함께 배포
Skill/Agent frontmatter해당 컴포넌트가 활성화된 동안해당 파일에 정의되어 함께 배포

Skill이나 Agent의 frontmatter에도 hooks 필드를 직접 정의할 수 있어서 해당 Skill이나 Agent가 활성화된 동안에만 적용되고 끝나면 자동으로 정리되는 범위가 한정된 Hook을 만들 수 있다.

1
2
3
4
5
6
7
8
9
10
---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---

Skill, Agent, Hook 비교

셋 다 ‘특정 상황에서 Claude의 동작에 개입한다’는 목적은 비슷하지만 개입하는 층이 다르다.

구분SkillAgent(Subagent)Hook
역할지침, 절차 문서독립 실행되는 작업자이벤트 발생 시 실행되는 규칙
개입 방식Claude에게 지침을 제공작업을 별도 컨텍스트로 위임이벤트를 가로채서 직접 실행/차단
실행 주체Claude(지침을 참고해서 판단)별도의 Subagent 프로세스셸 명령어·HTTP·MCP 도구 등 외부 실행체
실행 보장Claude가 따를지는 확률적Claude가 위임할지는 확률적조건만 맞으면 항상 실행(결정론적)
트리거대화 맥락, 수동 호출대화 맥락, 수동 호출생명주기 이벤트(PreToolUse 등)
되돌릴 수 있는 것판단 기준작업 결과도구 호출 자체를 사전에 차단 가능

정리하면 Skill은 무엇을 알아야 하는지, Agent는 누가 수행하는지, Hook은 언제, 무조건 무엇이 실행되는지를 담당한다고 볼 수 있다.
세 개념은 함께 조합할 수도 있다. 예를 들어 특정 Agent에게만 적용되는 Hook을 정의해서, 그 Agent가 파일을 수정할 때마다 자동으로 보안 검사를 돌리게 만들 수 있다. Skill 안에 Hook을 넣어서, 그 Skill이 활성화된 동안에는 항상 특정 검증 스크립트가 실행되도록 강제할 수도 있다.

언제 무엇을 써야 할까

Skill을 쓰는 경우

  • Claude가 특정 절차나 컨벤션을 참고해서 판단하면 되는 경우
  • 강제성보다는 가이드에 가까운 지침일 때

Agent를 쓰는 경우

  • 컨텍스트를 격리해서 별도로 처리하고 싶은 작업일 때
  • 역할별로 도구 권한을 나누고 싶을 때

Hook을 쓰는 경우

  • Claude의 판단 여부와 무관하게 반드시 실행되어야 하는 작업일 때(보안 검사, 린트, 포맷팅)
  • 위험한 명령어를 원천적으로 차단해야 할 때
  • 파일 수정, 커밋, 세션 시작 등 특정 시점에 자동으로 부가 작업을 실행하고 싶을 때

결론

Skill과 Agent가 Claude의 판단 범위 안에서 동작하는 확장이라면, Hook은 그 판단 바깥에서 무조건 실행되는 안전장치에 가깝다.
좋은 워크플로우는 셋을 적절히 나눠 쓰는 것이다. 세 도구는 아래와 같은 기준으로 사용하면 된다.

  • 그냥 하라고 알려주는 것으로 충분하면 Skill
  • 따로 떼어서 시키는 것이 필요하면 Agent
  • 무조건 실행되도록 강제하는 것이 필요하면 Hook
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.