AI 개발 가이드

Claude Code Hooks 완전 가이드 - PreToolUse부터 Stop까지 자동화 워크플로 설계

소개왕 탑백귀 2026. 5. 7. 18:40

Claude Code Hooks 완전 가이드 - PreToolUse부터 Stop까지 자동화 워크플로 설계

2026년 5월 기준 | AI 개발 가이드

요약: Claude Code의 Hooks는 도구 실행 전후, 사용자 입력, 작업 종료 같은 특정 시점에 셸 명령을 자동 실행하는 기능입니다. 포매터 자동 적용, 위험 명령 차단, 작업 종료 알림 같은 워크플로를 settings.json 한 곳에서 정의할 수 있어요. 공식 문서 기준으로 8가지 Hook 종류와 matcher 패턴을 정리하고, 실제로 자주 쓰는 예시 세 가지를 단계별로 살펴봅니다.

1. Hooks가 무엇이고 왜 필요한가

Claude Code를 어느 정도 써본 분들이라면 한 번쯤 이런 상황을 겪어보셨을 거예요. 코드를 수정하고 나서 매번 포매터를 직접 돌려야 한다거나, "이 명령은 위험하니 절대 실행하지 마" 같은 지시를 매번 프롬프트에 적어야 하는 경우 말이죠. 매번 같은 지시를 반복하다 보면 빠뜨리기 쉽고, 그러다 보면 결과물 품질도 들쭉날쭉해집니다.

Hooks는 이런 반복 작업을 settings.json 파일에 한 번 정의해두면, Claude Code가 알아서 적절한 시점에 실행해주는 기능입니다. 예를 들어 "Edit 도구가 호출된 후에는 prettier를 자동으로 돌려라"라고 적어두면, 코드를 수정할 때마다 포매터가 자동으로 적용됩니다.

automation workflow gears process

출처: Pixabay (geralt)

중요한 점은 Hooks가 모델의 판단에 의존하지 않는다는 것입니다. 프롬프트로 지시한 규칙은 모델이 잊거나 무시할 수 있지만, Hooks는 settings.json에 등록된 셸 명령으로 실행되기 때문에 결정적으로 동작합니다. "거의 항상 잘 따르는 규칙"과 "항상 실행되는 규칙"의 차이라고 보면 됩니다.

또 하나의 장점은 팀 단위로 표준화하기 쉽다는 점입니다. 프로젝트 루트의 .claude/settings.json에 Hook을 정의하고 git에 커밋하면, 같은 프로젝트를 받은 동료도 동일한 자동화 환경에서 작업하게 됩니다. 코드 컨벤션, 빌드 명령, 보안 정책을 코드 형태로 공유할 수 있는 셈이죠.

2. Hook 종류 8가지 정리

2026년 5월 공식 문서 기준으로 사용 가능한 Hook 이벤트는 8가지입니다. 각각이 어느 시점에 트리거되는지를 알아두면, 원하는 자동화를 어떤 Hook에 걸어야 하는지 판단하기 쉬워집니다.

이벤트 이름 트리거 시점 대표 활용
PreToolUse 도구 호출 직전 위험 명령 차단, 입력 검증
PostToolUse 도구 호출 직후 포매터, 린터, 테스트 자동 실행
UserPromptSubmit 사용자가 프롬프트 제출 시 컨텍스트 자동 주입, 프롬프트 로깅
Stop 메인 에이전트가 응답을 마칠 때 완료 알림, 결과 후처리
SubagentStop 서브에이전트가 응답을 마칠 때 서브에이전트별 결과 로깅
Notification 권한 요청·입력 대기 시 사운드, 데스크톱 알림
PreCompact 컨텍스트 압축 직전 대화 백업, 요약 저장
SessionStart 세션 시작 시점 환경 변수 로드, 초기 컨텍스트 출력

가장 자주 쓰는 건 PreToolUse, PostToolUse, Stop 세 가지입니다. PreToolUse는 도구 실행을 막거나 검증할 때 쓰고, PostToolUse는 실행 후 후처리, Stop은 작업이 끝났을 때 알림을 받는 용도예요.

SubagentStop은 메인 에이전트가 Task 도구를 통해 서브에이전트를 호출했을 때, 그 서브에이전트가 종료된 시점에 트리거됩니다. 메인 Stop과 달리 부분 작업의 완료를 추적할 수 있어요.

3. settings.json 작성법과 matcher 패턴

Hook은 세 곳의 settings.json에 정의할 수 있습니다. 우선순위와 적용 범위가 다르니 상황에 맞게 골라 쓰면 됩니다.

  • 유저 글로벌: ~/.claude/settings.json — 모든 프로젝트에 공통 적용
  • 프로젝트: .claude/settings.json — git에 커밋, 팀 공유용
  • 로컬: .claude/settings.local.json — 본인만 쓰는 개인 설정 (gitignore)

