모듈 5: Hooks - 자동화 트리거

학습 목표

  • Hook의 개념과 동작 시점을 이해한다
  • 프로젝트에 정의된 Hook들의 역할을 설명할 수 있다
  • 간단한 Hook을 직접 작성할 수 있다
  • Hook의 보안 고려사항을 이해한다

Hook이란?

Claude Code 생명주기의 특정 시점에 자동으로 실행되는 쉘 스크립트입니다.

AI가 “선택적으로” 실행하는 것이 아니라, 설정된 조건에서 반드시 실행됩니다.

AI의 선택 = 불확실
Hook = 확실한 실행 보장

Hook 이벤트 목록

이벤트트리거 시점주요 용도
PreToolUse도구 실행 직전위험한 명령 차단
PostToolUse도구 실행 성공 후자동 포맷, 로그
UserPromptSubmit사용자 프롬프트 전송 시알림, 사전 처리
SubagentStop서브에이전트 완료 시결과 통합
Stop메인 에이전트 응답 완료최종 정리
SessionStart세션 시작환경 초기화
PreCompact컨텍스트 압축 전중요 정보 보존

프로젝트 Hook 설정 파일

// .claude/hooks/example-hook-config.json
 
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/log-tool-usage.sh",
            "description": "파일 편집 시 자동 로그"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "...(위험 명령 차단 스크립트)...",
            "description": "위험한 bash 명령 차단"
          }
        ]
      }
    ]
  }
}

Hook 1: 파일 편집 후 로그 (PostToolUse)

설정

"PostToolUse": [
  {
    "matcher": "Edit|Write|MultiEdit",
    "hooks": [{
      "type": "command",
      "command": ".claude/hooks/log-tool-usage.sh"
    }]
  }
]

동작

AI가 파일 편집 (Edit 도구)
        |
        v (자동 실행)
log-tool-usage.sh
  - 편집된 파일명 기록
  - 타임스탬프 기록
  - tool_use.log에 저장

활용 사례

  • 어떤 파일을 AI가 수정했는지 추적
  • 코드 리뷰 전 AI 수정 이력 확인
  • 팀 작업 이력 관리

Hook 2: 위험 명령 차단 (PreToolUse)

설정

"PreToolUse": [
  {
    "matcher": "Bash",
    "hooks": [{
      "type": "command",
      "command": "bash -c 'input=$(cat); cmd=$(echo \"$input\" | jq -r \".tool_input.command\"); if [[ \"$cmd\" =~ (rm|delete).*(\\*|\\.env|credentials|secret) ]]; then echo \"{\\\"action\\\": \\\"block\\\", \\\"message\\\": \\\"위험한 명령 차단\\\"\"}; else echo \"{}\"; fi'"
    }]
  }
]

동작

AI가 bash 명령 실행 시도
        |
        v (PreToolUse 실행)
명령어 검사:
  "rm -rf *"        -> BLOCK (위험)
  "rm .env"         -> BLOCK (환경 파일)
  "dotnet build"    -> 허용
  "dotnet test"     -> 허용
        |
        v (차단 시)
{"action": "block", "message": "위험한 명령 차단: rm -rf *"}
        |
        v (허용 시)
{}  (빈 객체 = 허용)

차단 조건

현재 설정에서 차단하는 패턴:

  • rm * 또는 delete * - 와일드카드 삭제
  • rm .env - 환경 설정 파일 삭제
  • rm credentials - 인증 정보 파일 삭제
  • rm secret - 비밀 키 파일 삭제

Hook 3: 검증 알림 (UserPromptSubmit)

설정

"UserPromptSubmit": [
  {
    "hooks": [{
      "type": "command",
      "command": "bash -c '...test/validate 키워드 감지 시 validation-gates 알림...'"
    }]
  }
]

동작

사용자: "이 코드 테스트해줘"
        |
        v (UserPromptSubmit 실행)
