Claude Code가 도구를 실행하기 직전, 위험한 명령을 자동으로 걸러주는 PreToolUse 훅의 동작 원리부터 설정 방법, 실전 활용 팁까지 단계별로 정리합니다.
📌 3줄 요약
- PreToolUse 훅은 Claude Code가 도구를 실행하기 직전에 사용자 스크립트를 먼저 실행하여 위험한 명령을 자동 차단하는 기능이다.
settings.json에 matcher와 command를 등록하면 특정 도구에만 훅을 적용할 수 있다.- 차단 스크립트를 작성하면
rm -rf /같은 위험 명령과 저장소 밖 파일 쓰기를 자동으로 막을 수 있다.
🗂 목차
PreToolUse 훅이 필요한 상황과 동작 원리
Claude Code는 사용자의 요청을 수행하기 위해 파일 편집, 터미널 명령 실행, 파일 생성 등 다양한 도구를 직접 호출합니다.
문제는 이 과정에서 rm -rf /처럼 위험한 명령이나, 프로젝트 폴더 바깥에 파일을 쓰는 실수가 발생할 수 있다는 점입니다.
⚠️ 특히 자동 허용 모드를 켜두면 사용자가 미처 확인하지 못한 채 위험한 명령이 바로 실행될 수 있어서 더 주의가 필요합니다.
PreToolUse 훅(hook)은 Claude Code가 도구를 실행하기 직전에 사용자가 지정한 스크립트를 먼저 실행하는 기능입니다. 이 스크립트가 "차단"을 반환하면 해당 도구 호출이 취소됩니다. 반대로 스크립트가 정상 종료를 반환하면 도구가 그대로 실행되기 때문에, 허용할 명령과 차단할 명령을 스크립트 안에서 자유롭게 구분할 수 있습니다.
🔍 쉽게 비유하면 공항 보안 검색대와 비슷합니다. 승객(도구 호출)이 탑승 게이트(실행)에 도달하기 전에 검색대(훅 스크립트)를 통과해야 하는 구조입니다. 검색대에서 위험물이 감지되면 탑승이 거부되듯, 훅 스크립트가 위험 패턴을 감지하면 도구 호출이 즉시 취소됩니다.
💬 제가 직접 써보니, 훅 없이 Claude Code를 쓸 때는 매번 도구 호출 알림을 눈으로 확인하고 수동으로 허용/거부를 판단해야 했습니다. 훅을 설정하고 나면 위험한 패턴은 자동으로 걸러지기 때문에 안심하고 자동 허용 모드를 활용할 수 있습니다. 사람이 일일이 판단하는 것보다 스크립트가 기계적으로 걸러주는 편이 실수도 적고 작업 흐름도 끊기지 않아서 훨씬 편리합니다.
settings.json에 PreToolUse 훅 등록하는 법
훅 설정은 .claude/settings.json 파일에 작성합니다. 이 파일이 없으면 프로젝트 루트에 .claude 폴더를 만들고 settings.json을 새로 생성하면 됩니다. 이미 .claude 폴더가 존재하는 경우에는 기존 settings.json에 hooks 키만 추가하면 됩니다.
기본 구조는 다음과 같습니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/block_dangerous_cmd.py"
}
]
}
]
}
}
matcher 필드는 어떤 도구에 훅을 적용할지 지정합니다. Bash로 설정하면 터미널 명령 실행 도구에만 훅이 동작합니다. 만약 모든 도구에 훅을 적용하고 싶다면 matcher를 생략하거나 ".*" 같은 정규식 패턴을 사용할 수도 있습니다.
command 필드에는 실행할 스크립트 경로를 넣습니다. 이 스크립트는 표준 입력(stdin)으로 도구 호출 정보를 JSON 형태로 전달받습니다. JSON에는 tool_name과 tool_input 필드가 포함되어 있어서 어떤 도구가 어떤 인자로 호출되었는지 스크립트 안에서 확인할 수 있습니다.
🔍 훅 스크립트가 종료 코드 2를 반환하면 도구 호출이 차단됩니다. 종료 코드 0을 반환하면 정상적으로 도구가 실행됩니다. 차단 시 사용자에게 보여줄 메시지는 stdout에 JSON 형식으로 출력합니다. 형식은 {"decision": "block", "reason": "차단 사유"} 입니다.
위험 명령과 저장소 밖 쓰기를 차단하는 스크립트
이제 실제 차단 로직을 담은 파이썬 스크립트를 작성합니다. .claude/hooks/block_dangerous_cmd.py 파일을 만들어 봅니다.
import sys
import json
import os
def main():
input_data = json.loads(sys.stdin.read())
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
# Bash 명령 차단 목록
if tool_name == "Bash":
command = tool_input.get("command", "")
dangerous = ["rm -rf /", "rm -rf ~", "mkfs", "dd if=",
"> /dev/sda", "chmod -R 777 /"]
for pattern in dangerous:
if pattern in command:
result = {"decision": "block",
"reason": f"위험 명령 감지: {pattern}"}
print(json.dumps(result))
sys.exit(2)
# Write/Edit 도구의 저장소 밖 경로 차단
if tool_name in ("Write", "Edit"):
file_path = tool_input.get("file_path", "")
repo_root = os.getcwd()
abs_path = os.path.abspath(file_path)
if not abs_path.startswith(repo_root):
result = {"decision": "block",
"reason": f"저장소 밖 쓰기 차단: {abs_path}"}
print(json.dumps(result))
sys.exit(2)
sys.exit(0)
if __name__ == "__main__":
main()
dangerous 리스트에 차단할 명령어 패턴을 추가하면 됩니다. 예를 들어 "sudo shutdown" 같은 패턴을 리스트에 넣으면 시스템 종료 명령도 차단할 수 있습니다.
저장소 밖 쓰기 차단은 os.path.abspath()로 절대 경로를 비교하는 방식으로 동작합니다. 상대 경로로 ../../etc/passwd 같이 상위 디렉토리를 타고 올라가는 경우도 절대 경로로 변환한 뒤 비교하기 때문에 우회가 어렵습니다.

