15. Hooks(훅)

🎯 이 장의 목표
  • Hook이 무엇이고 왜 "모델 지침"보다 강력한지 이해한다
  • 주요 라이프사이클 이벤트와 그 쓰임을 안다
  • PreToolUse 훅으로 위험한 동작을 차단하는 법을 안다
  • 훅이 "보안 경계"가 아니라 "기회적 가드레일"임을 이해한다

Hook이란

Hook은 Claude Code에서 무언가 일어날 때 실행되는 이벤트 기반 스크립트입니다. 프롬프트(모델의 해석에 의존)와 달리, 훅은 결정적 코드를 실행합니다 — 환각하지 않습니다.

비유하자면, 훅은 생산 라인의 자동 센서입니다. 작업자(모델)가 깜빡하든 말든, 특정 지점을 지나는 모든 제품에 대해 정해진 검사가 기계적으로 발동합니다.

📌 핵심 대비: CLAUDE.md에 "절대 rm -rf 쓰지 마"라고 적으면 컨텍스트 압박에 잊히거나 무시될 수 있습니다(9장에서 본 "안내일 뿐"). 하지만 PreToolUse 훅으로 rm -rf를 막으면 매번, 결정 시점에, 에러 메시지와 함께 발동합니다.

왜 훅이 필요한가

훅 없이는 모든 안전장치가 "모델이 지침을 이해하는지"에 달려 있습니다. 훅이 있으면 시스템 수준에서 규칙을 강제합니다:

  • rm -rf를 실행 전에 차단
  • 프로젝트 컨텍스트 자동 주입
  • 모든 도구 호출을 감사 로그로 기록
  • 편집 후 자동 린트 실행
flowchart LR
    A[이벤트 발생<br/>예: 도구 실행 직전]:::user --> B[훅 스크립트 실행<br/>결정적 코드]:::tool
    B --> C{종료 코드}:::danger
    C -->|0: 허용| D[도구 실행]:::result
    C -->|2: 거부| E[차단 + 에러 메시지]:::danger
    C -->|1: 오류| F[비차단 오류]:::agent

    classDef user fill:#FFE082,stroke:#F9A825,color:#000
    classDef tool fill:#90CAF9,stroke:#1E88E5,color:#000
    classDef danger fill:#EF9A9A,stroke:#E53935,color:#000
    classDef result fill:#A5D6A7,stroke:#43A047,color:#000
    classDef agent fill:#80DEEA,stroke:#00ACC1,color:#000
📌 핵심
예를 들어 PreToolUse 훅은 도구 실행 전 발동해, 도구 이름·입력을 stdin으로 받습니다. 종료 코드 2면 호출을 거부하고, 0이면 허용합니다.

주요 라이프사이클 이벤트

훅이 발동하는 지점은 30개가 넘습니다. 실제로 자주 쓰는 것들:

이벤트발동 시점차단 가능?용도
UserPromptSubmit프롬프트 제출 시프롬프트 검사·수정
PreToolUse도구 실행 직전1차 보안 체크포인트
PermissionRequest권한 요청 시자동 승인/거부
PostToolUse도구 완료 후❌(로깅만)린트 실행·결과 기록
Stop / SubagentStopClaude/서브에이전트 종료 시미완 작업 계속 강제
PreCompact컨텍스트 컴팩션 직전트랜스크립트 백업
SessionStart / SessionEnd세션 시작/끝컨텍스트 로드·정리
InstructionsLoaded지침 파일 로드 시어떤 규칙이 로드됐는지 디버깅
💡 팁
정보성 이벤트(차단 불가)는 막을 순 없지만 로깅·알림에 좋습니다. 예: PostToolUse로 편집 후 린트 결과를 주입, SessionEnd로 정리 작업.

훅 작성과 범위

훅은 settings.jsonhooks에 정의하며, 정의 위치가 곧 범위를 정합니다.

위치범위용도
프로젝트 .claude/settings.json팀 공유팀 정책 (커밋)
개인 ~/.claude/settings.json모든 프로젝트개인 가드레일

