새소식

300x250
AI/Claude Code Doc(공식문서) 번역본

Claude Code 공식문서 리뷰-참고자료(Reference)[5] : Hooks 참조(Hooks reference)

  • -
728x90

안녕하세요! 갓대희입니다. 

Claude Code Docs 공식 문서 >> [참고자료(Reference)] 섹션의 내용 중 [hooks 참고(Hooks reference)]를 살펴 보려고 합니다.

이번 섹션 부터는 영문, 한글번역본이 모두 공식문서로 존재하는 섹션이니 한글 문서를 편하게 참고 하셔도 될 것 같습니다.

https://code.claude.com/docs/en/hooks

 

Hooks reference - Claude Code Docs

This page provides reference documentation for implementing hooks in Claude Code.

code.claude.com

 

이 카테고리의 글은 편하게 공식 문서 위주의 내용을 눈으로 쭉 살펴 보고 넘어가는 목적을 갖고 시작 하게 되었습니다.

저도 초심으로 돌아가 기초적읜 글을 살펴보다보니, 지금와서 클로드에서 강조 하고자 하는 원칙이 어떤건지 되돌아볼 수 있는 계기가 되기도 하는 것 같아, 다른 분들도 꼭 한번 눈으로라도 이해 하고 넘어가는것이 좋다고 생각하여 공식 문서의 내용을 억지로 리뷰해보게 되었습니다.

 

Claude Code 훅(Hooks)

원본: code.claude.com

훅(Hook)이란 무엇인가?

"갈고리"라는 뜻의 훅은 프로그램 실행 흐름의 특정 지점에 "걸어놓는" 코드다. Git 훅이나 React 훅과 같은 개념이다.

Claude Code의 훅은 Claude가 도구를 사용하거나 세션을 시작/종료할 때 자동으로 실행되는 스크립트다. 파일 저장 후 자동 포맷팅, 위험한 명령어 차단, 알림 전송 등 다양한 자동화를 구현할 수 있다.

왜 훅이 필요한가?

자동화 파일 저장 후 자동 포맷팅, 린팅, 테스트 실행
보안 위험한 명령어 차단, 민감 파일 접근 방지
품질 관리 코드 스타일 강제, 커밋 메시지 규칙 적용
알림 특정 이벤트 발생 시 Slack/Discord 알림 전송

처음 듣는 용어들 - 기초 개념 정리

stdin (표준 입력) 프로그램이 데이터를 "입력받는 통로". 훅 스크립트는 Claude Code로부터 JSON 형식의 정보를 stdin으로 받는다.
stdout (표준 출력) 프로그램이 결과를 "출력하는 통로". 훅 스크립트가 JSON을 stdout으로 출력하면 Claude Code가 이를 읽어 처리한다.
stderr (표준 에러) 에러 메시지를 출력하는 통로. 종료 코드 2와 함께 사용하면 Claude에게 에러 메시지를 전달할 수 있다.
종료 코드 (Exit Code) 프로그램이 끝날 때 반환하는 숫자. 0=성공, 2=차단(블록), 그 외=에러. Python에서는 sys.exit(0)으로 설정.
JSON 데이터를 주고받는 형식. {"key": "value"} 형태. 훅 설정과 데이터 교환에 사용된다.

훅 실행 흐름 이해하기

PreToolUse 예시 (파일 저장 전)

사용자: "test.txt 만들어줘"
     │
     ▼
Claude: Write 도구 호출 준비
     │
     ▼
┌─────────────────────────────────┐
│  PreToolUse 훅 실행            │
│                                 │
│  stdin ← {"tool_name":"Write", │
│           "tool_input":{...}}   │
│                                 │
│  [당신의 스크립트 실행]          │
│                                 │
│  stdout → {"decision":"allow"}  │
│  exit(0)                        │
└─────────────────────────────────┘
     │
     ▼ (허용됨)
Write 도구 실행 → test.txt 생성
     │
     ▼
┌─────────────────────────────────┐
│  PostToolUse 훅 실행           │
│                                 │
│  stdin ← {"tool_name":"Write", │
│           "tool_response":{...}}│
│                                 │
│  [자동 포맷팅 등 후처리]        │
│                                 │
│  exit(0)                        │
└─────────────────────────────────┘
     │
     ▼
완료!
        

 

시작하기 전 준비사항

훅을 사용하기 위해 특별한 설치가 필요하지 않다. Claude Code만 설치되어 있으면 된다.

