PHP 파일 하나를 고칠 때마다 전체 테스트가 돌고, 포매터가 요청하지 않은 파일까지 바꾼다면 편집보다 자동화 수습에 시간이 더 듭니다. Hook은 파일 편집 전후처럼 정해진 사건이 생길 때 자동으로 실행되는 명령입니다. 모델이 필요할 때만 고르는 조언이 아니므로 작은 설정 오류도 매 편집에 반복됩니다.
핵심 Hook은 빠르고 결정적이며 되돌릴 수 있는 검사에만 먼저 씁니다. 이벤트 이름, 실행 대상 조건, 표준 입력 JSON, 종료 코드를 각각 시험하고 네트워크·배포·파일 재수정은 기본 Hook에서 제외합니다.
Hook을 쓰기 전에 현재 설정과 입력 형식부터 읽습니다
다음은 예시 세션입니다. 프로젝트 설정만 대상으로 하고 사용자 전역 설정은 제외합니다.
claude --permission-mode plan
PostToolUse에서 변경된 PHP 한 파일만 문법 검사하려 합니다.
읽기: CLAUDE.md, .claude/settings.json, scripts/**, 공식 Hook 입력 명세
허용: Read, Grep, 샘플 JSON에 대한 로컬 스크립트 실행
금지: ~/.claude 수정, 네트워크, 배포, 실제 파일 자동 포맷
반환: 이벤트·matcher·입력 필드 / 종료 코드 / 추가 파일 변경 가능성 / 수정 후보
중단: 파일 경로 출처나 차단 의미가 불명확할 때
세 고정 입력이 Hook 설정의 합격표가 됩니다
정상 PHP는 한 번 검사해 0, 문법 오류 PHP는 파일명과 한 줄 오류, 대상 밖 파일은 실행 0회가 기대 결과입니다. 프로젝트 Hook과 검사 스크립트가 이 표만 만족하도록 고치고, 사용자 전역 설정이나 실제 소스 재포맷이 변경 내역에 나타나면 중단합니다.
`/hooks`에서 출처를 보고 고정 입력 실행, `php -l`, `git diff -- .claude scripts`, `git diff --check`를 확인합니다. 대상 밖 파일을 바꾸거나 막으면 `disableAllHooks`로 멈추고, 재개 메모에 실패 입력과 설정 출처를 남깁니다.
느린 전체 테스트를 파일 단위 검사로 줄입니다
PostToolUse가 Edit와 Write 뒤에 실행되도록 했는데 명령이 전체 테스트를 부르면 작은 편집도 오래 멈춥니다. Hook 프로세스 안의 포매터가 파일을 다시 써도 새 PostToolUse가 자동으로 생기는 것은 아니지만, 사용자가 검토하지 않은 변경이 조용히 추가될 수 있습니다.
PHP 변경에는 `php -l`, JavaScript 변경에는 해당 파일의 빠른 린트처럼 짧은 검사만 둡니다. 전체 회귀는 작업 종료나 CI에서 한 번 실행합니다.
입력을 문자열로 이어 붙이지 않습니다
공식 Hook은 이벤트 정보를 JSON으로 표준 입력에 전달합니다. 경로를 파싱하고 허용된 저장소 내부 파일인지 확인한 뒤 검사합니다.
#!/usr/bin/env bash
set -euo pipefail
payload="$(cat)"
file="$(jq -r '.tool_input.file_path // empty' <<<"$payload")
case "$file" in
"$PWD"/*.php) php -l "$file" ;;
*) exit 0 ;;
esac
차단과 안내의 종료 코드를 구분합니다
PostToolUse는 편집이 끝난 뒤 실행되므로 문법 오류를 보고하고 Claude가 다음 행동을 고치게 할 수는 있어도 이미 끝난 편집을 되돌리지는 못합니다. 실행 전에 막아야 하는 규칙은 PreToolUse를 검토합니다. 표준 오류에는 사람이 고칠 수 있는 한 문장과 실패 파일을 남깁니다.
실행 대상 조건이 실제 도구 이름과 맞는지, 성공·실패 입력에서 반환 형식이 유효한지 작은 고정 입력으로 시험합니다. `/hooks` 화면에서는 어느 설정 파일에서 온 Hook인지도 확인할 수 있습니다.
배포 전에 세 장면을 재현합니다
정상 PHP 편집은 한 번 검사하고 통과해야 합니다. 문법이 깨진 편집은 빠르게 오류를 돌려주고, Claude가 그 피드백을 받은 뒤 추가 행동을 멈추는지 확인합니다. PHP가 아닌 파일은 아무 일 없이 지나가야 합니다. 이 세 결과와 실행 시간을 기록합니다.
Hook 자체가 설정 파일이나 대상 밖 소스를 수정하면 즉시 `disableAllHooks`로 전체 비활성화하고 원인을 좁힙니다. 관리 정책 Hook은 사용자 설정으로 끌 수 없을 수 있으므로 운영자에게 에스컬레이션합니다.
Hook에 맡기지 않을 것
운영 배포, DB 변경, 외부 메시지 전송, 비밀 회전은 편집 이벤트의 부수 효과로 실행하지 않습니다. 판단이 필요한 코드 리뷰도 결정적 Hook 하나로 승인하지 않습니다.
Hook이 알맞은 자리는 “이 파일은 문법상 유효한가”처럼 같은 입력에 같은 답이 나오는 좁은 질문입니다. 그 범위를 벗어나면 명시적 명령이나 CI 단계가 더 안전합니다.
Hook 결과는 종료 코드와 JSON으로 구분한다
Hook은 이벤트에 따라 입력을 JSON으로 받고, 일반 출력과 종료 코드로 진행 여부를 알립니다. 종료 코드 0은 성공이고 2는 이벤트가 지원할 때 차단 피드백으로 쓰입니다. PostToolUse처럼 이미 일어난 작업 뒤의 이벤트는 이전 작업을 되돌리지 않습니다. 그 밖의 종료 코드는 이벤트별 동작이 다르므로 공식 참조를 확인합니다. 구조화된 판단이 필요하면 표준 출력에 유효한 JSON만 남깁니다.
설정을 바꾼 뒤에는 기존 세션이 아니라 새 세션에서 다시 확인하는 편이 안전합니다. 처음에는 파일 기록처럼 부작용이 작은 Hook으로 입력 형태를 관찰하고, 실제 차단 로직은 실패·타임아웃·중복 실행까지 시험한 뒤 적용합니다. Hook에 비밀값을 출력하지 않습니다.