활용 예:

  • 비-협상 규칙: .env 파일 절대 읽기 금지, 외부 서비스에 글 올리기 전 항상 확인, 모든 도구 호출 감사 로깅
  • 계속 유도: Stop 훅이 Claude의 최종 응답을 검토해, 미완 작업을 "다 됐다"고 합리화하면 계속하도록 강제
  • 결정 시점 컨텍스트 주입: post-write 린트 결과, pre-tool 보안 경고
💡 팁
프롬프트 기반 훅은 기본 30초 타임아웃입니다. 필요하면 timeout 필드로 조정하세요. (Haiku 등이 JSON을 코드펜스로 감싸 파싱이 깨지지 않도록, 출력 형식을 명확히 지시하는 게 좋습니다.)

⚠️ 훅은 "보안 경계"가 아니다

매우 중요한 구분입니다. 훅은 결정적이지만 보안 경계는 아닙니다. 프롬프트 주입으로 우회될 수 있습니다.

flowchart TB
    A[Hook]:::tool --> B{무엇인가}:::agent
    B --> C[기회적 시점의<br/>'구조화된 개입']:::result
    B -.아님.-> D[난공불락의 벽]:::danger

    classDef tool fill:#90CAF9,stroke:#1E88E5,color:#000
    classDef agent fill:#80DEEA,stroke:#00ACC1,color:#000
    classDef result fill:#A5D6A7,stroke:#43A047,color:#000
    classDef danger fill:#EF9A9A,stroke:#E53935,color:#000

훅은 가드레일이지 벽이 아닙니다(guardrails, not walls). 도구 호출을 가로채고, 컨텍스트를 주입하고, 알려진 나쁜 패턴을 막고, 에이전트 행동을 유도하는 — 기회가 좋은 시점의 구조화된 개입입니다. 진짜 격리가 필요하면 OS 수준 샌드박싱을 써야 합니다(→ 23장).

📌 핵심
정리하면: 강제력은 deny 규칙 > 훅 > CLAUDE.md 순이고, OS 수준 격리가 필요하면 샌드박스. 용도에 맞는 층을 고르세요.

메모리·스킬·서브에이전트와 비교

자주 헷갈리는 네 가지를 정리하면:

메커니즘본질발동
CLAUDE.md / Skill지식(무엇을 알/할지)모델이 판단
Subagent격리된 일꾼위임 시
Hook결정적 코드라이프사이클 이벤트에 자동
💡 팁
"내 에이전트 설정이 엉망"인 상황의 흔한 원인은 층을 헷갈리는 것입니다. 강제해야 할 행동 제약을 시스템 프롬프트(CLAUDE.md)에 적거나, 라우팅 로직을 서브에이전트 안에 넣는 식이죠. 강제·이벤트 기반은 훅, 지식은 CLAUDE.md/스킬, 격리는 서브에이전트입니다.

이 장에서 배운 것

  • Hook은 라이프사이클 이벤트에 발동하는 결정적 스크립트로, 모델 지침과 달리 환각하지 않는다.
  • 주요 이벤트: PreToolUse(1차 보안 체크포인트)·PostToolUse·Stop·PreCompact·SessionStart 등. PreToolUse는 종료 코드로 허용(0)/거부(2)를 결정한다.
  • settings.jsonhooks에 정의하며 위치가 범위를 정한다(프로젝트=팀, 유저=개인).
  • 훅은 가드레일이지 벽이 아니다 — 프롬프트 주입으로 우회 가능. 진짜 격리는 샌드박스.
  • 강제력은 deny 규칙 > 훅 > CLAUDE.md. 층을 헷갈리지 말 것.

✍️ 확인 문제

  1. "절대 rm -rf 금지"를 CLAUDE.md에 적는 것과 PreToolUse 훅으로 막는 것은 어떻게 다른가요?
  2. PreToolUse 훅에서 종료 코드 0과 2는 각각 무엇을 의미하나요?
  3. "훅은 가드레일이지 벽이 아니다"라는 말의 의미와, 진짜 격리가 필요할 때의 대안은 무엇인가요?
다음 장: 16. Plugins(플러그인) — 지금까지의 확장들을 묶어 배포하기.