이 글에서 사용하는 주요 용어 정의
용어정의
| PreToolUse | 도구 인자 확정 후 실제 실행 직전에 호출되는 훅 이벤트 |
| 훅(Hooks) | 특정 시점에 외부 검사 스크립트를 실행하는 확장 기능 |
| 종료 코드 2 | 도구 실행을 취소하고 stderr를 피드백하는 차단 신호 |
| Fail-Closed | 오류나 타임아웃 시 작업을 즉시 차단하는 안전 원칙 |
도입
Claude Code는 자율적인 개발 작업을 지원하지만, 예기치 못한 상황에서 rm -rf나 git reset --hard 같은 파괴적인 셸 명령어를 실행할 위험이 있습니다.
이를 방지하기 위해 도구 실행 직전 외부 검사 로직을 호출하는 PreToolUse 훅의 설정과 차단 메커니즘을 정리합니다.
PreToolUse 훅의 동작 원리와 설정 위치
PreToolUse 이벤트는 도구 인자 확정 후 실제 실행 직전에 호출됩니다. 검사 스크립트는 표준 입력(stdin) JSON 객체에서 tool_input.command를 추출해 유효성을 검사합니다.
[이미지 삽입 #1: 명령어 실행 직전 사전 검사기와 차단 흐름도]
설정은 프로젝트 공용(.claude/settings.json) 등에 정의하며 병합되어 병렬 실행됩니다.
아래는 scripts/guard-rm.sh와 scripts/guard-git.sh를 등록하는 미실행 설정 예시입니다. 서브명령어를 재귀 파싱하는 if 필터로 프로세스를 최적화하고 공백 경로 대비 큰따옴표 인용이나 Exec Form을 적용합니다. v2.1.295 이상에서 지원하는 "onFailure": "block"으로 실패 시 차단(Fail-Closed)을 보장합니다.
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [
{ "type": "command", "if": "Bash(rm *)", "command": "\"${CLAUDE_PROJECT_DIR}/scripts/guard-rm.sh\"", "onFailure": "block" },
{ "type": "command", "if": "Bash(git *)", "command": "\"${CLAUDE_PROJECT_DIR}/scripts/guard-git.sh\"", "onFailure": "block" }
]
}]
}
}
차단 메커니즘 1: 종료 코드 2 활용
스크립트가 표준 입력 JSON의 tool_input.command를 검사해 금지 패턴 감지 시 stderr에 사유를 출력하고 exit 2로 종료합니다. bash와 jq가 필요하며, scripts/guard-rm.sh에 저장 후 chmod +x 권한을 부여합니다.
#!/usr/bin/env bash
# scripts/guard-rm.sh (미실행 예제, bash/jq 필요, chmod +x)
set -euo pipefail
COMMAND=$(jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -Eq '(^|[;&|]\s*)rm\s+.*(-[a-zA-Z]*[rR]|--recursive)'; then
echo "안전 정책 위반: 재귀적 삭제 명령은 실행할 수 없습니다." >&2
exit 2
fi
exit 0
이 스크립트는 rm 명령의 -r/-R이나 --recursive 재귀 삭제를 탐지하며 단순 rm -f는 허용합니다(정규식 특성상 난독화 우회 한계 존재). 일반 에러 exit 1은 비차단 오류이므로 반드시 exit 2를 반환해야 실행 취소 및 stderr 피드백이 이뤄집니다.
차단 메커니즘 2: 최신 JSON 구조화 출력
세밀한 제어가 필요하다면 hookSpecificOutput 규격을 사용합니다. exit 0으로 정상 종료하며 stdout으로 JSON 객체를 반환합니다.
scripts/guard-git.sh로 저장하고 앞서 설정한 "if": "Bash(git *)" 필터를 통해 호출되며, bash·jq 설치 및 chmod +x 권한이 필요합니다.
#!/usr/bin/env bash
# scripts/guard-git.sh (미실행 예제, bash/jq 필요, chmod +x)
set -euo pipefail
COMMAND=$(jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -Eq '(^|[;&|]\s*)git\s+reset\s+--hard'; then
cat <<'EOF'
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "git reset --hard 명령은 변경 유실 위험으로 차단되었습니다. git stash를 검토하세요."
}
}
EOF
exit 0
fi
exit 0
과거 최상위 {"decision": "block"} 대신 hookSpecificOutput의 permissionDecision: "deny"를 사용해야 합니다. permissionDecisionReason의 거부 사유는 모델 피드백으로 전달됩니다.
운영 안정성을 위한 핵심 체크리스트
1. 실패 시 차단(onFailure: "block"): 훅 오류나 타임아웃 시 기본값은 도구 실행입니다. Claude Code v2.1.295 이상에서 지원하는 "onFailure": "block"을 핸들러에 추가해 Fail-Closed 상태를 유지합니다.
2. Exec Form 활용: 경로에 공백이 있다면 큰따옴표로 묶거나 command와 args를 분리하는 Exec Form으로 셸 해석 오류나 주입 위험을 방지합니다.
3. 권한 규칙 병행: 정규식으로 차단하기 어려운 복잡한 명령은 Claude Code의 기본 권한 거부 규칙(Permission Deny Rules)을 병행합니다.
마무리
Claude Code의 PreToolUse 훅은 위험한 셸 명령 실행을 사전에 차단하는 가드레일입니다. 단순 검사는 exit 2로, 사유 전달은 hookSpecificOutput JSON 응답으로 구성합니다.
.claude/settings.json에 스크립트를 등록하고 필요에 따라 onFailure 설정을 검토해 보시기 바랍니다.
참고 링크
'AI' 카테고리의 다른 글
| Codex에서 GitHub 연동하기 ㅡ PR 자동 코드 리뷰부터 codex review 설정까지 (0) | 2026.10.20 |
|---|---|
| Claude Code MCP 서버 연동하기 ㅡ 설치부터 GitHub DB 연결까지 (Windows 트러블슈팅 포함) (0) | 2026.10.20 |
| Claude Code Skills 만들기 SKILL.md로 반복 작업을 명령으로 등록하기 (0) | 2026.10.19 |
| Claude Code 유휴 시간 압축 설정 /config에서 Idle compaction 켜고 끄기 (0) | 2026.10.18 |
| Gemini File Search 사용법: 문서 업로드부터 검색 저장소로 RAG 질의하기 (0) | 2026.10.17 |