프롬프트에 "test" 키워드 감지
        |
        v (stderr로 알림)
"Reminder: validation-gates subagent 사용을 권장합니다."
        |
        v
Claude에게 프롬프트 전달 (차단 아님, 알림만)

Hook 4: 서브에이전트 완료 알림 (SubagentStop)

동작

validation-gates 에이전트 작업 완료
        |
        v (SubagentStop 실행)
"Validation gates completed. Running final checks..."
(stderr에 출력 - 개발자에게 알림)

Hook 입출력 형식

입력 (stdin JSON)

// PostToolUse - Edit 도구 실행 후
{
  "tool_name": "Edit",
  "tool_input": {
    "file_path": "src/ui/ViewModels/MyViewModel.cs",
    "old_string": "old code",
    "new_string": "new code"
  },
  "tool_result": { "success": true }
}
// PreToolUse - Bash 도구 실행 전
{
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf *"
  }
}

출력 (stdout JSON)

// 허용 (진행)
{}
 
// 차단 (중단)
{
  "action": "block",
  "message": "이유 설명"
}

Hook 설정 적용 방법

프로젝트 전용 설정

# .claude/settings.json에 추가
cp .claude/hooks/example-hook-config.json .claude/settings.json

전체 사용자 설정 (모든 프로젝트에 적용)

# Windows: %APPDATA%\Claude\settings.json
# Mac/Linux: ~/.claude/settings.json

실습: 커스텀 Hook 작성

목표: C# 파일 편집 후 자동으로 빌드 경고 확인

# .claude/hooks/check-build-warning.sh
 
#!/bin/bash
 
# stdin에서 JSON 읽기
INPUT=$(cat)
 
# 편집된 파일명 추출
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
 
# .cs 파일만 처리
if [[ "$FILE" == *.cs ]]; then
    echo "C# 파일 편집 감지: $FILE" >&2
 
    # 빌드 실행하여 경고 확인
    BUILD_OUTPUT=$(dotnet build 2>&1)
    WARNING_COUNT=$(echo "$BUILD_OUTPUT" | grep -c "warning")
 
    if [ "$WARNING_COUNT" -gt 0 ]; then
        echo "주의: 빌드 경고 ${WARNING_COUNT}개 발생" >&2
    fi
fi
 
# Hook 성공 (Claude 계속 진행)
echo "{}"
// settings.json에 등록
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{
          "type": "command",
          "command": ".claude/hooks/check-build-warning.sh"
        }]
      }
    ]
  }
}

Hook 디버깅

# Claude Code 디버그 모드 실행
claude --debug
 
# 출력에서 Hook 관련 로그 확인
# [HOOK] PostToolUse triggered for Edit
# [HOOK] Command: .claude/hooks/log-tool-usage.sh
# [HOOK] Exit code: 0
# [HOOK] Output: {}

보안 고려사항

Hook은 쉘 명령을 실행하므로 주의가 필요합니다:

# 위험: stdin 데이터를 직접 eval
eval "$(echo "$INPUT" | jq -r '.command')"  # 절대 금지
 
# 안전: jq로 특정 필드만 추출 후 검증
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" =~ ^[a-zA-Z0-9/_.-]+$ ]]; then  # 경로 형식 검증
    # 처리
fi

주의사항:

  • 입력 데이터 항상 검증
  • 절대 경로 사용 권장 (PATH 조작 방지)
  • 민감한 정보 (API Key 등) Hook 스크립트에 하드코딩 금지

핵심 정리

  • Hook = Claude 생명주기 이벤트에 반드시 실행되는 쉘 스크립트
  • AI의 선택이 아닌 결정론적 자동화
  • 주요 용도: 위험 명령 차단, 자동 포맷, 작업 로그, 알림
  • 입력: stdin JSON / 출력: {} (허용) 또는 {"action":"block"} (차단)
  • .claude/settings.json에 등록하여 활성화
  • 보안: 입력 데이터 항상 검증, eval 금지