devAlice
← AI Agents

Claude Code hooks — 세션·도구 이벤트에 자동 동작 붙이기

settings.json hooks로 세션 시작·도구 호출 전후·종료에 셸 명령을 끼우는 패턴. 보안 차단·자동 포맷·로깅·외부 알림 5종 예시.

Claude Code의 hookssettings.json의 hooks 키에 정의해 두면 세션 시작·도구 호출 전후·세션 종료 등 이벤트에서 셸 명령을 자동 실행한다. AI가 무엇을 하기 직전에 가로채고, 직후에 보정하고, 끝날 때 정리할 수 있는 강력한 확장점이다.

이 가이드는 Claude Code 1.x / 공식 hooks 스펙 기준. Claude Code 셋업 이후 자동화·보안을 한 단계 더 굳히는 단계다.

TL;DR

Hook 이벤트발동 시점대표 활용
SessionStart세션 시작 직후메모리 동기화 메시지, env 검증
UserPromptSubmit사용자가 프롬프트 제출 직후슬래시 커맨드 인식, 로깅, 후처리 트리거
PreToolUse도구 호출 직전위험 명령 차단, 시크릿 파일 보호
PostToolUse도구 호출 직후자동 포맷(prettier), 린트, 사이드이펙트 기록
Stop세션 종료 직전변경 요약, 로그 백업, 알림

핵심 규칙:

  • exit code 2로 종료하면 도구 호출이 차단된다 (PreToolUse 시).
  • hook stdin으로 JSON이 들어옴 — jq로 파싱.
  • hook은 셸 명령이므로 OS 차이(macOS/Linux/Windows WSL) 주의.

사전 조건

  • Claude Code 1.x 설치 — Claude Code 셋업
  • jq 설치 (brew install jq / apt install jq / winget install jqlang.jq)
  • settings.json 편집 가능 (보통 ~/.claude/settings.json)

권장 베이스라인 다운로드

devAlice 권장 hooks 5종 + 권한 deny/allow 리스트 — 그대로 받아서 본인 환경에 맞춰 수정하면 된다.

settings.json
# 1. 다운로드
curl -fsSL https://devalice.jaceclub.com/assets/ai-agents/claude-code-hooks/settings.example.json -o settings.json
 
# 2. SHA-256 검증
shasum -a 256 settings.json
# 기대값: 30c1d4117a935cac03b601c71125c11b71733c8c59517ab129fd8913cfb8a69e
 
# 3. 코드 검사 (특히 PreToolUse hook들)
less settings.json
 
# 4. 적용 위치로 복사 (macOS/Linux)
mkdir -p ~/.claude
cp settings.json ~/.claude/settings.json

⚠️ 주의: hooks는 임의 셸 명령을 실행한다. 다른 사람의 hooks를 그대로 적용하기 전 반드시 코드 검사. 본 예시는 안전한 5종 기본 셋이지만 본인 환경에 맞춰 검토 후 사용.


1. hooks 구조 — 5분

~/.claude/settings.jsonhooks 키:

{
  "hooks": {
    "EventName": [
      {
        "matcher": "정규식 또는 도구 이름",  // 선택
        "hooks": [
          {
            "type": "command",
            "command": "/bin/bash -lc '실제 셸 명령'"
          }
        ]
      }
    ]
  }
}

매칭 규칙:

  • matcher 미지정 → 모든 발동에 hook 실행
  • matcher가 도구 이름 (Bash, Write, Edit, Read 등) → 해당 도구만
  • matcher가 정규식 (^/(?:ej|euijinpro)\\b) → UserPromptSubmit에서 프롬프트 텍스트 매칭

stdin으로 들어오는 JSON:

{
  "session_id": "...",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/Users/...",
  "tool_name": "Bash",
  "tool_input": { "command": "..." },
  "tool_response": { /* PostToolUse 전용 */ }
}

jq -r .tool_input.command로 추출.


2. SessionStart — 세션 컨텍스트 적재

가장 단순한 hook. 세션 진입 시 한 줄 출력:

"SessionStart": [
  {
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'printf \"[세션 시작] %s\\n메모리 동기화: 최신 (skip)\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
      }
    ]
  }
]

stdout이 시스템 메시지로 노출된다. 이를 활용:

  • 메모리 파일 자동 로드 트리거
  • 프로젝트별 컨텍스트 출력
  • 시작 시 git 상태 요약
# 더 풍부한 예시
command='/bin/bash -lc "echo [세션 시작]; git -C \"$PWD\" status -s; git -C \"$PWD\" log --oneline -3"'

3. PreToolUse — 위험 명령 차단 (가장 가치 높은 hook)

exit 2로 차단. AI가 위험한 셸 명령을 시도해도 막을 수 있다.

3.1 위험 패턴 차단

"PreToolUse": [
  {
    "matcher": "Bash",
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'cmd=$(jq -r .tool_input.command); case \"$cmd\" in *\"rm -rf /\"*|*\"sudo rm\"*|*\":(){\"*) echo \"위험 명령어 차단: $cmd\" >&2; exit 2;; esac'"
      }
    ]
  }
]

차단 패턴:

  • rm -rf / — 시스템 파괴
  • sudo rm — 광범위 삭제 (필요하면 화이트리스트로 풀어주기)
  • :(){ — fork bomb (:(){ :|: & };:)

3.2 시크릿 파일 수정 차단

{
  "matcher": "Write|Edit",
  "hooks": [
    {
      "type": "command",
      "command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in */.env|*/credentials.json|*/.aws/credentials) echo \"시크릿 파일 수정 차단: $path\" >&2; exit 2;; esac'"
    }
  ]
}

.env, credentials.json, ~/.aws/credentials 등을 AI가 실수로 변경하지 못하게.

3.3 특정 디렉토리 보호

command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); case \"\$path\" in /etc/*|/System/*) echo \"시스템 경로 차단: \$path\" >&2; exit 2;; esac"'

