"๋งค๋ฒ ๊ฐ์ ์ผ์ ๋ฐ๋ณตํ๋ ๊ฒ ์ง๊ฒน๋ค๊ณ ? ํด๋ก๋์๊ฒ ๋งก๊ฒจ๋ฒ๋ ค!"
์๋ ํ์ธ์, ์ง์น ๊ฐ๋ฐ์ ์ฌ๋ฌ๋ถ! ๐ซ ๋ ์ค๋๋ ์ฝ๋ ์ง๊ณ , ํ ์คํธ ๋๋ฆฌ๊ณ , ํฌ๋งทํ ํ๊ณ , ๋ฐฑ์ ํ๊ณ ... ๋์๋ ๋ฐ๋ณต์ ๋ช์์ ํ์ฐ์ ๊ฑฐ๋ฆฌ๊ณ ๊ณ์ ๊ฐ์?
"์, ์ด๋ฐ ๊ท์ฐฎ์ ์ผ๋ค์ ํด๋ก๋๊ฐ ์์์ ํด์คฌ์ผ๋ฉด..."
๊ทธ๋ฐ ์ฌ๋ฌ๋ถ์ ์ํ ๊ฒ์ ์ฒด์ธ์ ๊ฐ ์์ต๋๋ค! ๋ฐ๋ก Claude Code Hooks์ ๋๋ค. ์ด ๋ง๋ฒ ๊ฐ์ ๊ธฐ๋ฅ์ผ๋ก ํด๋ก๋๋ฅผ ์ฌ๋ฌ๋ถ๋ง์ ์๋ฒฝํ ๊ฐ๋ฐ ์ด์์คํดํธ๋ก ๋ณ์ ์์ผ ๋ณด์ธ์.
๋ง์น ์ถฉ์คํ ๋ฐ๋ ค๊ฒฌ์ ํ๋ จ์ํค๋ฏ, ํด๋ก๋์๊ฒ "ํ์ผ ์ ์ฅํ๋ฉด ์๋์ผ๋ก ํฌ๋งทํ ํด!", "์ํํ ๋ช ๋ น์ด๋ ์ ๋ ์คํํ์ง ๋ง!", "์์ ๋๋๋ฉด ์๋ฆผ ๋ณด๋ด์ค!" ๊ฐ์ ํน๋ณํ ์ง์๋ฅผ ๋ด๋ฆด ์ ์์ต๋๋ค.
์ค๋์ ์ฌ๋ฌ๋ถ์ ๊ฐ๋ฐ ์์ฐ์ฑ์ 10๋ฐฐ ํฅ์์ํฌ Claude Code Hooks์ ๋ชจ๋ ๊ฒ์ ํํค์ณ๋ณด๊ฒ ์ต๋๋ค! ๐ฏ
Hook์ ์์ด๋ก '๋์๋ฐ๋'์ด๋ '๊ฐ๊ณ ๋ฆฌ'๋ฅผ ์๋ฏธํฉ๋๋ค. ์ด๋ฆ ๊ทธ๋๋ก, ํ๋ก๊ทธ๋จ์ ํน์ ๋์์ด ์ผ์ด๋๋ ์๊ฐ์ ๋์์ฑ์(Hooking), ์ฐ๋ฆฌ๊ฐ ์ํ๋ ํน๋ณํ ๋ช ๋ น์ ์คํํ๋ ๊ธฐ์ ์ ๋๋ค.
ํด๋ก๋์ ์์ ํ๋ฆ์ ๊ฐ๋ฌผ์ด๋ผ๊ณ ์์ํด ๋ณด์ธ์. ์ฐ๋ฆฌ๋ ์ด ๊ฐ๋ฌผ์ ๋์๋ฐ๋์ ๋์ ธ ๋์ ์ ์์ต๋๋ค.
PreToolUse
๋ผ๋ ๋์๋ฐ๋: ํด๋ก๋๊ฐ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ง์ ์ด๋ผ๋ ๋ฌผ๊ณ ๊ธฐ๋ฅผ ๋์์ฑ๋๋ค.PostToolUse
๋ผ๋ ๋์๋ฐ๋: ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์งํ๋ผ๋ ๋ฌผ๊ณ ๊ธฐ๋ฅผ ๋์์ฑ๋๋ค.Stop
์ด๋ผ๋ ๋์๋ฐ๋: ํด๋ก๋์ ๋ชจ๋ ์์
์ด ๋๋๋ ์๊ฐ์ ๋์์ฑ๋๋ค.์ด๋ ๊ฒ ๋์์ฑ ์๊ฐ์, ์ฐ๋ฆฌ๋ ๋ฏธ๋ฆฌ ์ค๋นํด ๋ ๋ช ๋ น์ด(๋ฏธ๋ผ)๋ฅผ ์คํ์ํค๋ ๊ฑฐ์ฃ . ์ด๊ฒ ๋ฐ๋ก Hooks์ ์ ๋ถ์ ๋๋ค! ๊ฐ๋จํ์ฃ ?
"ํ๋กฌํํธ์ ๋งค๋ฒ ์ง์ํ ์ผ์, ์ฝ๋๋ก ์๋ํํ์ฌ ์ค์๋ฅผ ์์ ๊ณ ์์ฐ์ฑ์ ๊ทน๋ํํ๊ธฐ ์ํด!"
๋งค๋ฒ // ์ด ์ฝ๋ ๋ค ์ง๋ฉด prettier๋ก ํฌ๋งทํ
ํด์ค
๋ผ๊ณ ์ง์ํ๋ ๋์ , ํ์ผ์ด ์ ์ฅ๋ ๋๋ง๋ค ์ธ์ ๋, ํ์คํ๊ฒ ํฌ๋งทํ
์ ์คํํ๋ Hook์ ๋ง๋๋ ๊ฒ์ด ํจ์ฌ ๋ ๋๋ํ ๋ฐฉ๋ฒ์
๋๋ค.
์ด๋ก ์ ๊ทธ๋ง! ์ง์ ๋ง๋ค์ด๋ณด์ฃ . ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ด๋ฉด์๋ ์ ์ฉํ Hook๋ถํฐ ์์ํด๋ณด๊ฒ ์ต๋๋ค.
ํด๋ก๋๊ฐ ls
, git add
, npm install
๊ฐ์ ๋ช
๋ น์ด๋ฅผ ์คํํ ๋๋ง๋ค ์๋์ผ๋ก ๋ก๊ทธ ํ์ผ์ ๊ธฐ๋กํด๋๋ฉด, ๋์ค์ "ํด๋ก๋๊ฐ ๋๋์ฒด ๋ญ ํ์ง?"๋ผ๊ณ ๊ถ๊ธํ ๋ ์ฝ๊ฒ ํ์ธํ ์ ์์ต๋๋ค.
/hooks
ํด๋ก๋ ์ฑํ ์ฐฝ์ ์ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ๋ฉด Hook ์ค์ ํ๋ฉด์ด ์ด๋ฆฝ๋๋ค.
PreToolUse
๋ฅผ ์ ํํฉ๋๋ค. ๋ช
๋ น์ด๋ฅผ ์คํํ๊ธฐ ์ ์ ๊ธฐ๋กํ๊ณ ์ถ์ผ๋๊น์.
+ Add new matcher...
๋ฒํผ์ ๋๋ฅด๊ณ Bash
๋ผ๊ณ ์
๋ ฅํฉ๋๋ค. Bash๋ ํด๋ก๋๊ฐ ํฐ๋ฏธ๋ ๋ช
๋ น์ด๋ฅผ ์คํํ ๋ ์ฌ์ฉํ๋ ๋๊ตฌ์
๋๋ค.
์ด์ ํต์ฌ์ ๋๋ค! ์๋ ๋ช ๋ น์ด๋ฅผ ๊ทธ๋๋ก ๋ณต์ฌํด์ ๋ถ์ฌ๋ฃ์ผ์ธ์.
jq -r '"["$(date -Iseconds)"] \(.tool_input.command) - \(.tool_input.description // "์ค๋ช
์์")"' >> ~/.claude/bash-command-log.txt
์ด ๋ง๋ฒ์ ํ ์ค์ด ํ๋ ์ผ:
jq
: JSON ๋ฐ์ดํฐ๋ฅผ ์์๊ฒ ๊ฐ๊ณตํ๋ ๋๊ตฌdate -Iseconds
: ํ์ฌ ์๊ฐ์ ISO ํ์์ผ๋ก ๊ฐ์ ธ์ค๊ธฐtool_input.command
: ํด๋ก๋๊ฐ ์คํํ๋ ค๋ ๋ช
๋ น์ดtool_input.description
: ํด๋ก๋๊ฐ ์ ์ ์ค๋ช
>> ~/.claude/bash-command-log.txt
: ๋ชจ๋ ๋ด์ฉ์ ๋ก๊ทธ ํ์ผ์ ์ถ๊ฐUser settings
์ ์ ์ฅํ๊ณ ESC
๋ก ๋์จ ๋ค, ํด๋ก๋์๊ฒ ๊ฐ๋จํ ๋ช
๋ น์ด๋ฅผ ์์ฒญํด๋ณด์ธ์.
ํ์ฌ ๋๋ ํ ๋ฆฌ์ ํ์ผ ๋ชฉ๋ก์ ๋ณด์ฌ์ค
๊ทธ๋ฌ๋ฉด ~/.claude/bash-command-log.txt
ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ก์ด ๋จ์ ๊ฑฐ์์:
[2025-07-01T15:03:49Z] ls -la - ํ์ฌ ๋๋ ํ ๋ฆฌ์ ํ์ผ ๋ชฉ๋ก์ ๋ณด์ฌ์ค
์ถํํฉ๋๋ค! ๐ ์ฌ๋ฌ๋ถ์ ์ฒซ ๋ฒ์งธ Hook์ด ์์ฑ๋์์ต๋๋ค!
์ด์ ์ง์ง ์ฌ๋ฏธ์๋ ์๊ฐ์ ๋๋ค. ์ค์ ๊ฐ๋ฐ์์ ๋ฐ๋ก ์จ๋จน์ ์ ์๋ ์ ์ฉํ Hook ๋ ์ํผ๋ค์ ์๊ฐํด๋๋ฆฌ๊ฒ ์ต๋๋ค.
๊ณ ๋ฏผ: "์ฝ๋ ๋ค ์ง ๋ค์์ prettier, black ๊ฐ์ ํฌ๋งทํฐ ๋๋ฆฌ๋ ๊ฑธ ์๊พธ ๊น๋นกํ๋ค..."
ํด๊ฒฐ์ฑ : ํ์ผ ์ ์ฅํ ๋๋ง๋ค ์๋์ผ๋ก ๊ฐ ์ธ์ด์ ๋ง๋ ํฌ๋งทํฐ๋ฅผ ์คํํ๋ Hook!
#!/bin/bash
# ํ์ผ: ~/.claude/hooks/auto-format.sh
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
tool_name=$(echo "$input" | jq -r '.tool_name')
# ํ์ผ ์ฐ๊ธฐ/์์ ๋๊ตฌ๊ฐ ์๋๋ฉด ์ข
๋ฃ
if [[ ! "$tool_name" =~ ^(Write|Edit|MultiEdit)$ ]] || [[ -z "$file_path" ]]; then
exit 0
fi
# ํ์ผ ํ์ฅ์๋ณ๋ก ๋ค๋ฅธ ํฌ๋งทํฐ ์คํ
case "$file_path" in
*.py)
if command -v black >/dev/null; then
black "$file_path"
echo "โจ Python ์ฝ๋ ์ ๋ฆฌ ์๋ฃ: $(basename "$file_path")"
fi
;;
*.js|*.jsx|*.ts|*.tsx|*.json)
if command -v prettier >/dev/null; then
prettier --write "$file_path"
echo "โจ JavaScript ์ฝ๋ ์ ๋ฆฌ ์๋ฃ: $(basename "$file_path")"
fi
;;
*.go)
if command -v gofmt >/dev/null; then
gofmt -w "$file_path"
echo "โจ Go ์ฝ๋ ์ ๋ฆฌ ์๋ฃ: $(basename "$file_path")"
fi
;;
esac
์ค์ ๋ฐฉ๋ฒ:
PostToolUse
Write|Edit|MultiEdit
/home/user/.claude/hooks/auto-format.sh
๊ณ ๋ฏผ: "์ค์ํ ํ์ผ ์์ ํ๋ค๊ฐ ์ค์๋ก ๋ ๋ ค๋จน์ผ๋ฉด ์ด๋กํ์ง?"
ํด๊ฒฐ์ฑ : ํ์ผ ์์ ํ ๋๋ง๋ค ์๋์ผ๋ก ๋ฐฑ์ ํ๊ณ Git์ ์ปค๋ฐ๊น์ง!
#!/usr/bin/env python3
# ํ์ผ: ~/.claude/hooks/auto-backup-commit.py
import json
import sys
import os
import subprocess
from datetime import datetime
from pathlib import Path
def main():
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError:
sys.exit(1)
tool_name = input_data.get("tool_name", "")
file_path = input_data.get("tool_input", {}).get("file_path")
# ํ์ผ ์์ ๋๊ตฌ์ด๊ณ ํ์ผ์ด ์กด์ฌํ ๋๋ง ์คํ
if tool_name not in ["Write", "Edit", "MultiEdit"] or not file_path or not os.path.exists(file_path):
sys.exit(0)
# 1. ๋ฐฑ์
์์ฑ
backup_dir = Path.home() / ".claude" / "backups" / datetime.now().strftime("%Y-%m-%d")
backup_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%H%M%S")
backup_file = backup_dir / f"{Path(file_path).name}.{timestamp}.bak"
subprocess.run(["cp", file_path, str(backup_file)])
print(f"๐พ ๋ฐฑ์
์๋ฃ: {backup_file}")
# 2. Git ์ปค๋ฐ (Git ์ ์ฅ์์ธ ๊ฒฝ์ฐ์๋ง)
if os.path.exists(".git"):
try:
# ๋ณ๊ฒฝ์ฌํญ์ด ์๋์ง ํ์ธ
result = subprocess.run(["git", "diff", "--quiet", "HEAD", "--", file_path],
capture_output=True)
if result.returncode != 0: # ๋ณ๊ฒฝ์ฌํญ ์์
subprocess.run(["git", "add", file_path])
commit_msg = f"[Auto] {Path(file_path).name} ์์ "
subprocess.run(["git", "commit", "-m", commit_msg])
print(f"๐ Git ์ปค๋ฐ ์๋ฃ: {commit_msg}")
except Exception as e:
print(f"Git ์ปค๋ฐ ์คํจ: {e}", file=sys.stderr)
if __name__ == "__main__":
main()
๊ณ ๋ฏผ: "ํด๋ก๋๊ฐ ์ ๋ ฅ ๊ธฐ๋ค๋ฆด ๋ ์๋ฆผ์ด ๋๋ฌด ์ฌ์ฌํ๋ค..."
ํด๊ฒฐ์ฑ : ์ด์์ฒด์ ๋ณ๋ก ๋ค๋ฅธ ์๋ฆผ์ ๋ณด๋ด๋ ๋๋ํ Hook!
#!/usr/bin/env python3
# ํ์ผ: ~/.claude/hooks/smart-notification.py
import json
import sys
import subprocess
import platform
def send_notification():
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError:
sys.exit(1)
message = input_data.get("message", "ํด๋ก๋๊ฐ ๋น์ ์ ์ฐพ๊ณ ์์ด์! ๐ญ")
title = input_data.get("title", "Claude Code")
system = platform.system().lower()
try:
if system == "darwin": # macOS
subprocess.run([
"osascript", "-e",
f'display notification "{message}" with title "{title}" sound name "Glass"'
])
elif system == "windows": # Windows
ps_command = f'''
Add-Type -AssemblyName System.Windows.Forms;
$notification = New-Object System.Windows.Forms.NotifyIcon;
$notification.Icon = [System.Drawing.SystemIcons]::Information;
$notification.BalloonTipTitle = "{title}";
$notification.BalloonTipText = "{message}";
$notification.Visible = $true;
$notification.ShowBalloonTip(5000);
Start-Sleep -Seconds 1;
$notification.Dispose();
'''
subprocess.run(["powershell", "-Command", ps_command])
else: # Linux
subprocess.run(["notify-send", title, message])
print(f"๐ ์๋ฆผ ์ ์ก ์ฑ๊ณต: {message}")
except Exception as e:
print(f"์๋ฆผ ์ ์ก ์คํจ: {e}", file=sys.stderr)
if __name__ == "__main__":
send_notification()
๊ณ ๋ฏผ: "์ค์๋ก rm -rf
๋ sudo
๊ฐ์ ์ํํ ๋ช
๋ น์ด๋ฅผ ์คํํ๋ฉด ์ด๋กํ์ง?"
ํด๊ฒฐ์ฑ : ์ํํ ๋ช ๋ น์ด ํจํด์ ๋ฏธ๋ฆฌ ์ฐจ๋จํ๋ ๋ณด์ Hook!
#!/usr/bin/env python3
# ํ์ผ: ~/.claude/hooks/security-guard.py
import json
import sys
import re
# ์ํํ ๋ช
๋ น์ด ํจํด๋ค
DANGEROUS_PATTERNS = [
(r"\brm\s+(-rf?|--recursive)", "ํด๋ ๊ฐ์ ์ญ์ ๋ ์ํํ์ฌ ์ฐจ๋จ๋์์ต๋๋ค."),
(r"\bsudo\b", "๊ด๋ฆฌ์ ๊ถํ ์คํ์ ํ์ฉ๋์ง ์์ต๋๋ค."),
(r"\bchmod\s+(777|666)", "๋ชจ๋ ์ฌ์ฉ์์๊ฒ ๊ถํ์ ์ฃผ๋ ๊ฒ์ ์ํํฉ๋๋ค."),
(r"\bcurl\b.*\|.*(bash|sh)", "์ธํฐ๋ท ์คํฌ๋ฆฝํธ ์ง์ ์คํ์ ๋ณด์ ์ํ์ด ์์ต๋๋ค."),
]
def check_dangerous_command():
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError:
sys.exit(1)
if input_data.get("tool_name") != "Bash":
sys.exit(0)
command = input_data.get("tool_input", {}).get("command", "")
if not command:
sys.exit(0)
# ์ํํ ํจํด ๊ฒ์ฌ
for pattern, message in DANGEROUS_PATTERNS:
if re.search(pattern, command, re.IGNORECASE):
print(f"๐ซ ๋ณด์ ๊ฒฝ๊ณ : {message}", file=sys.stderr)
print(f"์ฐจ๋จ๋ ๋ช
๋ น์ด: {command}", file=sys.stderr)
sys.exit(2) # ์์
์ค๋จ
print("โ
๋ช
๋ น์ด ์์ ์ฑ ๊ฒ์ฌ ํต๊ณผ")
if __name__ == "__main__":
check_dangerous_command()
๊ฐ Hook ์ด๋ฒคํธ๊ฐ ์ธ์ ์คํ๋๋์ง ์ ํํ ์์์ผ ์ ์ ํ Hook์ ๋ง๋ค ์ ์์ต๋๋ค.
์คํ ์์ : ํด๋ก๋๊ฐ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ง์
์ฃผ์ ์ฉ๋:
๋๋ต ๋ฐฉ๋ฒ:
2
: ์์
์ค๋จ{"decision": "block", "reason": "์ด์ "}
์คํ ์์ : ํด๋ก๋๊ฐ ๋๊ตฌ ์ฌ์ฉ์ ์ฑ๊ณต์ ์ผ๋ก ๋ง์น ์งํ
์ฃผ์ ์ฉ๋:
์คํ ์์ : ํด๋ก๋๊ฐ ์ฌ์ฉ์ ์ ๋ ฅ์ ๊ธฐ๋ค๋ฆฌ๊ฑฐ๋ ์๋ฆผ์ ๋ณด๋ผ ๋
์ฃผ์ ์ฉ๋:
์คํ ์์ : ํด๋ก๋์ ๋ชจ๋ ์์ ์ด ์๋ฃ๋์ด ๋ํ๊ฐ ๋๋๋ ค๋ ์๊ฐ
์ฃผ์ ์ฉ๋:
โ ๏ธ ์ฃผ์์ฌํญ: ์๋ชป ์ฌ์ฉํ๋ฉด ๋ฌดํ ๋ฃจํ์ ๋น ์ง ์ ์์ผ๋ stop_hook_active
์ฒดํฌ ํ์!
MCP๋ ํด๋ก๋๊ฐ ์ธ๋ถ ๋ฐ์ดํฐ๋ฒ ์ด์ค, ํ์ผ ์์คํ , API ๋ฑ๊ณผ ์ฐ๊ฒฐํ๋ ํ์ค ํ๋กํ ์ฝ์ ๋๋ค.
# MCP ์๋ฒ ์ถ๊ฐ
claude mcp add-json filesystem '{
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/safe/directory"],
"tool_configuration": {
"allowed_tools": ["read_file", "write_file"]
}
}'
MCP Hook ์์ :
{
"hooks": {
"PreToolUse": [{
"matcher": "mcp__.*",
"hooks": [{
"type": "command",
"command": "echo 'MCP ๋๊ตฌ ์ฌ์ฉ ๊ฐ์ง' >> ~/.claude/mcp-log.txt"
}]
}]
}
}
Computer Use๋ ํด๋ก๋๊ฐ ์ค์ ๋ก ํ๋ฉด์ ๋ณด๊ณ ๋ง์ฐ์ค/ํค๋ณด๋๋ฅผ ์กฐ์ํ๋ ๊ธฐ๋ฅ์ ๋๋ค.
์ฃผ์ ์ก์ ๋ค:
screenshot
: ํ๋ฉด ์บก์ฒleft_click
: ๋ง์ฐ์ค ํด๋ฆญtype
: ํ
์คํธ ์
๋ ฅkey
: ํค๋ณด๋ ๋จ์ถํคscroll
: ์คํฌ๋กคleft_click_drag
: ๋๋๊ทธ์ธ๋ถ ๋๊ตฌ๋ค๊ณผ์ ์ฐ๋์ ๊ด๋ฆฌํ๋ Hook ์์คํ ์ ๋๋ค.
def weather_tool_hook(location):
# Pre-hook: ์
๋ ฅ ๊ฒ์ฆ
if not validate_location(location):
return {"error": "Invalid location"}
# Execution: API ํธ์ถ
weather_data = fetch_weather(location)
# Post-hook: ์๋ต ํฌ๋งทํ
return format_weather_response(weather_data)
์, ์ด์ ๊ฐ์ฅ ์ค์ํ ์๊ฐ์ ๋๋ค. Hooks๋ ์ฌ๋ฌ๋ถ์ ์ปดํจํฐ์ ๋ํ ๋ชจ๋ ๊ถํ์ ๊ฐ์ง๋๋ค. ์ฆ, ๊ฐ๋ ฅํ ๋งํผ ์ํํ ์ ์๋ค๋ ๋ป์ด์ฃ . ์๋ 5๊ฐ์ง ๋ณด์ ํฉ๊ธ๋ฅ ์ ๋ง์์ ์๊ธฐ๊ณ , ์์ ํ๊ฒ Hooks๋ฅผ ์ฌ์ฉํ์ธ์.
"$MY_VAR"
์ฒ๋ผ ํฐ๋ฐ์ดํ๋ก ๊ฐ์ธ์ฃผ์ธ์. ํ์ผ ์ด๋ฆ์ ๊ณต๋ฐฑ์ด ์๊ฑฐ๋ ํ ๋ ๋ฐ์ํ ์ ์๋ ์๋ง์ ์์์น ๋ชปํ ๋ฌธ์ ๋ฅผ ๋ง์์ฃผ๋ ์ต๊ณ ์ ๋ฐฉ์ด๋ง์
๋๋ค..key
), ํ๊ฒฝ๋ณ์ ํ์ผ(.env
), ๋น๋ฐ๋ฒํธ๊ฐ ์ ํ ํ์ผ ๋ฑ ๋ฏผ๊ฐํ ์ ๋ณด๋ Hook์ด ์ ๋ ์ ๊ทผํ์ง ๋ชปํ๋๋ก ๋ง์์ผ ํฉ๋๋ค. ํน์ ํ์ผ์ด๋ ํด๋ ์ ๊ทผ์ ๋ง๋ Hook์ ๊ฐ์ฅ ๋จผ์ ๋ง๋ค์ด ๋๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค.rm -rf
(๊ฐ์ ์ญ์ ), sudo
(๊ด๋ฆฌ์ ๊ถํ ์คํ), chmod 777
(๋ชจ๋์๊ฒ ๋ชจ๋ ๊ถํ ๋ถ์ฌ) ๊ฐ์ ๋ช
๋ น์ด๋ ์์คํ
์ ํ๊ดดํ ์ ์๋ ๊ฐ๋ ฅํ ํ์ ๊ฐ์ก์ต๋๋ค. ์ด๋ฐ ๋ช
๋ น์ด๋ค์ ์์ ์คํ๋ ์ ์๋๋ก ์ฐจ๋จํ๋ Hook์ ๋ง๋ค์ด ๋๋ฉด ์์ฌํ ์ ์์ต๋๋ค.์ค๋ ์ฐ๋ฆฌ๋ Claude Code Hooks์ ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ ์ค์ ํ์ฉ๋ฒ, ๊ทธ๋ฆฌ๊ณ ๊ฐ์ฅ ์ค์ํ ๋ณด์ ๊ท์น๊น์ง ํจ๊ป ์์๋ดค์ต๋๋ค.
Hooks๋ ๋จ์ํ ๋ฐ๋ณต ์์ ์ ์ค์ฌ์ฃผ๋ ๊ฒ์ ๋์ด, ์ฌ๋ฌ๋ถ์ ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ์ํฌํ๋ก์ฐ ์์ฒด๋ฅผ ์ฌ๋ฌ๋ถ์ ์คํ์ผ์ ๋ง๊ฒ ์ฌ์ฐฝ์กฐํ ์ ์๋ ๋ฌดํํ ๊ฐ๋ฅ์ฑ์ ๋๊ตฌ์ ๋๋ค.
์ด์ ์ฌ๋ฌ๋ถ์ ์ฐจ๋ก์ ๋๋ค. ์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก, ์ฌ๋ฌ๋ถ์ ๊ฐ๋ฐ ์ํ์ ๋์ฑ ์ค๋งํธํ๊ณ ํธ๋ฆฌํ๊ฒ ๋ง๋ค์ด ์ค ์์ ๋ง์ Hook์ ๋ง๋ค์ด ๋ณด์ธ์. ํด๋ก๋๋ผ๋ ๊ฐ๋ ฅํ ๋น์๋ฅผ ์ฌ๋ฌ๋ถ์ ์คํ์ผ์ ๋ง๊ฒ ๊ธธ๋ค์ด๋ ์ฌ๋ฏธ, ์ค๋๋ถํฐ ์์ํด ๋ณด์ธ์!
Happy Hacking!
jq
: JSON ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ์ค์์ค ์๋ฏธ ๋์ดํprettier
: JavaScript/TypeScript ์ฝ๋ ํฌ๋งทํฐblack
: Python ์ฝ๋ ํฌ๋งทํฐgofmt
: Go ์ธ์ด ์ฝ๋ ํฌ๋งทํฐnotify-send
: Linux ๋ฐ์คํฌํฑ ์๋ฆผ ๋๊ตฌ์ฉ์ด | ์ฌ์ด ์ค๋ช |
---|---|
Hook | ํน์ ์ํฉ์์ ์๋์ผ๋ก ์คํ๋๋ ๋ช ๋ น์ด (๋์ ๋ฐ๋์ฒ๋ผ ํน์ ์์ ์ "๋์์ฑ๋" ๊ธฐ๋ฅ) |
PreToolUse | ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ์ ์คํ๋๋ hook |
PostToolUse | ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ํ์ ์คํ๋๋ hook |
Matcher | ์ด๋ค ๋๊ตฌ์ hook์ ์ ์ฉํ ์ง ์ ํ๋ ํจํด (์ ๊ทํํ์ ์ง์) |
Shell Command | ์ปดํจํฐ์๊ฒ ์ง์ ๋ช ๋ น์ ๋ด๋ฆฌ๋ ํ ์คํธ |
JSON | ์ปดํจํฐ๋ค์ด ์๋ก ์ ๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋ ์ฌ์ฉํ๋ ํ์ (์ ๋ฆฌ์ ๋๋ ๋ฉ๋ชจ์ฅ ๊ฐ์ ๊ฒ) |
Exit Code | ํ๋ก๊ทธ๋จ์ด ๋๋ ๋ ๋ณด๋ด๋ ์ซ์ ์ ํธ (0=์ฑ๊ณต, 2=์ค๋จํด์ฃผ์ธ์) |
MCP | ํด๋ก๋๊ฐ ์ธ๋ถ ํ๋ก๊ทธ๋จ๋ค๊ณผ ๋ํํ ์ ์๊ฒ ํด์ฃผ๋ ๋ฒ์ญ๊ธฐ |
Computer Use | ํด๋ก๋๊ฐ ๋ง์ฐ์ค์ ํค๋ณด๋๋ฅผ ์ง์ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ |
Tool Use | ํด๋ก๋๊ฐ ๋ค์ํ ๋๊ตฌ๋ค์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ |
Regex | ๋ณต์กํ ๊ธ์ ํจํด์ ์ฐพ์ ๋ ์ฌ์ฉํ๋ ํน๋ณํ ๊ท์น |
Transcript | ํด๋ก๋์์ ๋ํ ๋ด์ฉ์ด ๋ชจ๋ ๊ธฐ๋ก๋๋ ์ผ๊ธฐ์ฅ |
Q: Hook์ด ์คํ ์ ๋ผ์!
A: /hooks
๋ฉ๋ด์์ ์ค์ ์ ํ์ธํ๊ณ , Ctrl-R
๋ก transcript๋ฅผ ํ์ธํด๋ณด์ธ์. ๊ทธ๋ฆฌ๊ณ ์คํฌ๋ฆฝํธ ํ์ผ์ ์คํ ๊ถํ(chmod +x
)๋ ํ์ธํด๋ณด์ธ์.
Q: Hook์ด ๋๋ฌด ์์ฃผ ์คํ๋ผ์ annoyingํด์.
A: Matcher๋ฅผ ๋ ๊ตฌ์ฒด์ ์ผ๋ก ์ค์ ํ๊ฑฐ๋, Hook ๋ด๋ถ์ ์กฐ๊ฑด๋ฌธ์ ์ถ๊ฐํด์ ํน์ ์ํฉ์๋ง ์คํ๋๋๋ก ๋ง๋ค์ด๋ณด์ธ์.
Q: Hook์์ ์๋ฌ๊ฐ ๋๋๋ฐ ์ด๋ป๊ฒ ๋๋ฒ๊น
ํ์ฃ ?
A: Hook ์คํฌ๋ฆฝํธ ๋งจ ์์ echo "Hook ์์: $(date)" >> /tmp/hook-debug.log
๊ฐ์ ๋ก๊ทธ๋ฅผ ์ถ๊ฐํด์ ์คํ ์ฌ๋ถ๋ฅผ ํ์ธํ์ธ์.
Q: ๋ค๋ฅธ ์ฌ๋์ด ๋ง๋ Hook์ ์ฐ๊ณ ์ถ์ด์.
A: ์ ๋ ์ดํดํ์ง ๋ชปํ ์ฝ๋๋ ์คํํ์ง ๋ง์ธ์! ํ ์ค ํ ์ค ์ฝ์ด๋ณด๊ณ , ๋ฌด์์ ํ๋์ง ์์ ํ ์ดํดํ ๋ค์์ ์ฌ์ฉํ์ธ์.
Q: Hook์ด ๋ด ์ปดํจํฐ๋ฅผ ๋ง๊ฐ๋จ๋ฆด ์ ์๋์?
A: ๋ค, ๊ฐ๋ฅํฉ๋๋ค. ๊ทธ๋์ ๋ณด์ ํฉ๊ธ๋ฅ ์ ๊ผญ ์ง์ผ์ผ ํฉ๋๋ค. ํนํ rm
, sudo
, chmod
๊ฐ์ ๋ช
๋ น์ด๋ ์กฐ์ฌํ์ธ์.
์ด ๋ธ๋ก๊ทธ ํฌ์คํธ๊ฐ ๋์์ด ๋์ จ๋์? ๋๊ธ๋ก ์ฌ๋ฌ๋ถ๋ง์ ์ฐฝ์์ ์ธ Hook ์์ด๋์ด๋ฅผ ๊ณต์ ํด์ฃผ์ธ์! ํจ๊ป ๋ ์ค๋งํธํ ๊ฐ๋ฐ ํ๊ฒฝ์ ๋ง๋ค์ด ๋๊ฐ์! ๐