라이프사이클 훅 (Hooks)
Claude Code 이벤트에 자동 실행되는 셸 스크립트 시스템입니다. (.ai-core v2.1.0 기준)
6개 Hook 종류
| Hook | 트리거 | 실행 방식 | 주요 기능 |
|---|---|---|---|
| UserPromptSubmit | 사용자 메시지 제출 | 동기 | 환경 검증, 컨텍스트 주입, 미확인 리포트 체크 |
| PreToolUse | 도구(Write/Edit) 실행 전 | 동기 | 위험 파일 경고, 자동 백업 |
| PostToolUse | 도구(Write/Edit) 실행 후 | 동기 | 보안 검사, 테스트 체크, 문서 정합성 |
| PreCompact | 컨텍스트 압축 전 | 동기 | 진행 중 work 파일/특허 문서 감지, 상태 저장 |
| PostCompact | 컨텍스트 압축 후 | 동기 | 저장된 상태 복원 안내, Claude에게 재주입 |
| Stop | 세션 종료 | 동기+백그라운드 | 세션 요약, Git 기록, handoff 자동 저장 |
bootstrap.sh 흐름
모든 hooks의 진입점입니다. settings.json에서 절대 경로로 호출됩니다.
Claude Code 이벤트 발생
↓
settings.json → hook 정의
↓
bash $HOME/.../bootstrap.sh {hook-name}
↓
bootstrap.sh → PROJECT_ROOT 결정 → exec hooks/{hook-name}.sh
↓
개별 hook 실행
settings.json 등록 예시
{
"hooks": {
"UserPromptSubmit": [{
"command": "bash $HOME/.../bootstrap.sh user-prompt-submit"
}],
"Stop": [{
"command": "bash $HOME/.../bootstrap.sh stop"
}]
}
}
PROJECT_ROOT 결정 순서
1. SCRIPT_DIR (bootstrap.sh 위치) → 2단계 상위 디렉토리
2. config/detail.json → paths.project_root (있으면 사용)
3. fallback: 추론값 사용
개별 Hook 경로 변수
각 hook 파일 상단에서 공통적으로 설정합니다.
SCRIPT_DIR="$(dirname "$0")" # .claude/hooks/
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" # multi-saas-kit/
AI_CORE_DIR="$PROJECT_ROOT/.ai-core" # .ai-core/
DATA_AI_DIR="$PROJECT_ROOT/.ai-core/data" # .ai-core/data/
CONFIG_DIR="$AI_CORE_DIR/config" # .ai-core/config/
stop.sh 포그라운드/백그라운드 구조
stop.sh는 다른 hooks와 달리 2단계로 실행됩니다. 포그라운드에서 필수 작업을 마친 뒤, 시간이 오래 걸리는 작업은 백그라운드(disown)로 넘깁니다.
[포그라운드] (Claude Code가 대기)
├── /-skip-all 감지 → 즉시 종료
├── stdin에서 JSON 입력 읽기 (transcript_path, session_id)
├── Git 요약 출력
├── AI Memory 제안
├── Handoff 자동 저장 (auto_save_handoff)
└── 백그라운드 태스크 시작
↓
[백그라운드] (disown, Claude Code 대기 없음)
├── Transcript 추출
├── 프로젝트 로그 동기화
├── 세션 요약 생성 (LLM 폴백 체인)
├── RAG 임베딩
├── 컨텍스트 아카이브
└── 체크리스트 검증
함수 스코핑 주의
run_heavy_tasks_background() 호출은 모든 함수 정의가 완료된 후에 실행됩니다. 함수를 정의하기 전에 백그라운드로 넘기면 함수를 찾지 못하는 스코핑 문제가 발생합니다. (v2.0.0에서 수정됨)
PostToolUse lib 모듈 구조
post-tool-use.sh는 진입점(약 100줄)이며, 실제 기능은 lib/ 폴더의 모듈로 분리되어 있습니다.
post-tool-use.sh (진입점)
├── source lib/config-loader.sh ← 설정 읽기
├── source lib/session-manager.sh ← 세션 ID 감지, 폴더 생성
├── source lib/security-scanner.sh ← SQL Injection, XSS, 시크릿, 디버그 코드
├── source lib/extended-checks.sh ← 포트 검증, 문서 계층 검사
├── source lib/semgrep-auto.sh ← Semgrep 자동 실행 (백그라운드)
├── source lib/test-check.sh ← 테스트 파일 존재 확인
├── source lib/doc-consistency.sh ← 문서 정합성 (특허 용어/참조)
├── source lib/deep-security.sh ← 심층 보안 (Critical 시 서브에이전트 안내)
├── source lib/transcript-sync.sh ← Transcript 동기화, RAG, 체크리스트
└── source lib/check-reporter.sh ← 결과 리포트 + 실행 상태 배너
검사 모듈 on/off 제어
global.json의 post_tool_use_checks 섹션에서 각 모듈을 제어합니다.
| 모듈 | config 키 | 대상 | 기본값 |
|---|---|---|---|
| Semgrep 자동 실행 | semgrep_auto | 코드 파일 (.php, .js 등) | OFF |
| 테스트 존재 확인 | test_check | 코드 파일 (.php, .py, .go) | OFF |
| 문서 정합성 | doc_consistency | md 파일 (특허 문서 특화) | OFF |
| 심층 보안 분석 | deep_security | Critical 이슈 발견 시 | OFF |
실행 상태 배너
매 Write/Edit 후 검사 현황을 테이블로 표시합니다.
┌──────────────────────────────────────────────┐
│ PostToolUse 검사 현황 │
│ semgrep │ SKIP │ config: disabled │
│ test-check │ PASS │ │
│ doc-check │ SKIP │ 비대상 파일 │
│ deep-sec │ SKIP │ config: disabled │
└──────────────────┴───────┴────────────────────┘
set +e 규칙
모든 hooks는 set +e로 실행합니다. 내부 명령 실패가 hook 전체를 중단하지 않습니다.
# 올바른 패턴
set +e
# 사용 금지 (단일 명령 실패로 전체 hook 중단)
set -e
왜 set -e를 쓰면 안 되는가
Claude Code는 hook exit code로 성공/실패를 판단합니다.
- exit 0: 성공 (stdout 출력이 Claude 컨텍스트에 주입)
- exit != 0: 실패 ("Failed with non-blocking status code" 표시)
set -e를 사용하면 jq 파싱 실패 같은 사소한 오류가 전체 hook을 중단시켜, 보안 검사나 컨텍스트 주입이 누락됩니다.
stdout vs stderr
| 출력 | 대상 |
|---|---|
| stdout | Claude 컨텍스트에 주입됨 (Claude가 읽음) |
| stderr | 터미널에만 표시 (디버그용) |
파일 구조
.claude/hooks/
├── bootstrap.sh ← 모든 hooks 진입점
├── user-prompt-submit.sh ← UserPromptSubmit
├── pre-tool-use.sh ← PreToolUse
├── post-tool-use.sh ← PostToolUse (진입점 → lib/ 로드)
├── pre-compact.sh ← PreCompact
├── post-compact.sh ← PostCompact
├── stop.sh ← Stop (포그라운드+백그라운드)
├── lib/ ← PostToolUse 모듈
│ ├── config-loader.sh
│ ├── session-manager.sh
│ ├── security-scanner.sh
│ ├── extended-checks.sh
│ ├── semgrep-auto.sh
│ ├── test-check.sh
│ ├── doc-consistency.sh
│ ├── deep-security.sh
│ ├── transcript-sync.sh
│ └── check-reporter.sh
└── prompt-context/ ← UserPromptSubmit 주입 컨텍스트
├── 001-environment.md
├── 002-project-context.md
├── 003-checklist-status.md
└── 004-multi-llm-opinions.md