예를 들어 팀 공통 규칙은 프로젝트 settings.json에 두고, 개인 알림 사운드 같은 건 로컬 settings.local.json에 두는 식이죠.

json configuration code editor

출처: Pixabay (StockSnap)

기본 구조는 다음과 같습니다.

// .claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_FILE_PATHS\"",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

핵심은 matcher입니다. matcher는 어떤 도구가 호출됐을 때 이 Hook을 실행할지 정규식으로 지정합니다. 위 예시처럼 "Edit|Write"로 적으면 Edit 도구나 Write 도구가 호출됐을 때만 트리거되고, MultiEdit 같은 다른 도구는 무시됩니다.

matcher를 비워두면 모든 도구에 매칭되고, 와일드카드 "*"도 같은 의미입니다. Bash 명령에 한해 동작하게 하려면 "Bash", MCP 도구에만 걸고 싶으면 "mcp__.*" 같은 정규식을 쓸 수 있습니다.

Hook 명령에는 환경변수가 자동으로 주입됩니다. 가장 자주 쓰는 두 개를 알아두면 충분합니다.

  • $CLAUDE_FILE_PATHS — Edit/Write/Read에서 처리한 파일 경로 (공백 구분)
  • $CLAUDE_PROJECT_DIR — 현재 프로젝트 루트 디렉토리

이 두 변수만 알아도 대부분의 자동화는 충분히 만들 수 있습니다.

4. 실전 예시 1: 자동 포매터 (PostToolUse)

가장 먼저 만들어볼 만한 Hook은 자동 포매터입니다. Claude Code가 코드를 수정할 때마다 prettier나 black을 자동으로 돌리도록 해두면, 포맷 일관성이 좋아져요.

// JS/TS 프로젝트용 자동 포매터
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"$CLAUDE_FILE_PATHS\" | tr ' ' '\\n' | grep -E '\\.(js|ts|tsx|jsx)$' | xargs -r npx prettier --write",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

이 명령은 Claude Code가 수정한 파일 중 JS/TS 확장자만 골라서 prettier를 적용합니다. 다른 확장자는 자연스럽게 건너뛰기 때문에 마크다운이나 설정 파일을 수정할 때 에러가 나지 않아요.

Python 프로젝트라면 같은 패턴으로 black이나 ruff를 적용할 수 있습니다.

// Python 프로젝트용 ruff 포매터
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"$CLAUDE_FILE_PATHS\" | tr ' ' '\\n' | grep -E '\\.py$' | xargs -r ruff format",
            "timeout": 20
          }
        ]
      }
    ]
  }
}

여기서 한 가지 팁이 있어요. PostToolUse Hook은 비동기로 실행되지 않습니다. 즉, Hook이 실행되는 동안 Claude Code는 다음 작업으로 넘어가지 않고 기다립니다. 그래서 timeout을 너무 길게 잡으면 작업 흐름이 답답해질 수 있어요. 포매터는 보통 1~3초면 끝나기 때문에 30초 timeout이면 여유가 충분합니다.

실제로 사용해보면, 포매터를 일일이 돌릴 필요가 없어진다는 점이 가장 편합니다. 코드 리뷰에서 "들여쓰기 맞춰주세요" 같은 코멘트가 거의 사라지죠.

5. 실전 예시 2: 위험 명령 차단 (PreToolUse)

Claude Code가 강력한 만큼 위험한 명령을 실행할 가능성도 있습니다. rm -rf / 같은 극단적인 명령은 거의 일어나지 않지만, git push --forcegit reset --hard 같은 명령이 의도치 않게 실행되는 일은 종종 생깁니다.

security shield protection lock

출처: Pixabay (TheDigitalArtist)

PreToolUse Hook은 도구 실행 직전에 트리거되며, exit 코드를 통해 도구 호출을 차단할 수 있습니다. 차단 명령은 셸 스크립트로 작성하면 됩니다.

// 위험 명령 차단 Hook
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/scripts/block_dangerous.sh"
          }
        ]
      }
    ]
  }
}

Hook이 호출하는 셸 스크립트는 다음과 같이 작성합니다. Hook은 stdin을 통해 도구 호출 정보를 JSON으로 받습니다.

#!/usr/bin/env bash
# .claude/scripts/block_dangerous.sh

# Claude Code가 실행하려는 명령을 stdin JSON에서 추출
CMD=$(jq -r '.tool_input.command // ""')

# 차단 패턴 정의
BLOCK_PATTERNS=(
  "git push.*--force"
  "git reset --hard"
  "rm -rf /"
  "chmod 777"
  "sudo "
)

for pattern in "${BLOCK_PATTERNS[@]}"; do
  if [[ "$CMD" =~ $pattern ]]; then
    echo "BLOCKED: 위험 명령 패턴 감지 — $pattern" >&2
    exit 2  # exit 2: Claude Code에 차단 신호 전달
  fi
