Hook 시스템 개요
Hook은 Claude Code의 특정 이벤트에 자동으로 실행되는 스크립트입니다. 코드 생성 전후, 파일 저장 시점 등에 자동화된 작업을 수행할 수 있습니다.
yaml
# Claude Code 설정 파일 (~/.claude/config.yaml)
hooks:
# 코드 생성 전 실행
pre-generation:
command: "./scripts/prepare.sh"
enabled: true
# 코드 생성 후 실행
post-generation:
command: "./scripts/format.sh"
enabled: true
# Git 커밋 전 실행
pre-commit:
command: "npm run lint && npm test"
enabled: true
# 사용자 입력 제출 시 실행
user-prompt-submit:
command: "./scripts/validate.sh"
enabled: false사용 가능한 Hook 타입:
- pre-generation: 코드 생성 전 실행 (환경 준비, 설정 검증)
- post-generation: 코드 생성 후 실행 (포매팅, 린트, 테스트)
- pre-commit: Git 커밋 전 실행 (코드 품질 검사)
- user-prompt-submit: 사용자 입력 시 실행 (입력 검증, 전처리)
bash
# Hook 스크립트 예제 (scripts/format.sh)
#!/bin/bash
echo "🎨 코드 포매팅 시작..."
# Prettier로 포매팅
npx prettier --write "src/**/*.{ts,tsx,js,jsx}"
# ESLint 자동 수정
npx eslint --fix "src/**/*.{ts,tsx,js,jsx}"
echo "✅ 포매팅 완료!"Hook 실행 흐름: Hook이 0이 아닌 exit code를 반환하면 작업이 중단됩니다. 이를 활용하여 코드 품질 기준을 강제할 수 있습니다.
주의: Hook 스크립트는 실행 권한이 필요합니다.
chmod +x scripts/format.sh로 권한을 부여하세요.Pre-commit Hook
Git 커밋 전에 자동으로 실행되는 Hook입니다. 코드 품질 검사, 테스트 실행, 보안 스캔 등을 자동화하여 문제가 있는 코드가 커밋되는 것을 방지합니다.
yaml
# .claude/config.yaml
hooks:
pre-commit:
command: "./scripts/pre-commit.sh"
enabled: true
timeout: 300000 # 5분 (밀리초)
# 또는 직접 명령어 실행
pre-commit:
command: "npm run lint && npm test"
enabled: truebash
#!/bin/bash
# scripts/pre-commit.sh - 종합적인 pre-commit 검사
set -e # 에러 시 즉시 중단
echo "🔍 Pre-commit 검사 시작..."
# 1. TypeScript 타입 체크
echo "📘 TypeScript 타입 체크..."
npm run type-check
# 2. ESLint 검사
echo "🔎 ESLint 검사..."
npm run lint
# 3. 테스트 실행
echo "🧪 테스트 실행..."
npm test -- --coverage --coverageThreshold='{"global":{"lines":80}}'
# 4. 보안 취약점 스캔
echo "🔒 보안 스캔..."
npm audit --audit-level=moderate
# 5. 민감한 정보 검사
echo "🚨 민감 정보 검사..."
if git diff --cached | grep -E "(API_KEY|PASSWORD|SECRET|TOKEN)\s*="; then
echo "❌ 민감한 정보가 포함되어 있습니다!"
exit 1
fi
echo "✅ 모든 검사 통과!"- 자동 품질 검사: 커밋 전 자동으로 린트, 테스트 실행
- 문제 조기 발견: 코드 리뷰 전에 기본적인 문제 차단
- 팀 표준 강제: 모든 팀원에게 동일한 품질 기준 적용
- 보안 강화: API 키, 비밀번호 등 민감 정보 커밋 방지
bash
# 빠른 검사만 수행하는 경량 버전
#!/bin/bash
# scripts/pre-commit-fast.sh
# 변경된 파일만 검사
CHANGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.(ts|tsx|js|jsx)$')
if [ -z "$CHANGED_FILES" ]; then
echo "✅ 검사할 파일 없음"
exit 0
fi
# 변경된 파일만 린트
echo "$CHANGED_FILES" | xargs npx eslint
# 영향받는 테스트만 실행
npx jest --findRelatedTests $CHANGED_FILES --bail
echo "✅ 검사 완료!"성능 최적화: 전체 테스트 대신 변경된 파일과 관련된 테스트만 실행하면 Hook 실행 시간을 크게 단축할 수 있습니다.
Post-generation Hook
Claude Code가 코드를 생성한 직후 자동으로 실행되는 Hook입니다. 생성된 코드의 포매팅, 최적화, 자동 테스트 등을 수행합니다.
yaml
# .claude/config.yaml
hooks:
post-generation:
command: "./scripts/post-gen.sh"
enabled: true
env:
NODE_ENV: "development"
AUTO_FORMAT: "true"bash
#!/bin/bash
# scripts/post-gen.sh - 코드 생성 후 자동 처리
echo "🔧 Post-generation 처리 시작..."
# 1. 코드 포매팅
echo "🎨 Prettier 포매팅..."
npx prettier --write "src/**/*.{ts,tsx,js,jsx,json,css,md}"
# 2. Import 정리
echo "📦 Import 구문 정리..."
npx organize-imports-cli "src/**/*.{ts,tsx}"
# 3. ESLint 자동 수정
echo "🔧 ESLint 자동 수정..."
npx eslint --fix "src/**/*.{ts,tsx,js,jsx}"
# 4. 생성된 컴포넌트 스토리북 자동 생성
echo "📖 Storybook 스토리 생성..."
node scripts/generate-stories.js
# 5. 빠른 검증 테스트
echo "✅ 생성된 코드 검증..."
npm run type-check
echo "🎉 Post-generation 완료!"- 자동 포매팅: 생성된 코드를 프로젝트 스타일에 맞게 자동 정리
- Import 최적화: 불필요한 import 제거, 순서 정렬
- 즉각적인 피드백: 생성된 코드의 문제를 바로 발견
- 추가 파일 생성: 테스트, 문서, 스토리북 등 자동 생성
javascript
// scripts/generate-stories.js - 컴포넌트 스토리북 자동 생성
const fs = require('fs');
const path = require('path');
const glob = require('glob');
// 새로 생성된 컴포넌트 찾기
const components = glob.sync('src/components/**/*.tsx', {
ignore: ['**/*.test.tsx', '**/*.stories.tsx']
});
components.forEach(componentPath => {
const storyPath = componentPath.replace('.tsx', '.stories.tsx');
// 이미 스토리가 있으면 스킵
if (fs.existsSync(storyPath)) return;
const componentName = path.basename(componentPath, '.tsx');
const storyContent = `import type { Meta, StoryObj } from '@storybook/react';
import { ${componentName} } from './${componentName}';
const meta: Meta<typeof ${componentName}> = {
title: 'Components/${componentName}',
component: ${componentName},
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof ${componentName}>;
export const Default: Story = {
args: {},
};
`;
fs.writeFileSync(storyPath, storyContent);
console.log(`✅ Created story: ${storyPath}`);
});환경 변수 활용: Hook에서
CLAUDE_GENERATED_FILES환경 변수를 통해 생성된 파일 목록을 받을 수 있습니다.성능 주의: Post-generation Hook은 매 생성마다 실행되므로 무거운 작업은 피하고 빠른 처리에 집중하세요.
커스텀 Hook 작성
프로젝트 요구사항에 맞는 커스텀 Hook을 작성할 수 있습니다. 환경 변수, exit code, 표준 출력을 활용하여 Claude Code와 통신합니다.
bash
#!/bin/bash
# scripts/custom-hook.sh - 커스텀 Hook 템플릿
# 환경 변수 읽기
HOOK_TYPE="${CLAUDE_HOOK_TYPE}" # hook 타입
PROJECT_ROOT="${CLAUDE_PROJECT_ROOT}" # 프로젝트 루트
GENERATED_FILES="${CLAUDE_GENERATED_FILES}" # 생성된 파일들
echo "🎯 Hook Type: $HOOK_TYPE"
echo "📁 Project: $PROJECT_ROOT"
# 작업 수행
if [ "$HOOK_TYPE" = "post-generation" ]; then
# 생성된 파일들 처리
for file in $GENERATED_FILES; do
echo "Processing: $file"
# 커스텀 로직 실행
done
fi
# 성공: exit 0
# 실패: exit 1 (작업 중단)
exit 0javascript
// scripts/custom-hook.js - Node.js로 작성한 Hook
#!/usr/bin/env node
const { execSync } = require('child_process');
const fs = require('fs');
// 환경 변수
const hookType = process.env.CLAUDE_HOOK_TYPE;
const projectRoot = process.env.CLAUDE_PROJECT_ROOT;
const generatedFiles = (process.env.CLAUDE_GENERATED_FILES || '').split(',');
console.log(`🚀 Custom Hook: ${hookType}`);
try {
if (hookType === 'post-generation') {
// 1. TypeScript 파일 검증
const tsFiles = generatedFiles.filter(f => f.endsWith('.ts') || f.endsWith('.tsx'));
if (tsFiles.length > 0) {
console.log('📘 TypeScript 검증...');
execSync('npx tsc --noEmit', { stdio: 'inherit' });
}
// 2. 번들 사이즈 체크
console.log('📦 번들 사이즈 분석...');
const stats = execSync('npx webpack --json').toString();
const bundle = JSON.parse(stats);
if (bundle.assets[0].size > 1024 * 1024) { // 1MB
console.error('❌ 번들 사이즈가 너무 큽니다!');
process.exit(1);
}
// 3. 접근성 검사 (a11y)
console.log('♿ 접근성 검사...');
execSync('npx axe src/components --tags wcag2a', { stdio: 'inherit' });
}
console.log('✅ 커스텀 Hook 완료!');
process.exit(0);
} catch (error) {
console.error('❌ Hook 실패:', error.message);
process.exit(1);
}- 다양한 언어: Bash, Node.js, Python 등 모든 실행 가능한 스크립트
- 환경 변수: Claude Code가 제공하는 컨텍스트 정보 활용
- Exit Code: 0은 성공, 1은 실패로 작업 흐름 제어
- 표준 출력: 사용자에게 진행 상황 실시간 피드백
python
#!/usr/bin/env python3
# scripts/custom-hook.py - Python으로 작성한 Hook
import os
import sys
import subprocess
hook_type = os.getenv('CLAUDE_HOOK_TYPE')
project_root = os.getenv('CLAUDE_PROJECT_ROOT')
generated_files = os.getenv('CLAUDE_GENERATED_FILES', '').split(',')
print(f"🐍 Python Hook: {hook_type}")
try:
if hook_type == 'pre-commit':
# 1. Black 포매터
print("🎨 Black 포매팅...")
subprocess.run(['black', '.'], check=True)
# 2. mypy 타입 체크
print("📘 mypy 타입 체크...")
subprocess.run(['mypy', 'src'], check=True)
# 3. pytest 실행
print("🧪 pytest 실행...")
subprocess.run(['pytest', '--cov=src', '--cov-report=term-missing'], check=True)
print("✅ Python Hook 완료!")
sys.exit(0)
except subprocess.CalledProcessError as e:
print(f"❌ Hook 실패: {e}")
sys.exit(1)Hook 디버깅:
CLAUDE_HOOK_DEBUG=true 환경 변수를 설정하면 Hook 실행 시 상세한 디버그 정보를 볼 수 있습니다.Hook 활용 예제
실제 프로젝트에서 사용할 수 있는 다양한 Hook 예제를 소개합니다. 각 예제는 실무에서 바로 활용 가능합니다.
bash
# 예제 1: 자동 의존성 설치
# scripts/auto-install-deps.sh
#!/bin/bash
echo "📦 의존성 체크..."
# package.json 변경 확인
if git diff --cached --name-only | grep -q "package.json"; then
echo "package.json이 변경되었습니다. 의존성 설치 중..."
npm install
# package-lock.json도 커밋에 포함
git add package-lock.json
fi
# 새로운 import 감지 및 자동 설치
NEW_IMPORTS=$(git diff --cached | grep -E "^\+.*from ['"]" | grep -v node_modules || true)
if [ -n "$NEW_IMPORTS" ]; then
echo "새로운 import 감지. 누락된 패키지 설치 중..."
npx install-missing-deps
fibash
# 예제 2: 자동 버전 업데이트 및 체인지로그
# scripts/version-bump.sh
#!/bin/bash
echo "📝 버전 관리..."
# 커밋 메시지에서 버전 타입 추출
COMMIT_MSG=$(git log -1 --pretty=%B)
if echo "$COMMIT_MSG" | grep -q "BREAKING CHANGE"; then
VERSION_TYPE="major"
elif echo "$COMMIT_MSG" | grep -q "^feat"; then
VERSION_TYPE="minor"
elif echo "$COMMIT_MSG" | grep -q "^fix"; then
VERSION_TYPE="patch"
else
exit 0 # 버전 업데이트 불필요
fi
echo "🔢 ${VERSION_TYPE} 버전 업데이트..."
# 버전 업데이트
npm version $VERSION_TYPE --no-git-tag-version
# 체인지로그 자동 생성
npx conventional-changelog -p angular -i CHANGELOG.md -s
# 변경사항 커밋
git add package.json CHANGELOG.md
git commit --amend --no-edit- 의존성 자동화: package.json 변경 시 자동 설치
- 버전 관리: 커밋 메시지 기반 자동 버전 업데이트
- 문서 생성: API 문서, 체인지로그 자동 갱신
- 알림 통합: Slack, Discord 등으로 빌드 결과 전송
bash
# 예제 3: API 문서 자동 생성 및 배포
# scripts/update-docs.sh
#!/bin/bash
echo "📚 API 문서 생성..."
# TypeScript에서 API 문서 생성
npx typedoc --out docs/api src/
# OpenAPI 스펙 생성
npx swagger-jsdoc -d swagger-config.js src/**/*.ts -o docs/openapi.json
# 문서 사이트 빌드
cd docs && npm run build
# GitHub Pages에 자동 배포 (선택사항)
if [ "$AUTO_DEPLOY_DOCS" = "true" ]; then
echo "🚀 문서 배포..."
npx gh-pages -d docs/build
fibash
# 예제 4: Slack 알림 통합
# scripts/notify-slack.sh
#!/bin/bash
SLACK_WEBHOOK="$SLACK_WEBHOOK_URL"
COMMIT_MSG=$(git log -1 --pretty=%B)
AUTHOR=$(git log -1 --pretty=%an)
# 빌드 결과 전송
if [ $? -eq 0 ]; then
STATUS="✅ 성공"
COLOR="good"
else
STATUS="❌ 실패"
COLOR="danger"
fi
curl -X POST $SLACK_WEBHOOK -H 'Content-Type: application/json' -d "{
"attachments": [{
"color": "$COLOR",
"title": "빌드 $STATUS",
"text": "$COMMIT_MSG",
"fields": [
{"title": "작성자", "value": "$AUTHOR", "short": true},
{"title": "브랜치", "value": "$(git branch --show-current)", "short": true}
]
}]
}"Hook 조합: 여러 Hook을 조합하여 강력한 자동화 워크플로우를 구성할 수 있습니다. Pre-commit에서 검증, Post-generation에서 최적화, 그리고 배포 전 문서 갱신 등을 연계할 수 있습니다.
Hook 베스트 프랙티스
효과적이고 안정적인 Hook을 작성하기 위한 모범 사례입니다. 성능, 안정성, 유지보수성을 고려한 가이드라인을 제공합니다.
bash
#!/bin/bash
# ✅ 좋은 Hook 예제 - 베스트 프랙티스 적용
set -e # 에러 시 즉시 중단
set -u # 정의되지 않은 변수 사용 시 에러
# 1. 명확한 출력 메시지
echo "🔍 Pre-commit Hook 시작..."
# 2. 타임아웃 설정 (무한 대기 방지)
timeout 60s npm run lint || {
echo "❌ Lint 타임아웃 (60초 초과)"
exit 1
}
# 3. 실패 시 유용한 에러 메시지
if ! npm test; then
echo ""
echo "❌ 테스트 실패!"
echo "💡 다음을 시도해보세요:"
echo " - npm test -- --verbose (상세 로그)"
echo " - npm test -- --watch (특정 테스트만)"
exit 1
fi
# 4. 조건부 실행 (불필요한 작업 스킵)
if git diff --cached --name-only | grep -q '\.tsx\?$'; then
echo "📘 TypeScript 파일 검사..."
npm run type-check
else
echo "⏭️ TypeScript 파일 없음, 타입 체크 스킵"
fi
echo "✅ 모든 검사 통과!"- 빠른 실행: 전체 검사보다 변경된 부분만 검증하여 시간 단축
- 명확한 피드백: 진행 상황과 실패 이유를 명확하게 표시
- 에러 처리: 타임아웃, 재시도, 대체 로직으로 안정성 확보
- 조건부 실행: 필요한 경우에만 실행하여 효율성 증대
bash
# ❌ 피해야 할 안티패턴
# 1. 출력 없이 묵묵히 실행 (사용자 혼란)
npm test > /dev/null 2>&1
# 2. 무한 대기 가능성
npm install # 타임아웃 없음
# 3. 모호한 에러 메시지
if ! npm run build; then
echo "에러 발생" # 무엇이 문제인지 불명확
exit 1
fi
# 4. 매번 전체 테스트 실행 (느림)
npm test # 변경되지 않은 부분도 테스트
# 5. 에러 무시
npm run lint || true # 실패해도 계속 진행bash
# ✅ 성능 최적화 패턴
#!/bin/bash
# 1. 캐싱 활용
CACHE_DIR=".cache/hooks"
mkdir -p "$CACHE_DIR"
# 이전 결과 캐시 확인
CURRENT_HASH=$(git diff --cached | md5)
if [ -f "$CACHE_DIR/last-check" ] && [ "$(cat $CACHE_DIR/last-check)" = "$CURRENT_HASH" ]; then
echo "✅ 캐시된 결과 사용 (변경사항 없음)"
exit 0
fi
# 2. 병렬 실행
echo "🚀 병렬 검사 시작..."
npm run lint &
PID1=$!
npm run type-check &
PID2=$!
npm run test:unit &
PID3=$!
# 모든 작업 대기
wait $PID1 || exit 1
wait $PID2 || exit 1
wait $PID3 || exit 1
# 3. 결과 캐싱
echo "$CURRENT_HASH" > "$CACHE_DIR/last-check"
echo "✅ 모든 검사 완료!"yaml
# Hook 설정 모범 사례
# .claude/config.yaml
hooks:
pre-commit:
command: "./scripts/pre-commit.sh"
enabled: true
timeout: 120000 # 2분
env:
NODE_ENV: "test"
CI: "true" # CI 모드로 실행
post-generation:
command: "./scripts/post-gen.sh"
enabled: true
timeout: 30000 # 30초 (빠르게)
# 개발 중에는 비활성화할 수 있는 무거운 Hook
security-scan:
command: "./scripts/security-check.sh"
enabled: false # 필요시 활성화
timeout: 300000 # 5분개발 환경 고려: Hook이 너무 엄격하면 개발 속도가 느려집니다. 로컬에서는 빠른 검사만 하고, CI에서 전체 검증을 수행하는 것을 권장합니다.
SKIP_HOOKS=true 환경 변수로 임시 비활성화 옵션을 제공하세요.보안 주의: Hook 스크립트에 민감한 정보(API 키, 비밀번호)를 직접 포함하지 마세요. 환경 변수나 시크릿 관리 도구를 사용하세요.