필수 확인 사항
  • Claude Code 설치
    → 터미널에서 claude --version 으로 확인
  • JSON 기본 이해
    → 훅 설정 파일은 JSON 형식을 사용
  • 셸 스크립트 기초 (선택)
    → bash/python 스크립트를 훅으로 사용할 때 필요
처음 써보는가?

아래 "단계별 설정 가이드"의 첫 번째 예제부터 시작하면 된다. 5분이면 첫 훅을 만들 수 있다.

 

설정 파일 - 어디에 훅을 정의하는가?

훅은 다음 파일에서 설정한다 (우선순위 순):

위치 설명 언제 사용?
~/.claude/settings.json 사용자 전역 설정 모든 프로젝트에 적용할 훅
.claude/settings.json 프로젝트 설정 팀과 공유할 프로젝트별 훅
.claude/settings.local.json 로컬 프로젝트 설정 Git에 커밋하지 않을 개인 훅

💡 팁
처음 시작한다면 ~/.claude/settings.json에 테스트용 훅을 먼저 만들어보자. 확인 후 프로젝트 설정으로 옮기면 된다.

 

단계별 설정 가이드 - 첫 훅 만들기

1단계: 설정 파일 만들기

~/.claude/settings.json 파일이 없다면 만들자.

# 디렉토리 생성 (없다면)
mkdir -p ~/.claude

# 빈 설정 파일 생성
echo '{}' > ~/.claude/settings.json

2단계: 간단한 훅 추가하기

파일이 저장될 때마다 로그를 남기는 간단한 훅을 만들어보자.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "echo '파일이 저장되었습니다!'"
          }
        ]
      }
    ]
  }
}
이 훅이 하는 일
  • PostToolUse: 도구 실행 후 동작
  • "matcher": "Write": Write 도구(파일 저장)에만 반응
  • "command": 실행할 셸 명령어

3단계: 훅 확인하기

Claude Code를 실행하고 /hooks 명령어로 등록된 훅을 확인하자.

# Claude Code 실행
claude

# 훅 목록 확인
/hooks

등록된 훅이 표시되면 성공이다. 이제 Claude에게 "test.txt 파일을 만들어줘"라고 요청하면 파일 저장 후 훅이 실행되는 것을 볼 수 있다.

 

훅 유형 - Command vs Prompt

어떤 유형을 선택해야 할까?
대부분의 경우 Command 훅으로 충분하다. LLM의 판단이 필요한 복잡한 조건에서만 Prompt 훅을 사용하자.

유형 설명 사용 예
command bash 명령어 실행
stdin으로 JSON 입력 받음
자동 포맷팅, 린팅, 알림 전송
prompt LLM(Haiku)에 판단 요청
자연어로 조건 정의 가능
"작업이 완료되었는지 확인해줘"
Command 훅 예시
{
  "type": "command",
  "command": "npx prettier --write \"$FILE_PATH\"",
  "timeout": 30
}
Prompt 훅 예시
{
  "type": "prompt",
  "prompt": "사용자가 요청한 모든 작업이 완료되었는지 확인해주세요. 완료되지 않았다면 이유를 설명해주세요.",
  "timeout": 30
}

→ Prompt 훅은 Stop, SubagentStop, UserPromptSubmit, PreToolUse, PermissionRequest 이벤트에서만 지원

 

훅 이벤트 - 언제 훅이 실행되는가?

가장 많이 사용하는 이벤트
처음 시작한다면 PostToolUse (도구 실행 후)와 PreToolUse (도구 실행 전)부터 익혀보자.

이벤트 실행 시점 필터 지원(Matcher 지원)
PreToolUse 도구 호출 (허용/거부 가능) o
PostToolUse 도구 성공적 완료 o
PermissionRequest 권한 대화상자 표시 시 o
Notification 알림 발송 시 o
UserPromptSubmit 사용자 프롬프트 제출 시 x
Stop 메인 에이전트 응답 완료 시 x
SubagentStop 서브에이전트(Task) 완료 시 x
PreCompact 컨텍스트 압축 전 o
SessionStart 세션 시작/재개 시 o
SessionEnd 세션 종료 시 x

 

매처(Matcher) 패턴 - 어떤 도구에 반응할 것인가?

