19. Git 훅(Hooks) — 자동 검사 걸기
- Git 훅이 무엇이고 언제 실행되는지 이해한다
- 네이티브 훅(
.git/hooks)으로 pre-commit을 만들어 본다 - 훅이 push되지 않는다는 한계와 그 해결책을 안다
- husky + lint-staged + commitlint로 실무 자동화를 구성한다
19.1 Git 훅이란?
Git 훅은 커밋·푸시 같은 특정 시점에 자동으로 실행되는 스크립트입니다. "이 시점에 이 검사를 꼭 거쳐라"라고 Git에 자동 검문소를 설치하는 것입니다.
예를 들면:
- 커밋하기 직전 → 코드 스타일 검사(린트)·테스트를 돌려, 통과 못 하면 커밋 차단
- 커밋 메시지 작성 시 → 메시지 형식이 규칙에 맞는지 검사
- 푸시하기 직전 → 전체 테스트 실행
flowchart LR
C["git commit"]
H["🔍 pre-commit 훅<br/>린트 / 테스트"]
OK["✅ 커밋 완료"]
NO["❌ 커밋 거부"]
C --> H
H -- "통과" --> OK
H -- "실패" --> NO
classDef cmd fill:#dbeafe,stroke:#2563eb,color:#1e40af
classDef hook fill:#fef3c7,stroke:#d97706,color:#92400e
classDef ok fill:#dcfce7,stroke:#16a34a,color:#166534
classDef no fill:#fee2e2,stroke:#dc2626,color:#991b1b
class C cmd
class H hook
class OK ok
class NO no
19.2 훅은 어디에 있나? — .git/hooks
모든 저장소에는 .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 확장자가 붙어 있으면 비활성입니다. 확장자를 떼면 활성화됩니다.
주요 훅 종류
| 훅 이름 | 실행 시점 | 용도 |
|---|---|---|
pre-commit | 커밋 직전 | 린트, 테스트, 포맷 검사 |
prepare-commit-msg | 메시지 편집기 열리기 전 | 메시지 템플릿 자동 삽입 |
commit-msg | 메시지 작성 후 | 메시지 형식 검증 |
post-commit | 커밋 완료 후 | 알림 등 |
pre-push | 푸시 직전 | 전체 테스트 |
pre-rebase | 리베이스 직전 | 특정 브랜치 리베이스 금지 |
19.3 네이티브 pre-commit 훅 만들어보기
직접 간단한 pre-commit 훅을 만들어봅시다.
# .git/hooks/pre-commit 파일 생성 touch .git/hooks/pre-commit
내용 작성 (pre-commit 파일):
#!/bin/sh echo "🔍 커밋 전 검사 실행 중..." # 예: 'console.log'가 있으면 커밋 거부 if git diff --cached | grep -q "console.log"; then echo "❌ console.log 가 포함되어 있습니다. 제거 후 커밋하세요." exit 1 # 0이 아닌 값을 반환하면 커밋이 중단됨 fi echo "✅ 검사 통과!" exit 0
실행 권한을 줍니다 (안 주면 동작 안 함).
chmod +x .git/hooks/pre-commit
이제 console.log가 든 변경을 커밋하려 하면 거부됩니다.
exit 0이면 통과, 0이 아니면 작업 중단입니다. 이것으로 "검사 실패 시 커밋 차단"을 구현합니다.정규표현식으로 작성자 이메일 검증 (예)
회사 이메일로만 커밋하도록 강제하는 pre-commit 훅 예시:
@mycompany\.com$에서 $는 "문자열의 끝", \.은 "마침표 글자 그대로"를 뜻합니다.#!/bin/sh EMAIL=$(git config user.email) if ! echo "$EMAIL" | grep -qE "@mycompany\.com$"; then echo "❌ 회사 이메일(@mycompany.com)로만 커밋할 수 있습니다." exit 1 fi
19.4 네이티브 훅의 치명적 한계
.git/hooks는 .git 폴더 안에 있고, .git은 절대 push되지 않습니다(14장). 즉 내가 만든 훅은 내 컴퓨터에만 있고, 팀원들에게는 전달되지 않습니다.
나: .git/hooks/pre-commit ✅ 있음 동료: .git/hooks/pre-commit ❌ 없음 (push 안 되니까)
팀 전체가 같은 훅을 쓰게 하려면 다른 방법이 필요합니다. 여기서 husky 같은 도구가 등장합니다.
19.5 husky — 팀과 공유되는 훅
husky는 훅 스크립트를 프로젝트 폴더(.husky/)에 두고 버전 관리에 포함시켜, npm install 한 번이면 팀원 모두에게 훅이 설치되게 해주는 도구입니다. Node.js 프로젝트에서 표준처럼 쓰입니다.
설정 절차
# 1. Node.js 프로젝트 준비 npm init -y # 2. husky 설치 (개발 의존성) npm install --save-dev husky # 3. husky 초기화 (.husky/ 폴더 + prepare 스크립트 생성) npx husky init
husky init은 .husky/ 폴더를 만들고, package.json에 다음을 추가합니다.
{
"scripts": {
"prepare": "husky"
}
}
prepare는 npm install 후 자동 실행되는 npm 스크립트입니다. 그래서 팀원이 프로젝트를 받아 npm install만 하면 훅이 자동 설치됩니다. 이것이 네이티브 훅과의 결정적 차이입니다.pre-commit 훅 추가
.husky/pre-commit 파일을 만들고 실행할 명령을 적습니다.
# .husky/pre-commit npm test
이제 .husky/pre-commit은 일반 파일이라 Git에 커밋되어 팀과 공유됩니다. 누구나 커밋 시 자동으로 테스트가 돌아갑니다.
19.6 실전 구성: 테스트 + 린트 자동화
- Jest: 코드가 의도대로 동작하는지 자동으로 확인하는 테스트 도구.
- ESLint: 코드의 문법·스타일 문제를 잡아내는 린터(앞서 설명한 린트 도구).
- Prettier: 코드의 들여쓰기·줄바꿈 등 서식을 자동으로 정리해주는 포매터.
다른 언어에도 비슷한 역할의 도구가 있으며, 훅에 거는 방식은 동일합니다.
테스트 도구(Jest) 준비
npm install --save-dev jest
package.json:
{
"scripts": {
"test": "jest",
"lint": "eslint ."
}
}
간단한 함수와 테스트 예:
// sum.js function sum(a, b) { return a + b; } module.exports = sum; // sum.test.js const sum = require('./sum'); test('1 + 2 = 3', () => { expect(sum(1, 2)).toBe(3); });
.husky/pre-commit:
npm run lint npm test
이제 커밋할 때마다 린트와 테스트가 자동으로 돌고, 실패하면 커밋이 막힙니다.
19.7 lint-staged — 바뀐 파일만 검사하기
프로젝트가 커지면 매 커밋마다 모든 파일을 린트하는 건 느립니다. lint-staged는 스테이지에 올라간(변경된) 파일만 검사해 속도를 크게 높입니다.
npm install --save-dev lint-staged
package.json에 설정 추가:
{
"lint-staged": {
"*.js": ["eslint --fix", "prettier --write"],
"*.{json,md}": ["prettier --write"]
}
}
.husky/pre-commit을 lint-staged 호출로 변경:
npx lint-staged
이제 커밋 시 방금 수정한 .js 파일만 ESLint·Prettier가 검사·자동수정합니다. 전체 검사보다 훨씬 빠릅니다.
git commit ─► lint-staged ─► 스테이지된 .js만 eslint --fix
통과하면 커밋, 아니면 차단
19.8 commitlint — 커밋 메시지 규칙 강제
팀의 커밋 메시지를 일관된 형식(예: Conventional Commits)으로 강제하려면 commitlint를 commit-msg 훅에 연결합니다.
npm install --save-dev @commitlint/cli @commitlint/config-conventional
설정 파일 commitlint.config.js:
module.exports = { extends: ['@commitlint/config-conventional'] };
.husky/commit-msg 훅:
npx --no-install commitlint --edit "$1"
이제 커밋 메시지는 다음 형식을 따라야 합니다.
feat: add login button ✅ (type: subject 형식) fix: resolve cart crash ✅ update stuff ❌ (type 없음 → 거부)
| type | 의미 |
|---|---|
feat | 새 기능 |
fix | 버그 수정 |
docs | 문서 |
refactor | 리팩터링 |
test | 테스트 |
chore | 잡무(빌드/설정 등) |
19.9 훅 건너뛰기 — --no-verify
급할 때 훅을 일시적으로 건너뛰려면:
git commit -m "WIP" --no-verify # pre-commit, commit-msg 훅 생략 git push --no-verify # pre-push 훅 생략
--no-verify는 비상용입니다. 남용하면 훅을 설치한 의미가 없어집니다. 정말 필요한 경우에만 쓰세요. (husky는 HUSKY=0 git commit ...으로 전체 비활성화도 가능합니다.)19.10 정리: 네이티브 훅 vs husky
| 네이티브 훅 | husky | |
|---|---|---|
| 위치 | .git/hooks/ | .husky/ (프로젝트 폴더) |
| 공유 | ❌ push 안 됨 | ✅ Git에 커밋되어 공유 |
| 설치 | 수동 | npm install 시 자동 |
| 적합 | 개인용 빠른 검사 | 팀 협업 표준 |
19.11 이 장에서 배운 것 (요약)
- Git 훅 = 특정 시점에 자동 실행되는 스크립트 (검문소)
- 네이티브 훅은
.git/hooks/에 두고 실행권한 부여,exit 0=통과 / 비0=중단 - 핵심 한계: 로컬 훅은 push되지 않아 팀과 공유 불가
- husky(v9)는
.husky/에 훅을 두고npm install로 자동 설치 → 팀 공유 해결 - lint-staged로 변경된 파일만 빠르게 검사
- commitlint로 커밋 메시지 형식 강제
- 비상시
--no-verify로 건너뛰기 (남용 금지)
✍️ 확인 문제
- 네이티브 훅(
.git/hooks)을 팀원과 공유할 수 없는 이유는? - 커밋 시 변경된 파일만 린트하려면 어떤 도구를 쓰나요?
- 훅 스크립트가 커밋을 중단시키려면 무엇을 반환해야 하나요?
다음 장에서는 코드를 무료로 웹에 배포하는 GitHub Pages를 배웁니다. → 20_GitHubPages.md