Claude Code Hooks 진짜 활용법 — 매번 자동으로 X 시키기

Claude Code를 쓰다 보면 “매번 같은 작업을 자동으로 시키고 싶다”는 욕구가 생깁니다. 파일 저장 후 자동 lint, 위험한 명령 차단, 세션 시작 시 오늘의 컨텍스트 자동 주입 같은 것들입니다. CLAUDE.md에 적어두거나 메모리에 저장해도 클로드가 매번 그것을 따르리라는 보장이 없습니다 — 결국 클로드의 판단에 맡기는 구조이기 때문입니다. 이 자리를 정확히 채우는 기능이 Hooks입니다. 이번 글에서는 Hooks가 정확히 무엇이고, 정말 매번 도는지, 메인 세션에는 어떤 영향을 주는지, 그리고 진짜 손에 익을 만한 자동화 패턴 8가지를 정리하겠습니다.

Hooks는 어떤 기능인가

Hooks는 Claude Code 세션의 특정 이벤트(예: 사용자가 프롬프트를 제출했을 때, 도구를 실행하기 직전, 응답이 끝났을 때)에 셸 명령이나 MCP 도구 호출을 자동으로 실행하도록 등록하는 기능입니다. 공식 가이드는 Claude Code Hooks 문서Hooks Guide에서 확인할 수 있습니다.

설정 파일은 세 곳에 둘 수 있고, 우선순위가 다릅니다.

위치적용 범위특징
~/.claude/settings.json모든 프로젝트전역 — 개인 작업 환경 통일용
.claude/settings.json현재 프로젝트(공유)git에 커밋해 팀 전체에 적용
.claude/settings.local.json현재 프로젝트(나만)git ignore — 로컬 비밀 명령 등

같은 이벤트에 hook이 여러 위치에 정의돼 있으면 병합돼서 모두 실행됩니다. 한 군데서 다른 데를 덮어쓰는 구조가 아닙니다.

핵심 질문 — 정말 “매번” 도는가

이게 Hooks를 쓰는 이유의 전부입니다. 결론은 네, 매번 돕니다.

  • Skill·메모리·preference: 결국 클로드의 판단을 거칩니다. “이 상황에 이 스킬을 써야 할까?”라고 추론한 끝에 실행되거나, 까먹고 안 쓰기도 합니다.
  • Hook: 클로드의 판단을 거치지 않습니다. 등록한 이벤트가 발화하고 매처(matcher) 조건이 맞으면 결정적으로 실행됩니다. “from now on when X” 같은 자동화는 메모리·preference로는 보장되지 않고 hook이어야만 보장됩니다.

그래서 보안 차단, 자동 포매팅, 컨텍스트 자동 주입처럼 “한 번이라도 빠지면 의미가 없는” 작업은 hook이 정답입니다.

이벤트와 메인 세션 영향

이벤트가 많지만 실전에서 자주 쓰는 핵심 이벤트와 그 영향만 정리하면 다음과 같습니다.

이벤트발화 시점메인 컨텍스트 영향
SessionStart세션 시작·재개·clear 직후stdout이 컨텍스트에 주입됨
UserPromptSubmit사용자가 프롬프트 제출stdout이 추가 컨텍스트로 주입, exit 2면 프롬프트 차단
PreToolUse도구 실행 직전exit 2 또는 decision: deny로 도구 차단
PostToolUse도구 실행 직후일반적으로 컨텍스트 영향 없음
Notification승인 대기·idle 대기 등 알림없음 — 외부 알림용
Stop응답 완료없음 — 정리·알림용
PreCompact컨텍스트 자동 압축 직전차단 가능
SessionEnd세션 종료없음 — 로그·정리용

핵심 패턴 두 가지를 기억하면 됩니다. 첫째, SessionStart·UserPromptSubmit hook의 stdout은 메인 대화에 주입됩니다. 두 이벤트가 컨텍스트 자동 주입의 통로입니다. 둘째, PreToolUse는 exit code 2(또는 JSON deny 응답)로 도구 실행을 막을 수 있습니다. 위험 명령 차단·정책 강제의 통로입니다.

설정 스키마 한 번에 보기

실제 settings.json에 hook을 정의하는 형태는 다음과 같습니다.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/Users/me/.claude/hooks/check-dangerous-cmd.sh",
            "timeout": 30
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo \"현재 시각: $(date '+%Y-%m-%d %H:%M:%S %Z')\""
          }
        ]
      }
    ]
  }
}

matcher는 도구 이름이나 정규식이고, 생략하면 모든 도구에 매칭됩니다. type은 일반 셸 명령을 위한 command 외에 mcp_tool(MCP 서버 도구를 직접 호출), http, prompt 등이 추가됐습니다. timeout 기본값은 비교적 길게(수백 초 단위) 잡혀 있어 Hook이 과하게 오래 돌면 에러 로그만 남고 통과합니다.

진짜 손에 익는 자동화 패턴 8가지

1. 매 프롬프트마다 현재 시각 자동 주입 (UserPromptSubmit)

날짜·요일에 민감한 작업(예: 블로그 발행, 일정 관리, 시한이 있는 정책)에서 매번 “오늘 며칠이지?”를 묻지 않아도 되도록 합니다.

2. 위험한 셸 명령 차단 (PreToolUse + Bash)

rm -rf, git push --force, DROP TABLE 같은 패턴이 도구 인자에 들어 있으면 hook 스크립트가 exit 2로 응답해 실행 자체를 막습니다. 클로드의 판단에만 기대지 않고 실수의 마지막 안전망을 깔 수 있습니다.