패턴 설명 예시
Write 정확히 Write 도구만 파일 저장 시
Edit|Write Edit 또는 Write (정규식) 파일 편집/저장 시
Notebook.* Notebook으로 시작하는 도구 Jupyter 작업 시
mcp__memory__.* MCP 서버의 특정 도구 memory 서버 모든 도구
* 또는 "" 모든 도구 매칭 전체 로깅 시

주요 도구 이름
Task (서브에이전트), Bash (셸 명령), Glob, Grep, Read, Edit, Write, WebFetch, WebSearch

 

상황별 사용 가이드 - 이럴 때 이렇게

상황 1: "파일 저장 후 자동 포맷팅하고 싶어요"

JavaScript/TypeScript 파일 저장 후 Prettier로 자동 포맷팅하는 예시다.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path // .tool_input.path' | xargs -I{} npx prettier --write {}"
          }
        ]
      }
    ]
  }
}
상황 2: "위험한 명령어를 차단하고 싶어요"

rm -rf, git push --force 등 위험한 명령어를 실행 전에 차단하는 예시다.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.claude/scripts/block_dangerous.py"
          }
        ]
      }
    ]
  }
}
block_dangerous.py 스크립트 보기
#!/usr/bin/env python3
import json, sys, re

input_data = json.load(sys.stdin)
command = input_data.get("tool_input", {}).get("command", "")

# 위험한 패턴 정의
dangerous_patterns = [
    r"rm\s+-rf\s+/",
    r"git\s+push\s+.*--force",
    r":(){ :|:& };:",  # fork bomb
    r"mkfs\.",
    r"dd\s+if=.+of=/dev/"
]

for pattern in dangerous_patterns:
    if re.search(pattern, command, re.IGNORECASE):
        print(json.dumps({
            "hookSpecificOutput": {
                "hookEventName": "PreToolUse",
                "permissionDecision": "deny",
                "permissionDecisionReason": f"위험한 명령어 감지: {pattern}"
            }
        }))
        sys.exit(0)

# 위험하지 않으면 통과
sys.exit(0)
상황 3: "세션 시작 시 환경을 자동 설정하고 싶어요"

프로젝트 진입 시 nvm으로 Node 버전을 설정하거나, 가상환경을 활성화하는 예시다.

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "source ~/.nvm/nvm.sh && nvm use 20"
          }
        ]
      }
    ]
  }
}
상황 4: "작업 완료 시 Slack으로 알림을 받고 싶어요"

Claude가 응답을 완료하면 Slack 웹훅으로 알림을 보내는 예시다.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "curl -X POST -H 'Content-type: application/json' --data '{\"text\":\"Claude 작업 완료!\"}' $SLACK_WEBHOOK_URL"
          }
        ]
      }
    ]
  }
}

 

실전 Python 예제 - 복사해서 바로 사용하기

아래 예제들은 공식 문서의 패턴을 따르며, 복사해서 바로 사용할 수 있다.

예제 1: Bash 명령어 검증 (grep → rg 권장)

비효율적인 명령어 사용 시 더 좋은 대안을 제안하는 훅이다.

#!/usr/bin/env python3
# ~/.claude/scripts/validate_bash.py
import json
import re
import sys

# 검증 규칙: (패턴, 권장 메시지)
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "'rg' (ripgrep)가 'grep'보다 빠릅니다",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "'rg --files' 또는 'fd'가 'find -name'보다 빠릅니다",
    ),
]

def validate_command(command: str) -> list[str]:
    issues = []
    for pattern, message in VALIDATION_RULES:
        if re.search(pattern, command):
            issues.append(message)
    return issues

try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")

# Bash 도구가 아니거나 명령어가 없으면 통과
if tool_name != "Bash" or not command:
    sys.exit(0)

issues = validate_command(command)

if issues:
    for message in issues:
        print(f"• {message}", file=sys.stderr)
    sys.exit(2)  # 차단

sys.exit(0)  # 통과
예제 2: UserPromptSubmit - 민감 정보 검사 및 컨텍스트 추가

사용자 프롬프트에서 비밀번호/토큰을 감지하고, 현재 시간을 컨텍스트로 추가한다.

#!/usr/bin/env python3
# ~/.claude/scripts/check_prompt.py
import json
import sys
import re
import datetime

try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

prompt = input_data.get("prompt", "")

# 민감 정보 패턴 검사
sensitive_patterns = [
    (r"(?i)\b(password|secret|key|token)\s*[:=]", "프롬프트에 민감 정보가 포함된 것 같습니다"),
]

