Alert
์ด ๊ธ์ Claude Code์ ๋์์ ๋ฐ์ ์์ฑ๋์์ต๋๋ค
TL;DR
Git Hook์ ์ปค๋ฐ, ํธ์, ๋จธ์ง ๋ฑ Git ๋์์ ํน์ ์์ ์ ์๋์ผ๋ก ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๋ ์ด๋ฒคํธ ์์คํ ์ด๋ค. ๋ฆฐํธ ๊ฒ์ฌ, ์ปค๋ฐ ๋ฉ์์ง ๊ท์น ๊ฐ์ , ๋ฏผ๊ฐ ์ ๋ณด ์ ์ถ ๋ฐฉ์ง, ๋ฐฐํฌ ์๋ํ ๋ฑ ๋ค์ํ ์ฉ๋๋ก ํ์ฉํ๋ฉฐ, Huskyยทpre-commitยทLefthook ๊ฐ์ ๋๊ตฌ๋ก ํ ์ ์ฒด๊ฐ ๋์ผํ Hook์ ๊ณต์ ํ ์ ์๋ค.
1. Git Hook์ด๋
ํ๋ก๊ทธ๋๋ฐ์์ Hook์ ํน์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋ ์๋์ผ๋ก ์คํ๋๋ ์ฝ๋๋ฅผ ์๋ฏธํ๋ค. Git Hook๋ ๊ฐ์ ์๋ฆฌ๋ค. git commit, git push ๊ฐ์ Git ๋์์ด ์ผ์ด๋ ๋, ์ฌ์ ์ ๋ฑ๋กํด๋ ์คํฌ๋ฆฝํธ๊ฐ ์๋์ผ๋ก ์คํ๋๋ค.
1-1. .git/hooks/ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
git init์ผ๋ก ์ ์ฅ์๋ฅผ ๋ง๋ค๋ฉด .git/hooks/ ๋๋ ํ ๋ฆฌ๊ฐ ์๋ ์์ฑ๋๊ณ , ์ํ ์คํฌ๋ฆฝํธ๊ฐ ๋ค์ด์๋ค.
ls .git/hooks/
# applypatch-msg.sample pre-commit.sample
# commit-msg.sample pre-push.sample
# post-update.sample pre-rebase.sample
# pre-applypatch.sample prepare-commit-msg.sample
# ....sample ํ์ฅ์๋ฅผ ์ ๊ฑฐํ๋ฉด ํด๋น Hook์ด ํ์ฑํ๋๋ค. ์คํฌ๋ฆฝํธ๋ Bash, Python, Node.js ๋ฑ ์คํ ๊ฐ๋ฅํ ์ธ์ด๋ผ๋ฉด ๋ฌด์์ด๋ ์ฌ์ฉํ ์ ์๋ค.
# pre-commit Hook ํ์ฑํ
mv .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit1-2. ๋์ ์๋ฆฌ
Git Hook์ ํต์ฌ์ ์ข ๋ฃ ์ฝ๋(exit code) ๊ธฐ๋ฐ ์ ์ด๋ค.
- ์ข
๋ฃ ์ฝ๋
0โ ๋์ ๊ณ์ ์งํ - ์ข
๋ฃ ์ฝ๋
0 ์ด์ธโ ๋์ ์ค๋จ
์ด ๋จ์ํ ๊ท์น์ผ๋ก ์ปค๋ฐ ์ฐจ๋จ, ํธ์ ๊ฑฐ๋ถ ๊ฐ์ ์ ์ด๊ฐ ๊ฐ๋ฅํด์ง๋ค. ๋จ, ๋ชจ๋ Hook์ด ๋์์ ์ฐจ๋จํ๋ ๊ฒ์ ์๋๊ณ , post-* ๊ณ์ด Hook์ ๋์์ด ์ด๋ฏธ ์๋ฃ๋ ๋ค์ ์คํ๋๋ฏ๋ก ์๋ฆผ์ด๋ ํ์ฒ๋ฆฌ ์ฉ๋๋ก๋ง ์ธ ์ ์๋ค.
2. Client-side Hook
๊ฐ๋ฐ์์ ๋ก์ปฌ ํ๊ฒฝ์์ ์คํ๋๋ Hook์ด๋ค. ๊ฐ์ฅ ์์ฃผ ์ฐ์ด๋ Hook๋ค์ด ์ฌ๊ธฐ์ ์ํ๋ค.
2-1. ์ปค๋ฐ ๊ด๋ จ Hook
์ปค๋ฐ ๊ณผ์ ์ ๋ด๋ถ์ ์ผ๋ก ์ฌ๋ฌ ๋จ๊ณ๋ก ๋๋๋ฉฐ, ๊ฐ ๋จ๊ณ๋ง๋ค Hook ์ง์ ์ด ์๋ค.
pre-commit
git commit ์คํ โ [pre-commit] โ ์คํ
์ด์ง ์์ญ ์ค๋
์ท ์์ฑ โ ...
์ปค๋ฐ์ด ๋ง๋ค์ด์ง๊ธฐ ์ง์ ์ ์คํ๋๋ค. ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๋ Hook์ด๋ค.
- ๋ฆฐํธ ๊ฒ์ฌ (eslint, flake8 ๋ฑ)
- ์ฝ๋ ํฌ๋งคํ (prettier, black ๋ฑ)
- ๋ฏผ๊ฐ ์ ๋ณด(API ํค, ๋น๋ฐ๋ฒํธ) ์ ์ถ ๊ฐ์ง
- ํ์ ์ฒดํฌ
#!/bin/sh
# ์คํ
์ด์ง๋ ํ์ผ์์ console.log ๊ฒ์ถ ์ ์ปค๋ฐ ์ฐจ๋จ
if git diff --cached --name-only | xargs grep -l 'console.log' 2>/dev/null; then
echo "console.log๊ฐ ๋จ์์์ต๋๋ค."
exit 1
fiprepare-commit-msg
์ปค๋ฐ ๋ฉ์์ง ํธ์ง๊ธฐ๊ฐ ์ด๋ฆฌ๊ธฐ ์ ์ ์คํ๋๋ค. ๊ธฐ๋ณธ ์ปค๋ฐ ๋ฉ์์ง๋ฅผ ์๋์ผ๋ก ์ฑ์ธ ๋ ์ ์ฉํ๋ค.
- ๋ธ๋์น ์ด๋ฆ์์ ์ด์ ๋ฒํธ๋ฅผ ์ถ์ถํด ๋ฉ์์ง์ ์ฝ์
- ๋จธ์ง ์ปค๋ฐ, ์ค์ฟผ์ ์ปค๋ฐ์ ๊ธฐ๋ณธ ๋ฉ์์ง ์์
#!/bin/sh
# ๋ธ๋์น ์ด๋ฆ์ด feat/ISSUE-123 ํํ๋ฉด ์ปค๋ฐ ๋ฉ์์ง์ [ISSUE-123] ์๋ ์ถ๊ฐ
BRANCH=$(git symbolic-ref --short HEAD)
ISSUE=$(echo "$BRANCH" | grep -oE '[A-Z]+-[0-9]+')
if [ -n "$ISSUE" ]; then
sed -i.bak -e "1s/^/[$ISSUE] /" "$1"
ficommit-msg
์ปค๋ฐ ๋ฉ์์ง ์์ฑ์ด ๋๋ ๋ค, ์ปค๋ฐ์ด ํ์ ๋๊ธฐ ์ ์ ์คํ๋๋ค. ๋ฉ์์ง ๋ด์ฉ์ ๊ฒ์ฆํ๋ ์ฉ๋๋ค.
- Conventional Commits ํ์ ๊ฐ์ (
feat:,fix:,docs:๋ฑ) - ๋ฉ์์ง ๊ธธ์ด ์ ํ
- ์ด์ ๋ฒํธ ํฌํจ ์ฌ๋ถ ํ์ธ
#!/bin/sh
# Conventional Commits ํ์์ด ์๋๋ฉด ์ฐจ๋จ
if ! head -1 "$1" | grep -qE '^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+'; then
echo "์ปค๋ฐ ๋ฉ์์ง๊ฐ Conventional Commits ํ์์ด ์๋๋๋ค."
echo "์: feat: ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ์ถ๊ฐ"
exit 1
fipost-commit
์ปค๋ฐ์ด ์๋ฃ๋ ํ ์คํ๋๋ค. ์ข ๋ฃ ์ฝ๋์ ๋ฌด๊ดํ๊ฒ ์ปค๋ฐ์ ์ด๋ฏธ ์๋ฃ๋ ์ํ์ด๋ฏ๋ก, ์๋ฆผ ์ ์ก์ด๋ ๋ก๊ทธ ๊ธฐ๋ก ๊ฐ์ ํ์ฒ๋ฆฌ์ ์ฌ์ฉํ๋ค.
2-2. ์ด๋ฉ์ผ ๊ด๋ จ Hook
git am ๋ช
๋ น์ผ๋ก ์ด๋ฉ์ผ ํจ์น๋ฅผ ์ ์ฉํ ๋ ์ฌ์ฉ๋๋ Hook์ด๋ค. ๋ฉ์ผ๋ง ๋ฆฌ์คํธ ๊ธฐ๋ฐ ์ํฌํ๋ก์์ ์ฃผ๋ก ์ฐ์ธ๋ค.
| Hook | ์์ | ์ฉ๋ |
|---|---|---|
applypatch-msg | ํจ์น ์ ์ฉ ์ | ์ปค๋ฐ ๋ฉ์์ง ๊ฒ์ฆ |
pre-applypatch | ํจ์น ์ ์ฉ ํ, ์ปค๋ฐ ์ | ํจ์น ๊ฒฐ๊ณผ ๊ฒ์ฆ (ํ ์คํธ ์คํ ๋ฑ) |
post-applypatch | ์ปค๋ฐ ์๋ฃ ํ | ์๋ฆผ ์ ์ก |
์ฐธ๊ณ
์ด๋ฉ์ผ ํจ์น ์ํฌํ๋ก๋ Linux ์ปค๋ ๊ฐ์ ๋๊ท๋ชจ ์คํ์์ค ํ๋ก์ ํธ์์ ์ฃผ๋ก ์ฌ์ฉ๋๋ค. GitHub/GitLab ๊ธฐ๋ฐ PR ์ํฌํ๋ก์์๋ ๊ฑฐ์ ์ฐ์ด์ง ์๋๋ค.
2-3. ๊ธฐํ Client-side Hook
pre-rebase
git rebase ์คํ ์ ์ ๋์ํ๋ค. ์ด๋ฏธ ํธ์๋ ์ปค๋ฐ์ rebase๋ฅผ ๋ฐฉ์งํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ค.
#!/bin/sh
# main ๋ธ๋์น์์์ rebase ์ฐจ๋จ
if [ "$(git symbolic-ref --short HEAD)" = "main" ]; then
echo "main ๋ธ๋์น์์ rebaseํ ์ ์์ต๋๋ค."
exit 1
fipost-rewrite
git commit --amend๋ git rebase์ฒ๋ผ ๊ธฐ์กด ์ปค๋ฐ์ ๋ค์ ์ฐ๋ ๋ช
๋ น ์ดํ์ ์คํ๋๋ค.
post-checkout
git checkout์ด๋ git switch๋ก ๋ธ๋์น๋ฅผ ์ ํํ ํ ์คํ๋๋ค.
- ๋ธ๋์น๋ณ๋ก ๋ค๋ฅธ ํ๊ฒฝ ๋ณ์ ์ค์
- ์์กด์ฑ ์๋ ์ค์น (
npm install๋ฑ) - ๋น๋ ์บ์ ์ ๋ฆฌ
post-merge
git merge ์๋ฃ ํ ์คํ๋๋ค. ๋จธ์ง ํ ์์กด์ฑ์ ๋ค์ ์ค์นํ๊ฑฐ๋, ๋ง์ด๊ทธ๋ ์ด์
์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๋ ๋ฐ ํ์ฉํ๋ค.
pre-push
git push ์คํ ์ , ๋ฆฌ๋ชจํธ์ ๋ฐ์ดํฐ๊ฐ ์ ์ก๋๊ธฐ ์ง์ ์ ์คํ๋๋ค.
- ์ ์ฒด ํ ์คํธ ์ค์ํธ ์คํ
- ๋ณดํธ ๋ธ๋์น(main, production)๋ก์ ์ง์ ํธ์ ์ฐจ๋จ
- ๋์ฉ๋ ํ์ผ ํธ์ ๋ฐฉ์ง
#!/bin/sh
# main ๋ธ๋์น๋ก ์ง์ push ์ฐจ๋จ
BRANCH=$(git symbolic-ref --short HEAD)
if [ "$BRANCH" = "main" ]; then
echo "main ๋ธ๋์น๋ก ์ง์ pushํ ์ ์์ต๋๋ค. PR์ ์ฌ์ฉํ์ธ์."
exit 1
fi3. Server-side Hook
Git ์๋ฒ(์๊ฒฉ ์ ์ฅ์)์์ ์คํ๋๋ Hook์ด๋ค. ํ ์ ์ฒด์ ์ผ๊ด ์ ์ฉ๋๋ฉฐ, ํด๋ผ์ด์ธํธ๊ฐ ์ฐํํ ์ ์๋ค๋ ์ ์ด Client-side Hook๊ณผ์ ํต์ฌ ์ฐจ์ด๋ค.
pre-receive
ํด๋ผ์ด์ธํธ๊ฐ git push๋ฅผ ๋ณด๋ด๋ฉด, ์๋ฒ๊ฐ ref๋ฅผ ์
๋ฐ์ดํธํ๊ธฐ ์ ์ ์คํ๋๋ค. ํธ์ ์ ์ฒด๋ฅผ ํ ๋ฒ์ ์๋ฝํ๊ฑฐ๋ ๊ฑฐ๋ถํ๋ค.
- ๋ธ๋์น ๋ณดํธ ์ ์ฑ ๊ฐ์
- ์ปค๋ฐ ๋ฉ์์ง ๊ท์น ์๋ฒ ์ธก ๊ฒ์ฆ
- ํน์ ํ์ผ ํจํด ํธ์ ๊ฑฐ๋ถ (๋ฐ์ด๋๋ฆฌ, ๋์ฉ๋ ํ์ผ ๋ฑ)
update
pre-receive์ ๋น์ทํ์ง๋ง, ์
๋ฐ์ดํธ๋๋ ๊ฐ ๋ธ๋์น๋ง๋ค ๊ฐ๋ณ์ ์ผ๋ก ์คํ๋๋ค. ํน์ ๋ธ๋์น๋ง ์ ํ์ ์ผ๋ก ๊ฑฐ๋ถํ ์ ์๋ค.
#!/bin/sh
# release/* ๋ธ๋์น๋ ํน์ ์ฌ์ฉ์๋ง push ํ์ฉ
REF=$1
if echo "$REF" | grep -q "refs/heads/release/"; then
if [ "$USER" != "release-manager" ]; then
echo "release ๋ธ๋์น๋ release-manager๋ง pushํ ์ ์์ต๋๋ค."
exit 1
fi
fipost-receive
ํธ์๊ฐ ์๋ฃ๋ ํ ์คํ๋๋ค. ์๋ฒ ์ธก ํ์ฒ๋ฆฌ์ ํ์ฉํ๋ค.
- CI/CD ํ์ดํ๋ผ์ธ ํธ๋ฆฌ๊ฑฐ
- ๋ฐฐํฌ ์๋ํ
- ์ฑํ /์ด๋ฉ์ผ ์๋ฆผ ์ ์ก
- ์ด์ ํธ๋์ปค ์๋ ์ ๋ฐ์ดํธ
Server-side Hook์ด ์ค์ํ ์ด์
Client-side Hook์
--no-verify์ต์ ์ผ๋ก ์ฐํํ ์ ์์ง๋ง, Server-side Hook์ ์ฐํ๊ฐ ๋ถ๊ฐ๋ฅํ๋ค. ํ ์ ์ฒด์ ๋ฐ๋์ ์ ์ฉํด์ผ ํ๋ ์ ์ฑ ์ Server-side Hook์ผ๋ก ๊ฑธ์ด์ผ ํ๋ค.
4. Hook ์ค์ ํ์ฉ ์์
4-1. ๋ฆฐํธ/ํฌ๋งคํฐ ์๋ ์คํ (pre-commit)
์ปค๋ฐ ์ ์ ์คํ ์ด์ง๋ ํ์ผ๋ง ๋์์ผ๋ก ๋ฆฐํธ์ ํฌ๋งคํ ์ ์คํํ๋ค. ์ ์ฒด ํ๋ก์ ํธ๋ฅผ ๊ฒ์ฌํ๋ฉด ๋๋ฆฌ๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝ๋ ํ์ผ๋ง ๊ฒ์ฌํ๋ ๊ฒ์ด ํต์ฌ์ด๋ค.
#!/bin/sh
# ์คํ
์ด์ง๋ JS/TS ํ์ผ๋ง eslint ์คํ
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
if [ -n "$FILES" ]; then
npx eslint $FILES || exit 1
fi4-2. ์ปค๋ฐ ๋ฉ์์ง ์ปจ๋ฒค์ ๊ฐ์ (commit-msg)
ํ์์ Conventional Commits ๊ฐ์ ๋ฉ์์ง ๊ท์น์ ์ ํ๋๋ผ๋, ์ฌ๋์ด ๋งค๋ฒ ๊ธฐ์ตํ๊ธฐ๋ ์ด๋ ต๋ค. commit-msg Hook์ผ๋ก ์๋ ๊ฒ์ฆํ๋ฉด ๊ท์น์ด ์์ฐ์ค๋ฝ๊ฒ ์ ์ฐฉ๋๋ค.
4-3. ๋ฏผ๊ฐ ์ ๋ณด ์ ์ถ ๋ฐฉ์ง (pre-commit)
.env ํ์ผ์ด .gitignore์ ์๋๋ผ๋, ์ฝ๋ ์์ ํ๋์ฝ๋ฉ๋ API ํค๋ ๋น๋ฐ๋ฒํธ๋ ์ก์๋ด์ง ๋ชปํ๋ค.
#!/bin/sh
# AWS ํค ํจํด ๊ฐ์ง
if git diff --cached | grep -qE 'AKIA[0-9A-Z]{16}'; then
echo "AWS Access Key๊ฐ ๊ฐ์ง๋์์ต๋๋ค. ์ปค๋ฐ์ ์ค๋จํฉ๋๋ค."
exit 1
fi์ ๋ฌธ ๋๊ตฌ ํ์ฉ
๋จ์ ์ ๊ท์๋ณด๋ค๋ gitleaks๋ detect-secrets ๊ฐ์ ์ ๋ฌธ ๋๊ตฌ๋ฅผ pre-commit Hook์ ์ฐ๊ฒฐํ๋ ๊ฒ์ด ๋ ์์ ์ ์ด๋ค.
4-4. ๋ฐฐํฌ ์๋ํ (post-receive)
์๋ฒ์ post-receive Hook์ผ๋ก ํน์ ๋ธ๋์น์ ํธ์๊ฐ ์ค๋ฉด ์๋ ๋ฐฐํฌ๋ฅผ ์คํํ ์ ์๋ค.
#!/bin/sh
while read oldrev newrev refname; do
if [ "$refname" = "refs/heads/main" ]; then
echo "main ๋ธ๋์น ๋ฐฐํฌ ์์..."
GIT_WORK_TREE=/var/www/app git checkout -f main
cd /var/www/app && npm install && npm run build
fi
done5. Hook ๊ด๋ฆฌ ๋๊ตฌ
.git/hooks/ ๋๋ ํ ๋ฆฌ๋ Git์ด ์ถ์ ํ์ง ์๊ธฐ ๋๋ฌธ์, ํ์๋ค๊ณผ Hook์ ๊ณต์ ํ๋ ค๋ฉด ๋ณ๋ ๋๊ตฌ๊ฐ ํ์ํ๋ค. ํ๋ก์ ํธ์ ๊ธฐ์ ์คํ์ ๋ฐ๋ผ ์ ํฉํ ๋๊ตฌ๋ฅผ ์ ํํ๋ฉด ๋๋ค.
5-1. Husky (Node.js)
Node.js ํ๋ก์ ํธ์์ ๊ฐ์ฅ ๋๋ฆฌ ์ฐ์ด๋ Git Hook ๊ด๋ฆฌ ๋๊ตฌ๋ค. package.json๊ณผ ํจ๊ป ๋ฒ์ ๊ด๋ฆฌ๋๋ฏ๋ก ํ ์ ์ฒด๊ฐ ๋์ผํ Hook์ ๊ณต์ ํ๋ค.
# ์ค์น
npm install --save-dev husky
# ์ด๊ธฐํ (.husky/ ๋๋ ํ ๋ฆฌ ์์ฑ)
npx husky init.husky/pre-commit ํ์ผ์ ์์ ํด Hook์ ๋ฑ๋กํ๋ค.
# .husky/pre-commit
npx lint-stagedlint-staged์์ ์กฐํฉ
Husky๋ ๋ณดํต lint-staged์ ํจ๊ป ์ด๋ค. lint-staged๋ ์คํ ์ด์ง๋ ํ์ผ๋ง ๋์์ผ๋ก ๋ฆฐํธ/ํฌ๋งคํ ์ ์คํํด ์๋๋ฅผ ๋์ฌ์ค๋ค.
5-2. pre-commit framework (Python)
Python ์ํ๊ณ์์ ํ์ค์ฒ๋ผ ์ฐ์ด๋ ๋๊ตฌ๋ค. YAML ์ค์ ํ์ผ ํ๋๋ก ๋ค์ํ Hook์ ์ ์ธ์ ์ผ๋ก ๊ด๋ฆฌํ๋ค. Python ํ๋ก์ ํธ๊ฐ ์๋์ด๋ ์ฌ์ฉํ ์ ์๋ค.
# ์ค์น
pip install pre-commit.pre-commit-config.yaml ํ์ผ๋ก Hook์ ์ค์ ํ๋ค.
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black# Git Hook์ผ๋ก ๋ฑ๋ก
pre-commit install
# ์ ์ฒด ํ์ผ์ ์๋ ์คํ
pre-commit run --all-files์ปค๋ฎค๋ํฐ์์ ์ ๊ณตํ๋ ์๋ฐฑ ๊ฐ์ Hook์ ๊ฐ์ ธ๋ค ์ธ ์ ์๋ค๋ ๊ฒ์ด ํฐ ์ฅ์ ์ด๋ค.
5-3. Lefthook (Go)
Go๋ก ์์ฑ๋ ๋๊ตฌ๋ก, ๋ณ๋ ๋ฐํ์ ์์กด์ฑ ์์ด ๋ฐ์ด๋๋ฆฌ ํ๋๋ก ๋์ํ๋ค. ์ค์ ์ด ๊ฐ๊ฒฐํ๊ณ ๋ณ๋ ฌ ์คํ์ ์ง์ํด ์๋๊ฐ ๋น ๋ฅด๋ค.
# ์ค์น
brew install lefthooklefthook.yml ํ์ผ๋ก ์ค์ ํ๋ค.
pre-commit:
parallel: true
commands:
lint:
glob: "*.{js,ts,tsx}"
run: npx eslint {staged_files}
format:
glob: "*.{js,ts,tsx}"
run: npx prettier --check {staged_files}# Git Hook์ผ๋ก ๋ฑ๋ก
lefthook install5-4. simple-git-hooks (Node.js)
Husky๋ณด๋ค ๊ฐ๋ฒผ์ด ๋์์ด๋ค. ๋ณ๋ ๋๋ ํ ๋ฆฌ ์์ด package.json ์์ Hook์ ์ง์ ์ ์ธํ๋ค. ์ค์ ํ์ผ์ด ๋ฐ๋ก ์๊ณ , Hook ํ๋๋น ๋ช
๋ น์ด ํ๋๋ฅผ ๋งคํํ๋ ๊ตฌ์กฐ๋ผ์ ๋จ์ํ ํ๋ก์ ํธ์ ์ ํฉํ๋ค.
# ์ค์น
npm install --save-dev simple-git-hookspackage.json์ Hook์ ์ ์ธํ๋ค.
{
"simple-git-hooks": {
"pre-commit": "npx lint-staged",
"commit-msg": "npx commitlint --edit $1"
}
}# Git Hook์ผ๋ก ๋ฑ๋ก (package.json์ "prepare" ์คํฌ๋ฆฝํธ์ ๋ฃ์ด๋๋ฉด npm install ์ ์๋ ์คํ)
npx simple-git-hooksHusky์์ ์ฐจ์ด
Husky๋
.husky/๋๋ ํ ๋ฆฌ์ Hook๋ณ ์คํฌ๋ฆฝํธ ํ์ผ์ ๋๋ ๋ฐ๋ฉด, simple-git-hooks๋package.json์ ์ธ๋ผ์ธ์ผ๋ก ์ ์ธํ๋ค. Hook ์๊ฐ ์ ๊ณ ๋จ์ํ ๋ช ๋ น๋ง ์คํํ๋ฉด simple-git-hooks๊ฐ ๋ ๊ฐ๊ฒฐํ๋ค.
5-5. ๋๊ตฌ ๋น๊ต
| ํญ๋ชฉ | Husky | pre-commit | Lefthook | simple-git-hooks |
|---|---|---|---|---|
| ์ธ์ด | Node.js | Python | Go | Node.js |
| ๋ฐํ์ ์์กด์ฑ | Node.js ํ์ | Python ํ์ | ์์ (๋ฐ์ด๋๋ฆฌ) | Node.js ํ์ |
| ์ค์ ํ์ผ | .husky/ ๋๋ ํ ๋ฆฌ | .pre-commit-config.yaml | lefthook.yml | package.json ๋ด ์ธ๋ผ์ธ |
| ๋ณ๋ ฌ ์คํ | ๋ฏธ์ง์ | ๋ฏธ์ง์ | ์ง์ | ๋ฏธ์ง์ |
| ์ปค๋ฎค๋ํฐ Hook | lint-staged ์กฐํฉ | ์๋ฐฑ ๊ฐ ๋ด์ฅ ์ ์ฅ์ | ์ง์ ๋ช ๋ น ์ง์ | ์ง์ ๋ช ๋ น ์ง์ |
| ์ถ์ฒ ํ๊ฒฝ | Node.js ํ๋ก์ ํธ | Python ๋๋ ๋ค๊ตญ์ด ํ๋ก์ ํธ | ๋ฐํ์ ์์กด์ฑ ์์ด ์ฐ๊ณ ์ถ์ ๋ | Hook์ด ์ ๊ณ ์ค์ ์ ๊ฐ๊ฒฐํ๊ฒ ์ ์งํ๊ณ ์ถ์ ๋ |
6. ์ฃผ์์ฌํญ
--no-verify๋ก ์ฐํ ๊ฐ๋ฅ:git commit --no-verify๋ฅผ ์ฐ๋ฉด Client-side Hook์ ๊ฑด๋๋ธ ์ ์๋ค. ๋ฐ๋์ ์ง์ผ์ผ ํ๋ ๊ท์น์ Server-side Hook์ด๋ CI์์ ์ด์ค์ผ๋ก ๊ฒ์ฆํด์ผ ํ๋ค..git/hooks/๋ ๋ฒ์ ๊ด๋ฆฌ ๋์์ด ์๋: Git์.git/๋ด๋ถ ํ์ผ์ ์ถ์ ํ์ง ์๋๋ค. ํ ๊ณต์ ๊ฐ ํ์ํ๋ฉด ์์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๊ฑฐ๋, ๋ณ๋ ๋๋ ํ ๋ฆฌ์ ์คํฌ๋ฆฝํธ๋ฅผ ๋๊ณcore.hooksPath์ค์ ์ผ๋ก ์ฐ๊ฒฐํ๋ค.
# ์ปค์คํ
Hook ๋๋ ํ ๋ฆฌ ์ง์
git config core.hooksPath .githooks- ์คํ ์๊ฐ์ ์ฃผ์: pre-commit Hook์ด ๋๋ฆฌ๋ฉด ๊ฐ๋ฐ ํ๋ฆ์ด ๋๊ธด๋ค. ์ ์ฒด ํ ์คํธ๋ณด๋ค๋ ๋ณ๊ฒฝ ํ์ผ ๋์์ ๊ฐ๋ฒผ์ด ๊ฒ์ฌ๋ง ๊ฑธ๊ณ , ๋ฌด๊ฑฐ์ด ๊ฒ์ฆ์ CI์ ๋งก๊ธฐ๋ ๊ฒ์ด ์ข๋ค.
- ์คํ ๊ถํ ํ์: Hook ์คํฌ๋ฆฝํธ์ ์คํ ๊ถํ(
chmod +x)์ด ์์ผ๋ฉด ๋์ํ์ง ์๋๋ค.