DaleSchool

Hooks로 워크플로우 자동화

중급20분

학습 목표

  • Hooks의 주요 이벤트 타입을 이해한다
  • PreToolUse Hook으로 위험한 작업을 차단할 수 있다
  • PostToolUse Hook으로 자동 후처리를 구성할 수 있다

동작하는 코드

프로젝트의 .claude/settings.json에 Hook을 추가해봅시다. main 브랜치에서 파일 수정을 차단하는 Hook입니다:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "[ \"$(git branch --show-current)\" != \"main\" ] || { echo '{\"block\":true,\"message\":\"main 브랜치에서는 직접 수정할 수 없습니다. feature 브랜치를 만드세요.\"}' >&2; exit 2; }",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

이제 main 브랜치에서 Claude에게 파일 수정을 요청해보세요:

> README.md에 한 줄 추가해줘

Hook이 작동하여 수정이 차단됩니다. 다른 브랜치로 전환하면 정상적으로 수정됩니다:

> git checkout -b feature/test로 브랜치를 만들고, README.md를 수정해줘

직접 수정하기

이번에는 PostToolUse Hook으로 파일 저장 후 자동 린트를 실행해봅시다:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx eslint --fix $CLAUDE_FILE_PATHS 2>/dev/null || true",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Claude가 파일을 수정할 때마다 ESLint가 자동으로 실행되어 스타일을 교정합니다.

"왜?" — 반복되는 규칙을 코드로 강제하기

CLAUDE.md에 "main 브랜치에서 작업하지 마세요"라고 적어도 Claude가 무시할 수 있습니다. Hook은 이런 규칙을 코드로 강제합니다. 요청이 아니라 차단입니다.

주요 Hook 이벤트

| 이벤트 | 시점 | 용도 | | ------------------ | -------------------- | -------------------------------- | | PreToolUse | 도구 실행 전 | 위험 작업 차단, 조건부 승인 | | PostToolUse | 도구 실행 후 | 자동 포매팅, 린트, 로깅 | | Notification | 알림 발생 시 | 커스텀 알림 (Slack, 데스크톱 등) | | Stop | 응답 완료 시 | 결과 검증, 정리 작업 | | UserPromptSubmit | 사용자 입력 시 | 입력 전처리 | | SubagentStop | 서브에이전트 완료 시 | 서브에이전트 결과 후처리 | | PreCompact | 컨텍스트 압축 전 | 압축 전 데이터 보존 | | SessionStart | 세션 시작 시 | 환경 검증, 의존성 체크 | | SessionEnd | 세션 종료 시 | 정리, 로깅 |

이 중 가장 자주 쓰이는 건 PreToolUse, PostToolUse, Notification입니다.

Hook 설정 구조

{
  "hooks": {
    "이벤트이름": [
      {
        "matcher": "도구이름 패턴 (정규식)",
        "hooks": [
          {
            "type": "command",
            "command": "실행할 셸 명령어",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

matcher 패턴

matcher는 어떤 도구에 Hook을 적용할지 필터링합니다:

| 패턴 | 매칭 대상 | | ------------- | ------------------------- | | Edit\|Write | 파일 수정/생성 도구 | | Bash | 셸 명령어 실행 | | Read | 파일 읽기 | | .* | 모든 도구 (주의해서 사용) |

종료 코드로 동작 결정

Hook의 종료 코드가 동작을 결정합니다:

| 종료 코드 | 동작 | | --------- | --------------------------- | | 0 | 성공 — 작업 계속 진행 | | 2 | 차단 — 도구 실행을 막음 | | 그 외 | 오류로 처리 |

PreToolUse에서 exit 2를 반환하면 작업이 차단됩니다:

exit 2

stderr로 메시지를 전달하면 Claude에게 차단 이유를 알려줄 수 있습니다:

echo "main 브랜치에서는 수정할 수 없습니다." >&2
exit 2

심화 학습

Hook에서 사용할 수 있는 환경 변수는?

Hook 명령어 안에서 다음 환경 변수를 사용할 수 있습니다:

  • $CLAUDE_FILE_PATHS — 현재 도구 호출에 관련된 파일 경로들 (공백 구분)
  • $CLAUDE_PROJECT_DIR — 프로젝트 루트 디렉토리의 절대 경로

더 상세한 정보가 필요하면 stdin으로 전달되는 JSON을 jq로 파싱할 수 있습니다:

jq -r '.tool_input.command' >> ~/.claude/bash-log.txt
  1. .claude/settings.json에 main 브랜치 보호 Hook을 추가하고 테스트해보세요.
  2. PostToolUse Hook으로 파일 수정 후 자동으로 prettier --write를 실행하는 Hook을 만들어보세요.
  3. SessionStart Hook으로 세션 시작 시 "현재 브랜치: {브랜치명}" 메시지를 출력하는 Hook을 만들어보세요.

Q1. Claude Code가 파일을 수정하기 전에 실행되는 Hook 이벤트는?

  • A) PostToolUse
  • B) SessionStart
  • C) PreToolUse
  • D) Stop

참고 자료