본문으로 건너뛰기
wooncloud

클로드 코드 Hooks

정의 Claude Code의 라이프사이클 이벤트에 사용자 스크립트를 끼워넣는 메커니즘이다. 모델이 도구를 쓰기 직전, 직후, 세션 종료 시점 등에 자동으로 정해진 명령이 실행된다. 지금까지의 자동화(/loop, 루틴, 외부 스케줄러,…

·9 min read· views·

정의

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)와 명령을 등록한다.

{
  "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와 타입체크를 자동으로 거는 것 — 모델이 무슨 코드를 만들든 결과물의 외형은 항상 일정하다. 프롬프트로 "조심해" 적던 시절보다 안심하고 권한을 풀 수 있게 됐다.