본문 바로가기

실전 원칙

AI에게 규칙을 지시서로 맡길 때와 훅으로 강제해야 할 때

문제: AI가 규칙을 '읽었다'고 지킨 게 아니다

AI 코딩 에이전트에게 "push 전에는 반드시 검토해"라고 문서로 적어두면, 대부분의 경우 잘 지킨다. 그런데 '대부분'이 문제다. 컨텍스트가 길어지거나, 급한 작업이 끼거나, 프롬프트가 살짝 달라지면 같은 규칙도 건너뛴다. 문서는 부탁이지 강제가 아니기 때문이다.
필자는 여러 레포를 한 AI 에이전트로 관리하면서 이 경계를 마주했다. 전 시스템 공통 규칙 체계를 만들고, 그 첫 규칙 RULE-001(push 전 필수 검토)을 '문서로 부탁할지, 훅으로 막을지' 판단해야 했다. 결론부터 말하면 두 방식은 서로 다른 층위이며, 섞어 쓰되 역할을 나눠야 한다.


왜 문서만으로는 위험한가

문서 기반 규칙의 약점은 두 가지다. 첫째, 확률적이다. LLM은 지시를 따를 확률을 높일 뿐 100%를 보장하지 않는다. 둘째, 드리프트가 생긴다. 레포마다 규칙 파일을 복사해두면 시간이 지나며 내용이 조금씩 어긋난다.

드리프트 문제는 정본(source of truth)을 한 곳에 두는 방식으로 줄였다. 규칙 원본은 컨트롤 레포의 rules/GLOBAL-RULES.md 한 곳에만 두고, 관리 대상 9개 레포의 CLAUDE.md에는 임포트 한 줄만 넣는다.

@../ai-automation-control/rules/GLOBAL-RULES.md

이 한 줄은 각 레포의 CLAUDE.md가 공통 규칙 파일을 그대로 끌어오게 한다. 규칙을 고칠 때 원본 한 파일만 수정하면 되므로, 레포마다 사본이 달라지는 드리프트가 원천적으로 사라진다.
하지만 임포트로 드리프트를 막아도, '읽고 따를 확률'은 여전히 100%가 아니다. 진짜 어기면 안 되는 규칙은 실행 자체를 막는 편이 안전하다.


문서로 둘 규칙과 훅으로 내릴 규칙 구분

모든 규칙을 훅으로 만들면 유지보수가 무거워지고, 반대로 전부 문서에 맡기면 강제력을 보장하지 못한다. 독자가 자기 규칙을 나눌 때 참고할 만한 기준을 정리하면 다음과 같다.

스타일·컨벤션 (변수명, 커밋 메시지 형식)낮음, 되돌리기 쉬움문서(정본+임포트)
권장 절차 (PR 설명 작성 등)중간, 사람이 보완 가능문서 + 리뷰
비가역·위험 동작 (push, 배포, 삭제)높음, 되돌리기 어려움훅으로 결정적 차단

실제로 첫 규칙 RULE-001(push 전 필수 검토)은 세 번째 칸에 해당한다고 판단해, 문서만이 아니라 Claude Code의 PreToolUse 훅으로 강제했다.


어떻게 훅으로 강제했는가

강제 로직은 사용자 레벨 PreToolUse 훅 rules/hooks/check-push-review.sh가 담당한다. 동작 절차는 다음과 같다.

  1. 에이전트가 실행하려는 명령을 훅이 가로챈다(git push 실행 직전).
  2. 대상 레포가 관리 대상인지 판별한다. 비관리 레포(회사 프로젝트 등)는 즉시 통과시켜 영향이 전혀 없게 한다.
  3. 관리 레포라면 검토 승인 마커 .git/push-review-approved가 있는지, 그리고 그 마커가 현재 HEAD 해시와 일치하는지 확인한다.
  4. 마커가 없거나 해시가 다르면 push를 차단한다.

여기서 핵심은 마커를 HEAD 해시와 묶은 점이다. 단순히 '검토했다는 파일이 있으면 통과'로 두면, 검토 후 커밋을 하나 더 얹고 push하는 우회가 가능하다. 해시를 묶으면 커밋이 바뀐 순간 마커가 무효가 되어 이 우회가 자동으로 막힌다.


실패 사례: 오탐과 미탐을 둘 다 테스트해야 한다

첫 구현은 실행할 명령 문자열에 "push"라는 단어가 들어있는지 단순 매칭했다. 이게 바로 사고 났다. 커밋 메시지에 "push" 단어가 들어간 경우까지 push 명령으로 오인해 막으면 안 되는 걸 막는 오탐이 발생한 것이다.

그래서 명령 문자열을 shlex로 토큰 단위 분해하고, git의 하위 명령이 실제로 push인지 판별하도록 고쳤다. 여기에 더해 git -C <경로> push처럼 작업 디렉터리를 바꿔 우회하는 미탐(막아야 할 걸 놓침) 경로까지 테스트 케이스로 확인하고 나서야 완성됐다.

차단 훅을 만들 때 얻은 교훈은 명확하다. 차단기는 오탐(false positive)과 미탐(false negative)을 둘 다 테스트해야 한다. 하나만 보면, 너무 헐거워 뚫리거나 너무 빡빡해 정상 작업을 막는다.

증상 원인 확인 방법
정상 커밋인데 push가 막힘 커밋 메시지의 push 단어를 명령으로 오인(오탐) 메시지에 push 넣은 커밋으로 push 시도
검토 안 했는데 push 성공 git -C 등 우회 경로(미탐) 디렉터리 지정 push를 직접 실행
검토 후 커밋 추가로 우회 마커가 HEAD와 무관 승인 후 새 커밋 얹고 push 시도

적용 체크리스트

  • 규칙을 '어기면 되돌리기 어려운가'로 분류했는가? 그렇다면 문서가 아니라 훅 후보다.
  • 규칙 정본을 한 곳에만 두고, 나머지는 임포트/링크로 참조해 드리프트를 막았는가?
  • 차단 조건을 문자열 포함이 아니라 토큰/명령 단위로 판별하는가?
  • 승인 마커를 커밋 해시 등 '내용'과 묶어 우회를 막았는가?
  • 오탐 테스트(정상 작업이 막히지 않는지)와 미탐 테스트(우회 경로가 뚫리지 않는지)를 둘 다 돌렸는가?
  • 관리 대상이 아닌 곳에는 훅이 영향을 주지 않도록 예외를 명시했는가?

한계와 주의사항

이 사례는 Claude Code의 PreToolUse 훅과 git 레포 환경 기준이다. 다른 에이전트나 CI 환경에서는 훅을 거는 지점(pre-commit, 서버 사이드 pre-receive, CI 파이프라인)이 달라지므로 그대로 옮길 수는 없다. 여기서 가져갈 것은 스크립트가 아니라 원칙이다.
또한 사용자 레벨 훅은 그 사용자 환경에서만 작동한다. 조직 차원에서 규칙을 강제하려면 서버 측 훅(pre-receive 등)이나 브랜치 보호 규칙처럼 개인 환경 밖의 지점에 거는 방안을 함께 검토하는 편이 좋다. 다만 그런 수단도 권한 설정과 운영 정책에 따라 우회 여지가 달라질 수 있으므로, '거는 것'만으로 안전이 완성되지는 않는다. 훅이 있다고 사람이 검토를 대충 해도 된다는 뜻도 아니다 — 훅은 '검토를 안 하면 못 나가게' 막을 뿐, 검토의 질까지 보장하지는 못한다.

반응형