Write와 Edit 도구도 함께 차단하려면 settings.json의 matcher를 추가해야 합니다. 다음과 같이 배열에 항목을 하나 더 넣으면 됩니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{"type": "command",
"command": "python .claude/hooks/block_dangerous_cmd.py"}]
},
{
"matcher": "Write|Edit",
"hooks": [{"type": "command",
"command": "python .claude/hooks/block_dangerous_cmd.py"}]
}
]
}
}
matcher에 Write|Edit처럼 파이프(|)로 여러 도구를 지정할 수 있습니다. 하나의 스크립트로 여러 도구를 동시에 감시하는 구조입니다.
💬 제가 직접 써보니, 차단 패턴을 너무 넓게 잡으면 정상적인 rm 명령까지 막히는 문제가 있었습니다. 예를 들어 단순히 "rm"만 패턴으로 넣으면 rm temp.txt 같은 안전한 파일 삭제까지 전부 차단됩니다. rm -rf /처럼 구체적인 패턴으로 좁히되, 프로젝트 내부 파일 삭제는 허용하는 쪽이 실용적이었습니다.
훅 동작 확인과 실전 활용 팁
설정을 마쳤으면 실제로 차단이 되는지 테스트해야 합니다. Claude Code에게 "rm -rf / 실행해줘"라고 요청하면 훅이 동작하여 차단 메시지가 표시됩니다. 마찬가지로 프로젝트 폴더 바깥 경로에 파일을 써달라고 요청하면 Write 도구가 차단되는 것도 확인할 수 있습니다.


차단되면 Claude Code 화면에 훅이 반환한 reason 메시지가 나타납니다. Claude Code는 차단 사유를 읽고 대안을 스스로 제시하기 때문에 작업이 완전히 멈추지는 않습니다. 정상 동작을 확인한 뒤 자동 허용 모드와 함께 쓰면 효과가 극대화됩니다.
실전에서 유용한 팁 몇 가지를 정리합니다.
💡 차단 패턴은 정규식 대신 단순 문자열 매칭으로 시작하는 것을 권장합니다. 정규식은 강력하지만 예상치 못한 오탐이 발생하기 쉽습니다. 단순 문자열 매칭으로 충분한 경우가 대부분이므로, 필요할 때만 정규식으로 전환하는 순서가 안전합니다.
💡 훅 스크립트에서 stderr로 로그를 남기면 디버깅에 도움이 됩니다. print("검사 중: " + command, file=sys.stderr) 한 줄이면 충분합니다. 로그를 남겨두면 어떤 명령이 훅을 통과했고 어떤 명령이 차단됐는지 나중에 추적할 수 있어서 규칙을 개선할 때 유용합니다.
💡 팀 프로젝트에서는 .claude/hooks/ 폴더를 Git에 포함시켜 공유하면 됩니다. 모든 팀원이 동일한 안전 규칙을 적용받게 됩니다.
🔍 settings.json은 프로젝트별(.claude/settings.json)과 사용자별(~/.claude/settings.json) 두 곳에 설정할 수 있습니다. 프로젝트별 설정이 우선 적용되므로, 공통 규칙은 프로젝트 단위로 관리하는 편이 깔끔합니다.
💬 제가 직접 써보니, 훅 스크립트에 문법 오류가 있으면 Claude Code가 도구 자체를 실행하지 못하고 멈추는 현상이 있었습니다. 훅 스크립트를 수정한 뒤에는 반드시 python .claude/hooks/block_dangerous_cmd.py 명령으로 단독 실행하여 오류가 없는지 먼저 확인하는 습관이 중요합니다. 스크립트 단독 실행 시 stdin에 테스트용 JSON을 파이프로 넘기면 차단 로직까지 함께 검증할 수 있어서 더 확실합니다.
✍️ 마치며
PreToolUse 훅은 Claude Code의 강력한 자동화 능력을 안전하게 활용할 수 있게 해주는 핵심 장치입니다. settings.json에 몇 줄만 추가하면 위험 명령 차단과 저장소 밖 쓰기 방지를 자동화할 수 있습니다. 차단 패턴은 단순하게 시작해서 필요에 따라 점진적으로 확장하고, 팀 단위로 공유하면 모두가 같은 안전망 위에서 작업할 수 있습니다. 자동 허용 모드와 함께 사용하면 안전성과 생산성을 동시에 확보할 수 있으니, 아직 설정하지 않았다면 오늘 바로 시작해 보세요.
'AI 툴 문제 해결' 카테고리의 다른 글
| Claude Code 메모리 기능으로 프로젝트 맥락 기억시키는 법 (0) | 2026.07.23 |
|---|---|
| Opus 4.8 전환 뒤 자주 발생하는 설정 오류 해결법 (0) | 2026.07.23 |
| Claude Code 모델 업데이트 후 코딩 결과가 달라졌을 때 확인법 (0) | 2026.07.23 |
| Opus 4.8 업데이트 후 체감되는 코딩 차이와 대응법 (0) | 2026.07.23 |
| Skill·Hook·MCP·서브에이전트 헷갈릴 때 선택 기준 (0) | 2026.07.23 |