3. 파일 저장 후 자동 포매팅 / lint (PostToolUse + Edit·Write)

파일 경로를 입력으로 받아 prettier, ruff, gofmt 등을 자동 실행합니다. 클로드가 “포매터를 돌려야 하는지” 매번 판단하지 않아도 항상 같은 결과물이 나옵니다.

4. Secret 키·환경변수 유출 차단 (PreToolUse + Write/Bash)

커밋·파일 작성 직전, 변경 내용에 OPENAI_API_KEY, AWS_SECRET 같은 패턴이 보이면 차단합니다. “.env가 실수로 같이 커밋되는 사고”를 클로드 모델 변경과 무관하게 막아줍니다.

5. 세션 시작 시 작업 컨텍스트 주입 (SessionStart)

오늘 git status, 진행 중인 PR 목록, TODO 메모, 최근 에러 로그 같은 것을 stdout으로 출력하면 세션 시작과 동시에 클로드가 그 정보를 안고 시작합니다. “어디까지 했더라”부터 시작하는 시간이 사라집니다.

6. 응답 완료 시 데스크톱 알림 (Stop 또는 Notification)

긴 작업 후 자리를 비웠다가 와서 끝났는지 확인하던 시간을 없앱니다. macOS의 osascript나 Linux의 notify-send로 가벼운 알림을 띄우면 됩니다. 자리 비운 사이 끝나면 즉시 알 수 있습니다.

7. 디렉터리 이동 시 환경 자동 동기화 (CwdChanged)

프로젝트 간 점프할 때 direnv나 자체 스크립트로 환경변수를 갈아끼웁니다. 매 세션 수동으로 source하던 번거로움이 사라집니다.

8. 자동 압축 차단 또는 사전 백업 (PreCompact)

긴 세션이 자동 압축으로 핵심 컨텍스트를 잃기 직전, hook으로 현재 상태를 별도 파일에 백업하거나 아예 압축을 차단합니다. “방금 분석한 내용이 압축으로 사라졌다”는 사고를 막을 수 있습니다.

주의사항과 함정

  • 무한 루프: Stop hook 안에서 새 프롬프트를 트리거하면 다시 Stop이 호출돼 끝없이 돕니다. Stop hook은 페이로드의 stop_hook_active 플래그를 확인해 자기 자신이 트리거한 호출이면 빠져나가야 합니다.
  • 보안: settings.json에 등록된 hook은 임의 셸 명령을 실행할 수 있습니다. 신뢰할 수 없는 저장소를 클론한 직후 Claude Code 세션을 그 디렉터리에서 시작하면 위험합니다. 새 프로젝트 세션 시작 전에 .claude/settings.json·.claude/settings.local.json의 hook을 한 번 들여다보는 습관이 필요합니다.
  • 타임아웃과 성능: hook이 느리면 매 프롬프트·매 도구 호출마다 그만큼 지연됩니다. 자주 발화되는 이벤트(UserPromptSubmit, PreToolUse)에는 가벼운 명령만 두고, 무거운 작업은 비동기 처리가 가능한 이벤트(PostToolUse, Stop)나 외부 큐로 미루는 편이 안전합니다.
  • 권한 우회 불가: PreToolUse hook이 “allow”로 응답해도 settings의 deny 규칙은 그대로 적용됩니다. hook으로 보안 정책을 약화할 수는 없습니다.

마무리

Hooks는 다른 자동화 수단과 본질이 다릅니다. /loop·/btw·/ultrareview가 “메인 세션을 보호하면서 일을 다른 곳에 보내는” 도구라면, Hooks는 “클로드의 판단과 무관하게 결정적으로 실행되는 자동화“입니다. 한 번이라도 빠지면 의미가 없는 자동화(보안 차단, 매번 같은 컨텍스트 주입, 항상 같은 후처리)는 메모리나 CLAUDE.md가 아니라 Hook으로 가야 합니다. 자주 쓰는 패턴 한두 개를 settings.json에 박아두는 것만으로 작업 흐름의 결이 달라집니다.

자주 묻는 질문

Q. Hook을 잘못 짜서 매 프롬프트가 막히면 어떻게 풀 수 있나요?
Claude Code 세션을 일단 종료한 뒤, 텍스트 에디터로 settings.json을 열어 문제가 되는 hook을 주석 처리하거나 삭제한 다음 다시 세션을 여시면 됩니다. --no-hooks처럼 hook을 비활성화한 채 시작할 수 있는 옵션이 제공되는 경우도 있으니 공식 문서에서 본인 버전의 옵션을 확인하시기 바랍니다.

Q. CLAUDE.md에 “X를 자동으로 해줘”라고 적어둔 것과 hook은 결과가 같은가요?
다릅니다. CLAUDE.md는 클로드가 “참고”하는 지침이라 모델 버전·컨텍스트에 따라 따르거나 빠뜨릴 수 있습니다. hook은 클로드가 인지하든 말든 이벤트가 발화하면 항상 실행됩니다. “반드시 매번”이 필요하면 hook으로 가야 합니다.

Q. hook이 등록돼 있는지 어떻게 한 번에 확인하나요?
settings.json 파일을 직접 열어 hooks 섹션을 확인하는 것이 가장 정확합니다. 프로젝트 단위와 전역 단위가 분리돼 있으므로 두 파일을 모두 확인하시기 바랍니다. .claude/settings.local.json은 보통 git에서 제외돼 있어 같은 저장소에서도 사람마다 hook 구성이 다를 수 있다는 점도 함께 기억해두면 좋습니다.