done

exit 0  # 통과

여기서 핵심은 exit 코드입니다. exit 0이면 그대로 진행, exit 2이면 도구 호출이 차단되고 stderr 메시지가 Claude Code에 전달됩니다. 모델은 차단 사유를 보고 다른 방법을 시도하거나 사용자에게 확인을 요청하게 됩니다.

차단 패턴은 프로젝트 성격에 맞게 조정하면 됩니다. 운영 DB에 직접 쿼리하는 명령, 마이그레이션 강제 적용, 환경변수 파일 덮어쓰기 같은 것들도 후보가 됩니다.

6. 실전 예시 3: 작업 종료 알림 (Stop)

Claude Code에 긴 작업을 시켜놓고 다른 일을 하다 보면, 작업이 언제 끝났는지 모를 때가 많습니다. Stop Hook으로 작업 종료 알림을 만들어두면 멀티태스킹이 한결 편해져요.

notification bell alert smartphone

출처: Pixabay (geralt)

가장 가벼운 방법은 시스템 사운드를 재생하는 것입니다. macOS, Linux, Windows별로 명령이 다릅니다.

// macOS: 작업 종료 시 시스템 사운드
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "afplay /System/Library/Sounds/Glass.aiff"
          }
        ]
      }
    ]
  }
}

Linux는 paplayaplay, Windows는 PowerShell의 [console]::beep() 같은 명령으로 대체할 수 있습니다.

좀 더 적극적으로 알리고 싶다면 데스크톱 알림이나 Slack 메시지를 보낼 수도 있어요. 다음은 Slack 웹훅으로 메시지를 보내는 예시입니다.

// Slack 웹훅으로 종료 알림
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "curl -s -X POST -H 'Content-type: application/json' --data '{\"text\":\"Claude Code 작업이 종료되었습니다.\"}' $SLACK_WEBHOOK_URL",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

SLACK_WEBHOOK_URL은 환경변수로 두는 편이 안전합니다. settings.json에 직접 적으면 git에 커밋될 위험이 있고, 웹훅 URL이 노출되면 누구나 메시지를 보낼 수 있게 되니까요. 로컬 settings.local.json에 적거나, 셸 환경변수로 export 해두는 방식이 권장됩니다.

Notification Hook도 비슷하게 활용할 수 있습니다. Claude Code가 사용자에게 입력을 요청하거나 권한 승인을 기다릴 때 트리거되니, 자리를 비웠을 때 사운드로 알려주는 용도로 적합합니다.

7. 디버깅과 로깅

Hook이 의도대로 동작하지 않을 때 가장 먼저 할 일은 로그를 남기는 것입니다. Hook 명령에서 stderr로 출력한 내용은 Claude Code의 디버그 모드에서 확인할 수 있고, 파일로 직접 저장하는 방법도 있어요.

dashboard monitoring metrics analytics

출처: Pixabay (StockSnap)

모든 Hook 호출을 로그 파일에 남기는 디버깅용 Hook 예시입니다.

// 모든 PostToolUse 호출을 파일로 로깅
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "cat | tee -a $HOME/.claude/hook_log.jsonl > /dev/null"
          }
        ]
      }
    ]
  }
}

이렇게 해두면 Claude Code가 도구를 호출할 때마다 입출력 JSON이 hook_log.jsonl에 한 줄씩 누적됩니다. 어떤 도구가 어떤 인자로 호출됐는지 한눈에 보이기 때문에 새 Hook을 만들 때 참고하기 좋아요.

Claude Code 자체의 --debug 옵션도 유용합니다. 이 옵션을 켜고 실행하면 Hook이 어떤 시점에 트리거됐고 exit 코드는 무엇이었는지 콘솔에 실시간으로 표시됩니다. Hook이 무시되는 것 같은데 원인을 모르겠을 때 가장 빠른 진단 수단이에요.

또 하나의 팁은 matcher 패턴 검증입니다. 정규식이 의도한 대로 매칭되는지 확신이 없을 때는, matcher를 ""로 두고 모든 호출을 받은 다음 Hook 안에서 tool_name 필드를 확인하는 방식으로 디버깅할 수 있습니다.

# matcher 디버깅용 스크립트
TOOL=$(jq -r '.tool_name')
echo "[Hook] tool_name=$TOOL" >> $HOME/.claude/matcher_debug.log
exit 0

이렇게 한 번 돌려보면 어떤 도구 이름들이 실제로 매칭 대상인지 확인할 수 있고, 그 후 정확한 matcher 패턴을 작성하면 됩니다.

8. 보안과 주의사항

Hooks는 셸 명령을 임의로 실행하는 기능이기 때문에 잘못 쓰면 보안 사고로 이어질 수 있습니다. 운영 환경에서 쓰기 전에 다음 항목들을 점검해두는 편이 안전해요.

