- Claude Codeμ λΌμ΄νμ¬μ΄ν΄ νΉμ μμ μμ μλμΌλ‘ μ€νλλ μ¬μ©μ μ μ μ
Έ λͺ
λ Ή λλ LLM ν둬ννΈ
PreToolUse, PostToolUse, Stop λ± μ΄λ²€νΈμ λ°μνμ¬ κ²μ¦, μ°¨λ¨, 컨ν
μ€νΈ μ£Όμ
μν
- 3κ°μ§ νΈλ€λ¬ νμ
μ§μ: command (μ
Έ), prompt (LLM λ¨μΌν΄), agent (λꡬ μ¬μ© κ°λ₯ν subagent)
ν΄λΉ κ°λ
μ΄ νμν μ΄μ
- μνν λͺ
λ Ή μλ μ°¨λ¨:
rm -rf κ°μ νκ΄΄μ λͺ
λ Ήμ μ¬μ μ μ°¨λ¨
- μ½λ νμ§ μλ κ²μ¦: νμΌ μμ ν μλμΌλ‘ λ¦°ν
/ν¬λ§€ν
μ€ν
- μν¬νλ‘μ° μλν: μΈμ
μμ μ νκ²½ μ€μ , μ’
λ£ μ μ 리 μμ
λ±
AS-IS (Hooks μμ΄)
sequenceDiagram
autonumber
actor User
participant Claude
participant System as νμΌ μμ€ν
User->>Claude: "μ½λ μμ ν΄μ€"
Claude->>System: Write (νμΌ μ μ₯)
Note over System: β οΈ λ¦°ν
μ λ¨<br/>ν¬λ§€ν
μ λ¨
Claude->>System: Bash "rm -rf /"
Note over System: β οΈ μνν λͺ
λ Ή<br/>μ¬μ κ²μ¦ μμ
Note over User: λ§€λ² μλμΌλ‘<br/>"λ¦°νΈ λλ €μ€"<br/>"μνν κ±° νμ§λ§" μμ²
TO-BE (Hooks μ¬μ©)
sequenceDiagram
autonumber
actor User
participant Claude
participant Hook as Hook Handler
participant System as νμΌ μμ€ν
User->>Claude: "μ½λ μμ ν΄μ€"
Claude->>Hook: PreToolUse (Bash "rm -rf")
Hook-->>Claude: β exit 2 "μ°¨λ¨λ¨"
Note over Claude: μνν λͺ
λ Ή μλ μ°¨λ¨
Claude->>System: Write (νμΌ μ μ₯)
System-->>Hook: PostToolUse (Write)
Hook->>System: μλ λ¦°ν
/ν¬λ§€ν
μ€ν
Hook-->>Claude: β
"λ¦°νΈ ν΅κ³Ό"
Hook μ΄λ²€νΈ μ 체 λͺ©λ‘
| μ΄λ²€νΈ | λ°μ μμ | μ°¨λ¨ κ°λ₯ |
|---|
SessionStart | μΈμ
μμ/μ¬κ° μ | No |
UserPromptSubmit | μ¬μ©μ ν둬ννΈ μ μΆ ν, Claude μ²λ¦¬ μ | Yes |
PreToolUse | λꡬ νΈμΆ μ€ν μ | Yes |
PermissionRequest | νΌλ―Έμ
λ€μ΄μΌλ‘κ·Έ νμ μ | Yes |
PostToolUse | λꡬ νΈμΆ μ±κ³΅ ν | No (νΌλλ°±λ§) |
PostToolUseFailure | λꡬ νΈμΆ μ€ν¨ ν | No (νΌλλ°±λ§) |
Notification | μλ¦Ό λ°μ μ | No |
SubagentStart | Subagents μμ± μ | No |
SubagentStop | Subagents μλ£ μ | Yes |
Stop | Claude μλ΅ μλ£ μ | Yes |
TaskCompleted | νμ€ν¬ μλ£ νμ μ | Yes |
PreCompact | 컨ν
μ€νΈ μμΆ μ | No |
SessionEnd | μΈμ
μ’
λ£ μ | No |
μ€μ ꡬ쑰 (3λ¨κ³ μ€μ²©)
{
"hooks": {
"PostToolUse": [ // 1. Hook μ΄λ²€νΈ μ ν
{
"matcher": "Edit|Write", // 2. Matcherλ‘ νν°λ§ (regex)
"hooks": [ // 3. νΈλ€λ¬ μ μ
{
"type": "command",
"command": "/path/to/lint.sh"
}
]
}
]
}
}
3κ°μ§ νΈλ€λ¬ νμ
| νμ
| type | μ€λͺ
| κΈ°λ³Έ timeout |
|---|
| Command | "command" | μ
Έ λͺ
λ Ή μ€ν. stdinμΌλ‘ JSON μ
λ ₯, exit codeλ‘ κ²°κ³Ό | 600μ΄ |
| Prompt | "prompt" | LLMμ ν둬ννΈ μ μ‘, {ok: true/false} μλ΅ | 30μ΄ |
| Agent | "agent" | Read/Grep/Glob λꡬ μ¬μ© κ°λ₯ν subagent μμ± | 60μ΄ |
Exit Code κ·μΉ
| Exit Code | μλ―Έ | λμ |
|---|
| 0 | μ±κ³΅ | stdoutμ JSON νμ±, λꡬ νΈμΆ νμ© |
| 2 | μ°¨λ¨ | stderrλ₯Ό Claudeμ μλ¬λ‘ μ λ¬, λꡬ νΈμΆ μ°¨λ¨ |
| κΈ°ν | λΉμ°¨λ¨ μλ¬ | stderrλ₯Ό verbose λͺ¨λμ νμ, μ€ν κ³μ |
μ€μ νμΌ μμΉ
| μμΉ | κ²½λ‘ | μ μ© λ²μ |
|---|
| Personal | ~/.claude/settings.json | λ΄ λͺ¨λ νλ‘μ νΈ |
| Project | .claude/settings.json | ν΄λΉ νλ‘μ νΈ (곡μ κ°λ₯) |
| Local | .claude/settings.local.json | ν΄λΉ νλ‘μ νΈ (gitignore) |
| Plugin | <plugin>/hooks/hooks.json | νλ¬κ·ΈμΈ νμ±νλ κ³³ |
| Claude Code Skills / Subagents | frontmatterμ μ μ | ν΄λΉ μ»΄ν¬λνΈ νμ± μ |
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "νκ΄΄μ λͺ
λ Ή μ°¨λ¨λ¨"
}
}'
else
exit 0
fi
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": ".claude/hooks/block-rm.sh"
}]
}]
}
}
PostToolUse μ€μ μμ: μλ ν¬λ§€ν
#!/bin/bash
# .claude/hooks/auto-format.sh
# stdinμμ JSON μ
λ ₯ μ½κΈ°
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# νμΌ νμ₯μμ λ°λΌ ν¬λ§€ν° μ€ν
case "$FILE_PATH" in
*.ts|*.tsx|*.js|*.jsx)
npx prettier --write "$FILE_PATH" 2>/dev/null
;;
*.py)
black "$FILE_PATH" 2>/dev/null
;;
*.dart)
dart format "$FILE_PATH" 2>/dev/null
;;
esac
exit 0
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": ".claude/hooks/block-rm.sh"
}]
}],
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": ".claude/hooks/auto-format.sh"
}]
}]
}
}
μ°Έκ³ λ¬Έμ