Sandcorp샌드코프 · 웹·AI 제품 개발

새 프로젝트 받는 중 · 갱신 2026.10sandcorp.co.krGitHub

← 기록

Claude Code훅Windowsdev-kit

모델이 실수하기 전에 훅이 먼저 막는다 — dev-kit 연재 ③ 작업 환경

지침으로 적어도 자꾸 어기는 것은 Claude Code 훅으로 막았습니다. 큰 파일 통째로 읽기, Windows에서 깨지는 큰 heredoc을 훅이 처리하고, 답을 기다리느라 작업이 멈추던 문제는 질문 패널로 풀었습니다.

모델이 실수하기 전에 훅이 먼저 막는다

dev-kit 연재 ③ — read-guard, block-heredoc, 그리고 기다리지 않는 질문 패널

왜 만들었나

1편과 2편의 규칙은 모두 문서입니다. 모델이 읽고 따라야 하는데, 대화가 길어지면 잊습니다. 「큰 파일은 통째로 읽지 않는다」를 적어 두어도 가끔 26MB짜리 기록 파일을 통째로 열려고 합니다.

그래서 자주 어기고, 어기면 비싼 규칙은 Claude Code 훅으로 옮겼습니다. 훅은 도구가 실행되기 전에 끼어들어 호출을 막거나 고칩니다. 모델이 기억하든 못 하든 동작합니다.

두 훅은 dev-kit의 token-diet 플러그인에 들어 있습니다. 마지막으로, 질문 하나가 작업 전체를 멈추던 문제는 question-panel로 풀었습니다. Claude Code 화면에 도구와 패널을 더하는 확장(Claude Code에서는 mod라고 부릅니다)입니다.

훅이 끼어드는 자리

  1. 도구 호출

    모델이 Read나 Bash를 부른다

    여기까지는 평소와 같다
  2. PreToolUse 훅

    도구가 실행되기 전에 입력을 JSON으로 받아 본다

    Node 스크립트, 5초 안에 답해야 함
  3. 판단

    그대로 통과, 이유와 함께 거부, 또는 입력을 고쳐 허용

    deny / allow + updatedInput
  4. 모델

    거부 이유를 읽고 안내대로 다시 부른다

    지침을 기억하지 못해도 됨

3편에서 다루는 dev-kit 구성

구성끼어드는 곳하는 일
read-guardPreToolUse 훅(Read)1,000줄·80KB를 넘는 파일을 limit 없이 읽으면 거부. 한도는 환경변수로 바꾼다
block-heredocPreToolUse 훅(Bash)6,000자를 넘는 heredoc 명령을 스크립트 파일로 옮겨 source로 실행. 하루 지난 파일은 정리
question-panel확장(mod)ask 도구가 질문을 오른쪽 패널에 쌓고 바로 돌아감. /questions로 열고 /questions-clear로 지움
  • 두 훅은 플러그인을 설치하면 항상 켜집니다. 플러그인 없이 쓰려면 스크립트를 복사해 settings.json의 hooks.PreToolUse에 등록합니다.

부딪힌 문제와 해결

1. 큰 파일을 통째로 읽었다

지침에 「큰 파일은 위치를 찾고 나서 필요한 부분만 읽는다」를 적어 두어도, 모델은 가끔 수천 줄짜리 파일을 통째로 열었습니다. 한 번 읽은 내용은 대화가 끝날 때까지 매 턴 다시 읽힙니다. 1편의 계산대로라면 토큰이 턴 수만큼 곱해집니다.

→ read-guard 훅이 Read 호출을 봅니다. limit이 없고 남은 줄이 1,000줄을 넘거나 파일이 80KB를 넘으면 거부합니다. offset을 주면 그 뒤로 남은 줄만 셉니다. 이미지, PDF, 노트북처럼 Read가 따로 다루는 파일은 건드리지 않습니다.

2. Windows에서 큰 heredoc이 깨졌다