for pattern, message in sensitive_patterns:
    if re.search(pattern, prompt):
        output = {
            "decision": "block",
            "reason": f"보안 정책 위반: {message}. 민감 정보 없이 다시 작성해주세요."
        }
        print(json.dumps(output))
        sys.exit(0)

# 통과 시 현재 시간 컨텍스트 추가
context = f"현재 시간: {datetime.datetime.now()}"
print(context)

sys.exit(0)
예제 3: PreToolUse - 문서 파일 자동 승인

.md, .txt, .json 파일 읽기는 자동 허용하여 권한 요청을 건너뛴다.

#!/usr/bin/env python3
# ~/.claude/scripts/auto_approve_docs.py
import json
import sys

try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})

# Read 도구이고 문서 파일인 경우 자동 승인
if tool_name == "Read":
    file_path = tool_input.get("file_path", "")
    if file_path.endswith((".md", ".mdx", ".txt", ".json")):
        output = {
            "hookSpecificOutput": {
                "hookEventName": "PreToolUse",
                "permissionDecision": "allow",
                "permissionDecisionReason": "문서 파일 자동 승인"
            },
            "suppressOutput": True  # 출력 숨김
        }
        print(json.dumps(output))
        sys.exit(0)

sys.exit(0)

 

훅 입출력 - 데이터 주고받기

훅 입력 (stdin으로 받는 JSON)

모든 훅은 stdin으로 JSON 데이터를 받는다:

{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/current/working/directory",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  // 이벤트별 추가 필드...
}

이벤트별 추가 입력 필드

이벤트 추가 필드
PreToolUse / PostToolUse tool_name, tool_input, tool_use_id
PostToolUse + tool_response
UserPromptSubmit prompt
Notification message, notification_type
SessionStart source (startup|resume|clear|compact)
SessionEnd reason (clear|logout|prompt_input_exit|other)

종료 코드로 제어하기

종료 코드 의미 동작
0 성공 stdout을 JSON으로 파싱하여 제어
2 차단 오류 stderr만 Claude에 전달, 작업 차단
기타 비차단 오류 stderr 상세 모드에서만 표시

 

PreToolUse 결정 제어 - 도구 허용/거부

PreToolUse 훅에서 JSON 출력으로 도구 실행을 제어할 수 있다:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",  // allow | deny | ask
    "permissionDecisionReason": "허용 이유",
    "updatedInput": {  // 선택: 입력값 수정
      "file_path": "/modified/path"
    }
  }
}
결정 설명
allow 권한 시스템 우회하여 즉시 허용
deny 도구 호출 완전 차단
ask 사용자에게 확인 요청

 

이벤트별 JSON 출력 형식

각 이벤트는 hookSpecificOutput을 통해 이벤트별 특수한 제어가 가능하다. 종료 코드 0으로 exit할 때 stdout에 JSON을 출력하면 된다.

PermissionRequest - 권한 요청 제어

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",  // allow | deny
      "updatedInput": {...},   // 선택: 입력값 수정
      "message": "거부 이유",   // deny시 표시할 메시지
      "interrupt": false       // 작업 중단 여부
    }
  }
}

PostToolUse - Claude에게 추가 컨텍스트 제공

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "린팅 결과: 경고 2개 발견됨"  // Claude에게 전달할 정보
  }
}

UserPromptSubmit - 프롬프트 검증 및 컨텍스트 추가

// 프롬프트 차단하기
{
  "decision": "block",
  "reason": "민감한 정보가 포함되어 있습니다. 다시 입력해주세요."
}

// 컨텍스트 추가하기
{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "현재 시간: 2025-01-08 14:30, 브랜치: feature/new-ui"
  }
}

Stop / SubagentStop - 작업 계속 진행하기

// Claude가 멈추지 않고 계속 작업하도록 하기
{
  "decision": "block",
  "reason": "테스트가 아직 실행되지 않았습니다. 테스트를 실행해주세요."  // 필수
}

SessionStart - 세션 시작 시 컨텍스트 로드

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "프로젝트: my-app, Node: v20.10.0, 마지막 커밋: fix login bug"
  }
}

 

Prompt 기반 훅 응답 스키마

Prompt 기반 훅(type: "prompt")은 LLM(Haiku)이 판단한 결과를 다음 스키마로 반환한다.

{
  "decision": "approve",       // "approve" | "block"
  "reason": "판단 이유 설명",
  "continue": false,           // 작업 계속 여부
  "stopReason": "사용자에게 보여줄 메시지",
  "systemMessage": "경고 또는 컨텍스트"
}