8.1 신뢰할 수 없는 settings.json 자동 적용 금지

오픈소스 프로젝트를 클론할 때, 그 프로젝트에 포함된 .claude/settings.json이 자동으로 활성화되면 위험합니다. 누군가 악의적인 Hook을 심어둔 settings.json을 받았는데 그걸 모르고 Claude Code를 실행하면 임의 명령이 실행될 수 있거든요.

Claude Code는 신규 settings.json을 발견하면 사용자에게 승인을 요청하지만, 깃허브에서 다운로드 받은 코드를 처음 실행할 때는 settings.json 내용을 한 번 검토하고 승인하는 습관을 들이는 편이 안전합니다.

8.2 환경변수와 입력값 인용 처리

Hook 명령에 $CLAUDE_FILE_PATHS 같은 환경변수를 쓸 때는 반드시 따옴표로 감싸야 합니다. 파일명에 공백이나 특수문자가 들어 있을 때 명령이 깨질 수 있고, 더 나쁜 경우 명령 인젝션 위험도 있습니다.

권장: echo "$CLAUDE_FILE_PATHS" | xargs -r prettier --write

비권장: prettier --write $CLAUDE_FILE_PATHS

두 번째 형태는 파일명에 따옴표나 세미콜론이 들어가면 의도치 않은 명령이 실행될 수 있습니다. xargs를 쓰면 인자가 안전하게 처리됩니다.

8.3 timeout 설정

Hook에 timeout을 명시하지 않으면 기본값으로 동작하지만, 외부 API를 호출하는 Hook이라면 명시적으로 timeout을 잡아두는 편이 좋습니다. 네트워크가 끊겼을 때 Hook이 무한 대기하면 Claude Code 작업 흐름이 같이 멈추기 때문이죠.

로컬에서만 동작하는 Hook(포매터, 린터 등)은 30초 정도, 외부 API 호출은 10초 이내로 제한하는 편이 무난합니다.

8.4 git 커밋 전 점검

.claude/settings.json을 git에 커밋할 때는 비밀키나 웹훅 URL이 들어 있지 않은지 확인합니다. 한 번 커밋된 비밀값은 깃 히스토리에 영원히 남기 때문에, 사고가 나면 키 재발급까지 가야 합니다.

안전한 패턴은 settings.json에는 환경변수 참조만 적고, 실제 값은 .env나 시스템 환경변수로 관리하는 것입니다. .claude/settings.local.json은 기본적으로 gitignore되니, 개인 설정은 그쪽에 두는 것도 한 가지 방법입니다.

9. 마무리

Claude Code Hooks는 처음 보면 설정이 복잡해 보이지만, 한 번 익혀두면 반복 작업을 크게 줄여주는 기능입니다. 매번 같은 지시를 프롬프트에 적는 대신, settings.json에 한 번 정의해두면 같은 환경을 가진 모든 작업에서 자동으로 적용됩니다.

developer planning workflow desk laptop

출처: Pixabay (StartupStockPhotos)

처음 도입한다면 다음 순서를 추천합니다. 작은 것부터 시작해 점차 확장하는 방식이죠.

  1. 1단계: Stop Hook으로 종료 사운드 추가 (5분이면 됨)
  2. 2단계: PostToolUse로 자동 포매터 등록 (프로젝트별)
  3. 3단계: PreToolUse로 위험 명령 차단 (팀 표준화)
  4. 4단계: 로깅 Hook 추가 (디버깅용 hook_log.jsonl 누적)
  5. 5단계: SubagentStop, PreCompact 같은 고급 이벤트 활용

특히 팀 단위로 Claude Code를 도입했다면, 프로젝트 settings.json에 표준 Hook을 정의해두는 것을 권장합니다. 새 팀원이 들어왔을 때 별다른 설정 없이도 동일한 자동화 환경을 쓸 수 있어, 결과물 품질이 일정해지고 온보딩 시간도 줄어듭니다.

마지막으로 한 가지 강조하자면, Hook은 덧붙이는 도구라는 점입니다. 모델 자체의 행동을 통제하기보다는, 모델 작업의 전후에 자동화 레이어를 한 겹 더하는 개념이에요. 모델에게 모든 걸 시키려 하지 말고, 결정적으로 동작해야 할 부분은 Hook으로 빼두는 설계가 안정적입니다.

공식 문서는 Anthropic Claude Code 페이지에서 가장 최신 정보를 확인할 수 있고, 새 Hook 이벤트가 추가되거나 환경변수가 바뀌는 일도 종종 있으니 주기적으로 확인하면 좋습니다. 작은 Hook 하나라도 추가해두면 매일 반복하던 작은 수고가 사라지는 경험을 할 수 있을 거예요.