
CLAUDE.md에 작성한 규칙이 지켜지지 않는 문제와, hook이라는 '결정적인 발화 장치'
요약
CLAUDE.md에 작성된 규칙이 LLM의 확률적 판단에 의해 무시되는 문제를 Claude Code의 'hook' 메커니즘을 통해 해결하는 방법을 다룹니다. hook을 활용하여 특정 이벤트 발생 시 AI의 판단과 무관하게 결정적인 제어를 수행하는 원리와 활용 사례를 설명합니다.
핵심 포인트
- CLAUDE.md 규칙은 LLM의 판단에 의존하는 확률적 방식임
- hook은 특정 타이밍에 쉘 커맨드를 실행하는 결정적 제어 메커니즘임
- UserPromptSubmit, PreToolUse, Stop 등 주요 hook 이벤트 활용 가능
- PreToolUse를 통해 툴 실행 전 중단, 허용, 확인 요청 제어 가능
📝 체험 메모: "CLAUDE.md에 썼는데 조용히 지나쳐진다"는 상황은
여러 번 있었습니다. 그것이 "왜 그렇게 되는가"를 연구를 통해 조사하게 된 계기(즉, "왜 똑똑한 AI가 여전히 인간에게 지적을 받는가"를 여러 회사의 AI와 1차 정보로 검증한 회차)가 되었습니다. 거기서부터 산문 형태의 규칙을 hook으로 결정적으로 발화시키는 방향(advice_tripwire / destructive_guard 등)으로 나아갔습니다.
저는 Claude Code에 전달할 규칙을 CLAUDE.md라는 파일에 적어두고 있습니다. "실행하기 전에 목적을 확인해라", "되돌릴 수 없는 조작 전에는 잠시 멈춰라"와 같은 저만의 작법입니다. 한동안 운용하며 깨달은 것은, 작성한 규칙이 항상 지켜지는 것은 아니라는 점이었습니다. 긴 대화 도중이거나 다른 것에 집중하고 있으면, 제대로 적어둔 규칙이 조용히 무시되고 지나쳐집니다.
최근 이 "무시되는 현상"을 Claude Code 공식의 hook이라는 메커니즘으로 막을 수 있다는 것을 알게 되었습니다. 이 기사는 공식 문서의 1차 정보를 읽으면서, "파일에 적은 규칙(프롬프트)"과 "hook(훅)"이 어떻게 역할 분담을 하는지 초보자인 저 나름대로 정리한 것입니다.
애초에 hook이란 무엇인가
hook은 Claude Code의 라이프사이클의 정해진 타이밍에, 사용자가 지정한 쉘 커맨드(shell command)가 자동으로 실행되는 메커니즘입니다. 공식 훅 가이드는 그 존재 이유를 명확히 적고 있습니다.
They provide deterministic control over Claude Code's behavior, ensuring certain actions always happen rather than relying on the LLM to choose to run them.
(hook은 Claude Code의 동작에 대해 결정적인 제어를 제공하며, LLM이 실행을 선택하기를 기대하는 대신 특정 동작이 반드시 일어나도록 보장한다.)
이 부분이 핵심이었습니다. CLAUDE.md에 적은 지시는 AI가 읽고 "따르겠다"라고 판단해야 비로소 효력이 발생합니다. 즉, 확률적입니다. 반면 hook은 대상 이벤트가 발생하면 AI의 판단과는 무관하게 실행됩니다. 같은 "규칙"이라도 효력이 발생하는 성질이 완전히 다르다는 뜻입니다.
어떤 타이밍에 발화할 수 있는가
공식 훅 레퍼런스에는 많은 이벤트가 나열되어 있지만, 제가 주로 사용하는 것은 세 가지입니다.
사용자가 프롬프트를 보낸 직후에 실행되는 UserPromptSubmit. 툴(tool)이 실행되기 직전에 실행되는 PreToolUse. AI가 응답을 마칠 때 실행되는 Stop. 각각 "입력의 순간", "실행 직전", "턴의 종단"에 대응하며, 멈추고 싶은 상황에 맞춰 위치를 선택할 수 있습니다.
할 수 있는 일은 이벤트에 따라 다릅니다. 레퍼런스에 따르면, PreToolUse는 툴 실행을 중단·허용·사용자에게 확인을 요청하는 등의 판정을 할 수 있습니다(hook이 반환하는 permissionDecision 값으로 결정되며, deny = 중단 / allow = 허용 / ask = 확인 요청). UserPromptSubmit은 프롬프트 자체를 교체할 수는 없으며, 문맥(additionalContext)을 옆에 추가할 뿐입니다. 종료 코드(exit code)로 말하면, exit 2가 "그만둬"라는 신호이며, PreToolUse라면 툴 호출을 차단하고, Stop이라면 종료 자체를 막습니다.
제가 배치하고 있는 세 가지 hook
추상론만으로는 몸에 익지 않으므로, 실제로 작동시키고 있는 것들을 적겠습니다. 비밀 값은 하나도 나오지 않는, 탐지 메커니즘에 대한 이야기입니다.
첫 번째는 턴의 종단에서 실행되는 비밀 누출 체크입니다. Stop 이벤트에 추적 대상 파일을 스캔하여 API 키와 유사한 문자열이나 비밀 파일이 섞여 있지 않은지 조사하는 스크립트를 배치해 두었습니다. 발견되면 exit 2로 종료를 차단하여, 혼입된 것을 제외한 뒤 응답을 마치게 합니다. "커밋할 때 주의하자"라는 마음가짐이 아니라, 매 턴 반드시 실행된다는 점이 포인트였습니다.
두 번째는 파괴적인 커맨드의 탐지입니다. rm -r이나 git reset --hard, git push --force와 같이 한 번에 되돌릴 수 없는 커맨드를 PreToolUse
문자열을 대조하여, 걸리면 permissionDecision: "ask"로 자신에게 확인을 요청합니다. 일반적인 git commit이나 단일 파일의 rm은 함정에 빠뜨리지 않습니다. 확인을 너무 자주 하면 그것대로 형식화(形骸化)되기 때문입니다. 대상은 '부수적인 피해가 크고, 되돌릴 수 없는' 조작으로만 한정했습니다.
세 번째는 UserPromptSubmit에서 문맥(Context)을 추가하는 것입니다. 저는 두 대의 PC로 작업하고 있는데, 시작할 때 "최신으로 해줘"라는 암호를 입력하면 최신 상태로 동기화하는( git pull을 하고, 회수하지 못한 작업 브랜치가 없는지도 확인하는) — 저만의 발화점(Trigger point)이 있습니다. 이 "최신으로 해줘"를 포함한 프롬프트를 감지하면, 현재 조작 중인 PC가 어느 것인지 판별하는 정보나, 회수하지 못한 작업 브랜치 목록을 additionalContext로서 주입합니다. 이것은 중단시키는 hook이 아니라, 상기시키는 hook입니다. "최신으로 해줘"라고 입력하는 순간, 긴 CLAUDE.md 어딘가에 묻혀 있는 『최신화 절차』를 그 시점에 필요한 만큼만 눈앞에 가져다주는 역할을 합니다.
왜 "파일에 쓰는 것"만으로는 부족했는가
이 교체를 통해 가장 변한 것은 사고방식 그 자체였습니다.
사고가 발생할 때, 사람은 무심코 "규칙이 허술했나"라고 생각하며 CLAUDE.md에 주의 사항을 추가합니다. 하지만 자신의 실수를 되돌아보면, 진정한 원인은 "규칙이 허술했다"가 아니라 "규칙이 애초에 발화(Trigger)하지 않았다"는 점이 더 많았습니다. 적혀 있음에도 불구하고, 그 순간에 참조되지 않았던 것입니다.
이는 Anthropic 스스로가 지적하는 현상과도 맞닿아 있습니다. Claude Code의 문맥 설계에 관한 엔지니어링 블로그에서, 그들은 문맥(Context)을 "사용할수록 효과가 떨어지는 유한한 자원"으로 취급해야 한다고 쓰고 있습니다. 그렇다면 CLAUDE.md에 행을 계속 추가할수록, 정작 중요한 한 줄이 긴 문장 속에 파묻혀 놓치기 쉬워진다는 논리가 성립합니다. 규칙을 추가하는 것이 오히려 규칙이 작동하지 않게 만드는 원인이 될 수도 있다는 뜻입니다.
그래서 저는 확률에 맡겨도 되는 부분과, 기제로 반드시 발화시키고 싶은 부분을 나누기로 했습니다. "넘지 말아야 할 선"과 "상기해야 할 절차"만을 hook으로 옮기고, 판단 그 자체는 AI에게 남겨두는 것입니다. 이 선긋기입니다.
그래도 판단은 AI에게 남긴다
여기서 오해하고 싶지 않은 점은, "전부 hook으로 만들면 안전하다"는 것이 아니라는 점입니다.
파괴적인 커맨드를 감지하는 hook이 하는 일은, 감지하여 확인을 요청하는 단계까지입니다. "이 조작을 지금 이 대상에게 적용해도 괜찮은가"라는 판단은 여전히 AI와 저에게 남아 있습니다. hook은 "되돌릴 수 있는가·실제 환경인가 복제본인가·부수적인 피해는 무엇인가"를 고민해주지 않습니다. 발화의 방아쇠는 기계로 만들고, 판단은 사람과 AI가 맡는다. 이 분담입니다.
사실 이 구분 방식은 제가 마음대로 정한 것이 아니었습니다. 공식 hook 가이드에도 같은 취지의 문장이 있습니다.
For decisions that require judgment rather than deterministic rules, you can also use prompt-based hooks or agent-based hooks that use a Claude model to evaluate conditions.
(결정적인 규칙이 아니라 판단을 요하는 결정에 대해서는, Claude 모델을 사용하여 조건을 평가하는 "프롬프트 기반(prompt-based)" 또는 "에이전트 기반(agent-based)" hook을 사용할 수도 있다.)
공식에서도 결정적으로 다룰 수 있는 부분과 판단을 요하는 부분을 나누고 있습니다. 기계적으로 발화시키고 싶은 것은 쉘(Shell)의 hook, 판단이 필요한 것은 모델로, 라는 선긋기입니다. 제 방식과 방향이 같았다는 것을 나중에 알고 조금 안심했습니다.
초학자로서 얻은 교훈
코드를 아직 거의 작성할 수 없는 입장이지만, 이 정리를 통해 규칙이 그냥 지나쳐 버리는 횟수를 실제로 줄일 수 있었습니다.
규칙을 CLAUDE.md에 쓰는 것은 AI에게 "이렇게 행동해 주길 바란다"라고 전달하는 행위입니다. 하지만 그것은 확률적이며, 긴 대화 속에서는 놓치기 쉽습니다. 선을 넘지 말아야 할 조작이나, 그 순간 반드시 상기해야 할 절차는 문장으로 부탁하는 것이 아니라 hook이라는 발화 장치로 옮깁니다. 발화는 기계에 맡기고, 판단은 AI와 제가 쥐는 것입니다.
문장으로 작성한 규칙이 지켜지지 않는다고 느껴진다면, 부족한 것은 강한 어조가 아니라 확실하게 발화(trigger)되는 트리거(trigger)였을지도 모릅니다. 저는 지금 CLAUDE.md를 오히려 줄이는 방향으로 움직이면서, "이것만큼은 반드시 발화했으면 좋겠다"라고 생각하는 몇 가지만 hook으로 옮기고 있습니다.
출처
- Claude Code Docs, "Automate actions with hooks" (hook 가이드): https://code.claude.com/docs/en/hooks-guide
- Claude Code Docs, "Hooks reference" (이벤트 목록,
permissionDecision, 종료 코드 사양): https://code.claude.com/docs/en/hooks - Anthropic, "Effective context engineering for AI agents" (문맥은 유한한 자원이라는 관점): https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents
(인용한 영문은 공식 문서의 원문이며, 번역은 필자에 의한 참고용 번역입니다. 사양은 위의 1차 정보를 확인했습니다. 기사 중의 hook 예시는 자신의 환경에서 실제로 작동시키고 있는 탐지 메커니즘만을 비밀 값을 전혀 포함하지 않고 설명하고 있습니다.)
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기