지원 이벤트
Prompt 기반 훅은 다음 이벤트에서만 사용 가능하다:
Stop, SubagentStop, UserPromptSubmit, PreToolUse, PermissionRequest

 

환경 변수 - 훅에서 사용 가능한 변수

변수 설명 이벤트
CLAUDE_PROJECT_DIR 프로젝트 루트 절대 경로 모든 훅
CLAUDE_CODE_REMOTE 웹 환경이면 "true", CLI면 빈 값 모든 훅
CLAUDE_ENV_FILE 환경변수 영속화용 파일 경로 SessionStart만

 

환경 변수 영속화 - 세션 전체에서 유지하기

왜 환경 변수 영속화가 필요한가?

일반적으로 훅에서 설정한 환경 변수는 훅이 끝나면 사라진다. CLAUDE_ENV_FILE을 사용하면 세션 전체에서 환경 변수를 유지할 수 있다. nvm으로 Node 버전을 바꾸거나, Python 가상환경을 활성화할 때 유용하다.

기본 사용법

#!/bin/bash
# ~/.claude/scripts/setup-env.sh

if [ -n "$CLAUDE_ENV_FILE" ]; then
  # 환경 변수를 파일에 기록하면 세션 전체에서 사용 가능
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export API_KEY=your-api-key' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0

실전 예제: nvm으로 Node 버전 설정

nvm으로 Node 버전을 바꾸면 PATH 등 여러 환경 변수가 변경된다. 이 변경사항을 캡처해서 세션에 적용하는 예시다.

#!/bin/bash
# ~/.claude/scripts/nvm-setup.sh

# 변경 전 환경 변수 저장
ENV_BEFORE=$(export -p | sort)

# nvm 활성화 및 Node 버전 설정
source ~/.nvm/nvm.sh
nvm use 20

# 변경된 환경 변수만 추출해서 저장
if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  # comm -13: ENV_BEFORE에만 있는 것 제외, ENV_AFTER에서 새로 추가된 것만
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0
설정 파일 예시
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/scripts/nvm-setup.sh"
          }
        ]
      }
    ]
  }
}

 

이벤트별 상세 매처

일부 이벤트는 특별한 매처 값을 지원한다. 이를 통해 더 세밀한 제어가 가능하다.

Notification 이벤트 매처

매처 설명
permission_prompt 권한 요청 시 알림
idle_prompt 60초 이상 사용자 입력 대기 시
auth_success 인증 성공 시
elicitation_dialog MCP 도구 elicitation 시

SessionStart 이벤트 매처

매처 설명
startup 새 세션 시작 시
resume --resume, --continue, /resume로 재개 시
clear /clear 명령 후
compact 자동/수동 컴팩트 후

PreCompact 이벤트 매처

매처 설명
manual /compact 명령으로 수동 호출 시
auto 컨텍스트 창이 가득 차서 자동 호출 시

 

MCP 도구 훅 - MCP 서버 도구에 훅 연결하기

MCP 도구 네이밍 패턴

MCP(Model Context Protocol) 서버의 도구는 특별한 네이밍 패턴을 따른다:
mcp__<서버이름>__<도구이름>

MCP 도구 이름 예시

도구 이름 설명
mcp__memory__create_entities memory 서버의 create_entities 도구
mcp__filesystem__read_file filesystem 서버의 read_file 도구
mcp__github__search_repositories github 서버의 search_repositories 도구

MCP 도구 훅 설정 예시