4. PostToolUse — 자동 포맷 / 린트 / 로깅

도구 호출 이후 보정 작업.

4.1 prettier 자동 포맷

"PostToolUse": [
  {
    "matcher": "Edit|Write|MultiEdit",
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in *.ts|*.tsx|*.js|*.jsx) prettier --write \"$path\" 2>/dev/null || true ;; esac'"
      }
    ]
  }
]

AI가 Write/Edit한 직후 prettier가 자동 정리. 매 편집마다 0.5초 정도 추가.

4.2 변경 파일 로그

command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); echo \"[changed] \$(date +%T) \$path\" >> ~/.claude/change.log"'

세션 어떤 파일이 어떤 순서로 변경됐는지 시간순 기록.

4.3 type-check / lint (느린 작업은 별도 hook 권장)

매 편집마다 tsc --noEmit은 비용이 크다. Stop hook으로 옮기는 게 낫다 (§6).


5. UserPromptSubmit — 슬래시 커맨드 감지 / 후처리 트리거

"UserPromptSubmit": [
  {
    "matcher": "^/(?:ej|euijinpro)\\b",
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'echo \"[ej skill] 프롬프트 euijin.pro 기록 대상\" >&2'"
      }
    ]
  }
]
  • 슬래시 커맨드 감지 → 외부 시스템에 트리거 (예: Slack 알림, 별도 로깅)
  • 특정 키워드 (urgent, bug)에 자동 라벨링
  • 토큰 한도 임계치 검사

6. Stop — 세션 종료 시 cleanup

"Stop": [
  {
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'printf \"\\n[세션 종료] %s\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
      }
    ]
  }
]

활용 예:

  • 변경 파일 git status 요약
  • 백업 (rsync 또는 git stash)
  • 외부 시스템에 종료 알림
  • type-check / build 1회
command='/bin/bash -lc "if git diff --quiet; then echo \"[clean]\"; else echo \"[changes]\"; git diff --stat; fi"'

7. permissions — 권한 deny / allow

hooks와 별개지만 settings.json에 함께 들어가는 가장 중요한 보안 키.

"permissions": {
  "deny": [
    "Bash(rm -rf /:*)",
    "Bash(sudo rm:*)",
    "Read(.env)",
    "Read(.env.local)",
    "Read(.aws/credentials)",
    "Read(*/credentials.json)"
  ],
  "allow": [
    "Bash(git status:*)",
    "Bash(git log:*)",
    "Bash(git diff:*)",
    "Bash(pnpm:*)",
    "Bash(npm:*)",
    "Bash(node:*)"
  ]
}
  • deny는 무조건 우선 — hooks의 차단 로직과 보완 관계
  • allow는 사용자 확인 없이 자동 승인 (자주 쓰는 안전한 명령)
  • 그 외는 사용자 매번 확인

본 가이드의 hooks는 deny와 중복 가드 — defense in depth.


8. settings.json 위치와 우선순위

위치적용 범위비고
~/.claude/settings.json사용자 전역모든 프로젝트
<project>/.claude/settings.json프로젝트프로젝트 진입 시 user 설정에 머지
<project>/.claude/settings.local.json프로젝트 + 사용자 로컬gitignored 권장

머지 순서: user → project → project.local. 뒤가 앞을 덮어쓴다.

hooks는 머지가 아닌 배열 누적 — user에 hook 5개 + project에 hook 3개 = 총 8개 모두 실행.


9. 디버깅 — hook이 안 도는 것 같을 때

9.1 stdin payload 보기

임시 hook으로 무엇이 들어오는지 확인:

{
  "matcher": "Bash",
  "hooks": [
    {
      "type": "command",
      "command": "/bin/bash -lc 'cat > /tmp/claude-hook-debug.json'"
    }
  ]
}

cat /tmp/claude-hook-debug.json | jq .

9.2 exit code 확인

command='/bin/bash -lc "echo dbg >&2; exit 2"'

→ 차단되는지 (PreToolUse) 확인.

9.3 hook 자체가 호출됐는지

command='/bin/bash -lc "echo \"[hook fired $$ $(date +%T)]\" >> ~/.claude/hook-trace.log"'

세션 후 로그 파일 확인.

9.4 JSON 문법 오류

settings.json은 매우 까다롭다 — trailing comma, escape 누락이 흔한 함정. jq . ~/.claude/settings.json으로 한 번 검증.


10. 트러블슈팅

"command not found: jq"

brew install jq (macOS) / sudo apt install jq (Linux) / winget install jqlang.jq (Windows).

hook이 발동했지만 효과 없음

  • command가 단일 라인이어야 함. 여러 라인은 \\n 또는 별도 스크립트 파일.
  • 쉘 escape 주의 — JSON 안에서 \" 두 번 escape.

PreToolUse exit 2가 무시됨

  • 실제로 exit 2가 셸 명령 마지막 줄에 도달했는지 확인.
  • set -e 환경에서 case 매칭 후 ;;로 정상 종료되어 0이 되는 함정. case 안에서 직접 exit 2.

prettier가 동작하지 않음

  • 프로젝트 root에 prettier 설치 여부 확인 (pnpm prettier --version).
  • hook의 PATH에 node_modules/.bin 포함되지 않을 수 있음 → npx prettier 또는 절대 경로.

settings.json 수정해도 반영 안 됨

  • 세션 재시작 필요. 또는 /config reload (지원 시).
  • 잘못된 JSON 문법으로 무시되고 있을 수 있음. jq . 검증.

다음 단계

참고 링크

변경 이력

  • 2026-05-12 — 초안 (devAlice M2 시드 확장)

댓글