---
title: "클로드 코드 Hooks"
description: "정의 Claude Code의 라이프사이클 이벤트에 사용자 스크립트를 끼워넣는 메커니즘이다. 모델이 도구를 쓰기 직전, 직후, 세션 종료 시점 등에 자동으로 정해진 명령이 실행된다. 지금까지의 자동화(/loop, 루틴, 외부 스케줄러,…"
date: 2026-05-03
updated: 2026-05-03T13:55:02.627Z
tags: [클로드, 클로드코드, claude, claude code, hooks]
canonical: https://blog.wooncloud.com/posts/7
---

## 정의

Claude Code의 라이프사이클 이벤트에 사용자 스크립트를 끼워넣는 메커니즘이다. 모델이 도구를 쓰기 직전, 직후, 세션 종료 시점 등에 자동으로 정해진 명령이 실행된다. 지금까지의 자동화(/loop, 루틴, 외부 스케줄러, webhook)가 "**언제 Claude를 깨울까**"였다면, hooks는 "**Claude가 일하는 동안 무엇을 강제할까**"다. 카테고리가 다르다.

## git hook을 떠올리면 된다

git에 pre-commit, post-commit 훅이 있는 것과 같은 발상이다. 이벤트 시점에 정해둔 스크립트가 자동 실행된다. 차이는 git이 커밋 라이프사이클을 다루고 hooks는 에이전트 라이프사이클을 다룬다는 점뿐이다.

## 주요 이벤트

이벤트 종류는 몇 개 안 된다.

- **PreToolUse.** 도구 호출 직전. 차단·검증·승인 게이트.
- **PostToolUse.** 도구 호출 직후. 포맷팅·테스트·로깅.
- **UserPromptSubmit.** 사용자 입력 직후. 컨텍스트 주입·정책 체크.
- **Stop.** 세션 종료. 정리·리포트.
- **Notification.** 사람 주의가 필요한 시점.

## 가장 흔한 용도 셋

**1. 자동 포맷팅·린트.** PostToolUse(Edit/Write)에 prettier·eslint·gofmt를 건다. 모델이 어떻게 코드를 짜든, 끝나면 항상 팀 컨벤션에 맞는 코드만 남는다.

**2. 위험 명령 차단.** PreToolUse(Bash)에서 `rm -rf`, `git push --force`, 운영 DB 접속 같은 패턴을 정규식으로 잡아 거부한다. 모델이 헛짓할 가능성을 사람 신뢰가 아니라 코드로 막는다.

**3. 자동 테스트.** PostToolUse(Edit)에서 영향 받은 파일의 테스트를 돌린다. 실패하면 결과가 모델 컨텍스트로 돌아가서 모델이 알아서 고친다. 사람이 "테스트 돌려봐"라고 말할 필요가 없다.

## 설정 형태

`.claude/settings.json` 같은 설정 파일에 매처(matcher)와 명령을 등록한다.

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write $CLAUDE_FILE_PATHS" }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "./scripts/block-dangerous.sh" }
        ]
      }
    ]
  }
}
```

훅 스크립트는 stdin으로 이벤트 페이로드를 JSON으로 받고, exit code와 stdout으로 결과를 돌려준다. exit code 0이면 통과, 비0이면 차단(또는 모델에 피드백)이다.

## 왜 프롬프트 대신 hooks인가

"위험한 명령은 쓰지 말라"고 시스템 프롬프트에 적어둘 수도 있다. 작동은 한다. 가끔. 모델은 확률적이라서, 같은 규칙도 1000번 중 한두 번은 어긴다.

hooks는 결정론적이다. 모델이 동의하든 안 하든 무조건 실행된다. **모델 출력은 신뢰하지 말고, 코드로 강제하라**는 원칙의 가장 직접적인 구현이다. 프롬프트는 의도, hooks는 보장이다.

## 하네스 엔지니어링 관점

> "every component in a harness encodes an assumption about what the model can't do on its own."

Anthropic 공식 표현이다. 하네스의 모든 부품은 **모델이 혼자서 못 하는 것**에 대한 가정의 결정체다. hooks는 이 원칙을 가장 깔끔하게 보여주는 부품이다.

- 모델이 매번 포맷터를 부른다고 믿을 수 없다 → PostToolUse 훅
- 모델이 위험 명령을 절대 안 친다고 믿을 수 없다 → PreToolUse 훅
- 모델이 테스트를 빠짐없이 돌린다고 믿을 수 없다 → PostToolUse 훅

모델이 어떤 동작을 안정적으로 잘 하게 되는 순간, 그에 대응하는 훅은 뺄 수 있다. 거꾸로 모델이 새로운 일을 시작했는데 그게 못 미더우면 훅을 새로 단다. 즉 hooks는 **모델 능력의 이동에 따라 같이 진화하는 가드레일**이다.

## 다른 자동화와의 직교성

hooks는 /loop, 루틴, 외부 스케줄러, webhook 어느 것과도 같이 쓴다. 어느 트리거로 Claude가 깨어나든 라이프사이클은 똑같이 흐르고, 훅은 그 안에서 작동한다.

| | 트리거 | 라이프사이클 가드 |
|---|---|---|
| /loop, 루틴 | ✅ | — |
| 스케줄러 + 헤드리스 | ✅ | — |
| Webhook + 헤드리스 | ✅ | — |
| **Hooks** | — | ✅ |

자동화는 보통 "트리거 하나 + hooks"의 조합으로 쌓인다. 그래서 발표·블로그 구조에서도 트리거 카테고리(/loop, 루틴, 스케줄러, webhook, GitHub Actions) 다 끝낸 뒤에 hooks를 마지막에 두는 게 자연스럽다.

## 운영 시 유의점

- **훅은 빨라야 한다.** 매 도구 호출마다 실행되니 1초씩만 느려도 세션 전체가 답답해진다. 무거운 작업은 백그라운드로 던지고 훅 자체는 즉시 끝낸다.

- **실패 정책을 명시하라.** 훅이 깨졌을 때 차단할지 통과시킬지를 미리 정해야 한다. 포맷터 실패는 통과, 보안 검사 실패는 차단 — 같은 식으로 종류별로 다르다.

- **무한 루프 조심.** PostToolUse 훅이 또 도구를 호출하게 만들면 루프가 돈다. 훅은 부수 효과만, 새 작업은 모델에게.

- **컨텍스트 주입 시 토큰 비용.** UserPromptSubmit에서 너무 많은 컨텍스트를 자동 주입하면 매 턴 토큰이 누적된다. 필요한 것만.

## 정리

hooks는 자동화의 **트리거**가 아니라 **가드레일**이다. 모델을 언제 깨울지가 아니라, 깨어난 모델이 어떻게 행동할지를 코드로 강제한다. 프롬프트로 "잘 좀 해줘" 빌기에서 벗어나, 안 해도 강제되는 보장을 만드는 단계다. 자동화를 진지하게 운영할 거면 결국 훅을 안 쓸 수가 없다.

> **내 경우엔**
> PR 자동 리뷰 워커를 만들면서 처음 진지하게 hooks를 붙였다. 가장 효과 본 건 PreToolUse(Bash) 훅으로 운영 DB 접속·force push·rm 패턴을 정규식 차단한 것. 두 번째는 PostToolUse(Edit) 훅으로 prettier와 타입체크를 자동으로 거는 것 — 모델이 무슨 코드를 만들든 결과물의 외형은 항상 일정하다. 프롬프트로 "조심해" 적던 시절보다 안심하고 권한을 풀 수 있게 됐다.