{
  "hooks": {
    "PreToolUse": [
      {
        // memory 서버의 모든 도구에 반응
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory 작업 시작' >> ~/mcp-log.txt"
          }
        ]
      },
      {
        // 모든 MCP 서버의 write로 시작하는 도구에 반응
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.claude/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

 

문제 해결 - 자주 겪는 문제와 해결 방법

문제 1: "훅이 실행되지 않아요"

훅을 설정했는데 아무 반응이 없다면 다음을 확인하자.

해결 방법
  1. JSON 문법 확인
    cat ~/.claude/settings.json | jq .
    → 에러가 나면 JSON 문법이 잘못된 것
  2. /hooks 명령으로 확인
    → Claude Code에서 /hooks 실행하여 등록 확인
  3. 매처 이름 확인 (대소문자 구분)
    → "write"가 아니라 "Write"여야 함
  4. 스크립트 실행 권한 확인
    chmod +x ~/.claude/scripts/your-script.py

문제 2: "훅이 너무 느려요"

훅 실행 시간이 길면 Claude Code 응답이 느려진다.

해결 방법
  • 타임아웃 설정: 훅에 "timeout": 10 추가 (초 단위)
  • 백그라운드 실행: 로깅 등 결과가 필요 없는 작업은 &로 백그라운드 실행
  • 무거운 작업 분리: 린팅/테스트는 PostToolUse 대신 별도 워크플로우로

문제 3: "디버깅하고 싶어요"

훅이 어떻게 동작하는지 자세히 보고 싶다면 디버그 모드를 사용하자.

해결 방법
# 디버그 모드로 실행
claude --debug

# 출력 예시:
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Hook command completed with status 0

 

보안 고려사항

⚠️ 주의
훅은 사용자 권한으로 임의의 셸 명령을 실행한다. 잘못된 훅은 파일 삭제, 시스템 손상을 일으킬 수 있다.

모범 사례 설명
입력 검증 stdin으로 받은 JSON 데이터를 반드시 검증
셸 변수 인용 $VAR가 아닌 "$VAR" 사용
경로 순회 차단 경로에서 .. 패턴 확인
절대 경로 사용 CLAUDE_PROJECT_DIR 활용
민감 파일 보호 .env, .git/, 키 파일 접근 제한

 

설정 안전성 - 훅 변경 시 동작

💡 초급자 안내

Claude Code는 보안을 위해 세션 시작 시 훅 설정을 스냅샷으로 저장한다. 세션 중간에 훅을 수정해도 즉시 적용되지 않는다. 이는 악성 코드가 훅을 임의로 변경하는 것을 방지하기 위한 안전장치다.

훅 스냅샷 동작 방식

시점 동작
세션 시작 현재 훅 설정을 스냅샷으로 캡처
세션 진행 중 스냅샷에 저장된 훅만 실행 (파일 변경 무시)
외부에서 훅 수정 시 사용자에게 경고 메시지 표시
변경 적용 방법 /hooks 메뉴에서 검토 후 수동 적용

훅 변경 후 적용하기

훅 설정을 수정한 후 즉시 적용하려면:

# 방법 1: 새 세션 시작
claude # 새로 시작하면 변경된 훅 자동 로드

# 방법 2: /hooks 메뉴에서 적용
/hooks # 세션 내에서 변경사항 검토 및 적용

💡 왜 이렇게 동작하나요?

만약 훅이 즉시 적용된다면, 악성 스크립트가 다음과 같은 공격을 할 수 있다:

  1. Claude가 Write 도구로 .claude/settings.json을 수정
  2. 악의적인 훅이 바로 활성화됨
  3. 다음 도구 실행 시 악성 코드 실행

스냅샷 방식은 사용자의 명시적 확인 없이는 훅이 변경되지 않도록 보호한다.

 

실행 세부사항

항목
기본 타임아웃 60초 (훅별 설정 가능)
병렬화 모든 매칭 훅이 병렬 실행
중복 제거 동일한 훅 명령은 자동 중복 제거
출력 표시 상세 모드(Ctrl+O)에서 진행 표시

 

핵심 요약 - 빠른 참조

5분 만에 훅 시작하기
  1. ~/.claude/settings.json 파일 생성
  2. "hooks" 객체에 이벤트와 훅 정의
  3. Claude Code 재시작 또는 /hooks로 확인
  4. 동작 테스트
하고 싶은 것 사용할 이벤트
파일 저장 후 포맷팅 PostToolUse + matcher: "Edit|Write"
위험한 명령 차단 PreToolUse + matcher: "Bash"
세션 시작 시 환경 설정 SessionStart + matcher: "startup"
작업 완료 알림 Stop
특정 파일 접근 확인 PreToolUse + matcher: "Read"

 

관련 문서

문서 설명
공식 Hooks 문서 최신 공식 문서 (영문)
Settings 설정 파일 전체 가이드
MCP MCP 서버 연동
CLI 레퍼런스 명령줄 옵션
훅으로 Claude Code를 더 강력하게 활용하자

자동 포맷팅, 보안 검사, 알림까지 - 반복 작업을 자동화하고 개발 워크플로우를 개선할 수 있다.

300x250
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.

💡 AI 관련 질문이 있나요? 눌러보세요!