파일 내용을 cat > 파일 <<'EOF'로 통째로 쓰는 명령이 Windows의 Bash에서 가끔 파싱이 깨졌습니다. 깨지면 파일이 반만 쓰이거나 엉뚱한 명령이 실행됐고, 모델은 원인을 찾느라 턴을 썼습니다. 대화 기록을 세어 보니 heredoc 1,224건 중 12건이 깨졌고, 모두 7,600자 이상이었습니다. 6,000자 이하는 한 번도 깨지지 않았습니다.

→ 지침에는 「파일은 Write 도구로 쓴다」를 두고, 그래도 나오는 큰 heredoc은 block-heredoc 훅이 처리합니다. 6,000자를 넘는 명령을 임시 스크립트 파일로 옮기고 source '<파일>'로 바꿔 실행합니다. 같은 셸에서 실행하므로 cd나 변수의 의미는 원래 명령과 같습니다. 커밋 메시지 같은 짧은 heredoc은 건드리지 않습니다.

3. 훅 응답의 한글이 깨졌다

훅은 응답을 JSON으로 표준 출력에 씁니다. Windows 콘솔 인코딩을 거치면서 거부 이유의 한글이 깨졌고, 모델은 읽을 수 없는 이유를 받았습니다.

→ JSON을 쓰기 전에 ASCII 밖의 글자를 모두 \uXXXX 이스케이프로 바꿉니다. JSON으로는 같은 값이라 받는 쪽에서는 한글로 보입니다.

4. Python이 없는 기계가 있었다

read-guard는 처음에 Python 스크립트였습니다. 그런데 작업하는 기계 중에 Python이 없는 Windows PC가 있었습니다. 설치 환경이 기계마다 다르면 훅이 어디서는 동작하고 어디서는 조용히 빠집니다.

→ 두 훅을 모두 Node로 옮기고, 플러그인의 hooks.json이 등록하게 했습니다. 이제 기계마다 플러그인을 설치하기만 하면 됩니다.

5. 질문 하나가 작업 전체를 멈췄다

모델이 사용자에게 객관식으로 물을 때 쓰는 기본 도구(AskUserQuestion)는 답이 올 때까지 턴을 멈춥니다. 사용자가 자리를 비우면, 질문과 상관없는 다른 일까지 함께 멈췄습니다.

→ 질문 패널을 만들었습니다. Claude Code에 도구 하나와 오른쪽 패널을 더하는 확장(mod)입니다. 모델이 ask 도구를 부르면 질문은 패널에 쌓이고 도구는 곧바로 돌아옵니다. 모델은 하던 일을 계속하거나 턴을 마칩니다. 사용자가 나중에 답을 고르고 완료를 누르면 [질문 패널 답] 질문 = 고른 것 형식의 새 메시지가 들어갑니다. 답을 기다려야만 하는 급한 확인만 기본 도구로 묻습니다. 패널은 claude plugin test로 돌리는 시험(질문 쌓기, 모든 질문에 답하지 않으면 완료 막기, 다중 선택, 기타 입력)으로 지킵니다.

남은 한계와 설치

  • read-guard는 Read 도구만 봅니다. Bash의 cat으로 큰 파일을 열면 막지 못합니다. 그쪽은 아직 지침에 의존합니다.
  • heredoc의 6,000자 경계는 한 사용자의 기록에서 잡은 값입니다.
  • 질문 패널은 확장(mod)을 지원하는 Claude Code(2.1.312 이상)에서만 동작합니다.
  • 1편의 wait-ci.py와 worker-stats.py는 아직 Python이라, Python이 없는 기계에서는 돌지 않습니다.

연재에서 소개한 플러그인은 모두 하나의 마켓플레이스로 설치합니다.

/plugin marketplace add farrar142/dev-kit
/plugin install issue-track@dev-kit
/plugin install test-speed@dev-kit
/plugin install token-diet@dev-kit
/plugin install question-panel@dev-kit

규칙은 사고가 나면 늘어납니다. 다음 사고가 나면 그때도 재고, 원인을 하나 찾아, 저장소에 더하겠습니다.


dev-kit 연재 ① 토큰과 조율 · ② 시험 속도 · ③ 작업 환경(이 글)

문의 →