본문으로 건너뛰기 Husky 완벽 가이드 | Git Hooks·Pre-commit·Lint-staged·실전 활용

Husky 완벽 가이드 | Git Hooks·Pre-commit·Lint-staged·실전 활용

Husky 완벽 가이드 | Git Hooks·Pre-commit·Lint-staged·실전 활용

이 글의 핵심

Husky로 Git Hooks를 관리하는 완벽 가이드. Pre-commit, Pre-push, Lint-staged, Commitlint까지 실전 예제로 정리. Husky·Git Hooks·Pre-commit 중심으로 설명합니다.

이 글의 핵심

Husky로 Git Hooks를 관리하는 완벽 가이드입니다. Pre-commit, Pre-push, Lint-staged, Commitlint까지 실전 예제로 정리했습니다.

실무 경험 공유: Husky를 도입하면서, 린트 에러가 있는 코드가 커밋되는 일이 사라지고 코드 품질이 크게 향상된 경험을 공유합니다.

들어가며: “나쁜 코드가 커밋돼요”

실무에서 마주치는 문제들

린트 에러가 있는 코드가 푸시돼요

수동 확인은 놓치기 쉽습니다. Husky로 자동 검사합니다. 커밋 메시지가 일관적이지 않아요

규칙이 없습니다. Commitlint로 강제합니다. 테스트를 건너뛰고 푸시해요

실수로 빠뜨립니다. Pre-push Hook으로 방지합니다.

1. Husky란?

Git Hooks의 역사와 탄생 배경

Git Hooks는 Git이 처음 공개된 2005년부터 존재했던 기능입니다. .git/hooks/ 디렉터리에 셸 스크립트를 두면, 특정 Git 이벤트(커밋, 푸시, 머지 등)가 발생할 때 자동으로 실행됩니다. 초기에는 서버 관리자가 CI/CD 대신 post-receive 훅으로 자동 배포를 구현하거나, 커밋 메시지 형식을 강제하는 용도로 썼습니다.

문제는 팀 공유였습니다:

  • .git/hooks/저장소에 커밋되지 않음 (.git 폴더는 Git이 관리)
  • 팀원마다 훅을 수동으로 설치해야 함 → 신입이 훅을 모르면 그대로 푸시
  • Windows/macOS/Linux 경로·셸 차이 → 크로스 플랫폼 스크립트 작성 어려움

Husky 탄생 (2014년 Typicode 개발):

  • package.json으로 훅 관리 → npm install 시 자동 설치
  • Node.js 환경에서 실행 → 크로스 플랫폼 스크립트 작성 용이
  • 팀 전체 강제 → 저장소에 커밋되므로 누구든 동일한 훅 적용

클라이언트 훅 vs 서버 훅

Git Hooks는 클라이언트 훅(로컬)과 서버 훅(원격 저장소)으로 나뉩니다.

훅 종류실행 위치용도Husky 지원
클라이언트 훅
pre-commit로컬커밋 전 린트·테스트
prepare-commit-msg로컬커밋 메시지 템플릿 삽입
commit-msg로컬커밋 메시지 검증
post-commit로컬커밋 후 알림
pre-push로컬푸시 전 테스트·빌드
서버 훅
pre-receive서버푸시 거부 (권한 체크)
post-receive서버배포 트리거
update서버브랜치별 정책

Husky는 클라이언트 훅만 지원합니다. 서버 훅은 GitHub/GitLab의 Protected Branches·CI/CD로 대체합니다.

핵심 특징

Husky는 Git Hooks 관리 도구입니다. 주요 장점

  • 쉬운 설정: 간단한 명령어 (npx husky init)
  • Git Hooks: Pre-commit, Pre-push, Commit-msg 등
  • Lint-staged: 변경된 파일만 검사 (성능 최적화)
  • 팀 공유: package.json으로 관리 (자동 설치)
  • 크로스플랫폼: Windows, macOS, Linux (Node.js 기반)

Husky의 내부 동작

  1. 설치 시 (npx husky init):

    • .husky/ 폴더 생성
    • .git/hooks/ 에 Husky 로더 스크립트 설치
    • package.jsonprepare 스크립트에 husky 추가
  2. 커밋 시 (git commit):

    1. Git이 .git/hooks/pre-commit 실행
    2. Husky 로더가 .husky/pre-commit 찾아 실행
    3. .husky/pre-commit에서 npx lint-staged 등 실행
    4. 스크립트가 exit 0 → 커밋 진행
    5. 스크립트가 exit 1 → 커밋 거부
  3. 팀원 설치 시 (npm install):

    • prepare 스크립트 자동 실행 → Husky 훅 설치
    • 이미 .husky/ 파일이 있으므로 즉시 적용

2. 설치 및 기본 설정

설치

npm install -D husky
npx husky init

자동으로 .husky/ 폴더와 pre-commit 파일이 생성됩니다.

.husky/pre-commit

npm test

3. Pre-commit Hook

ESLint + Prettier

# .husky/pre-commit
npm run lint
npm run format:check

전체 파일 검사

# .husky/pre-commit
npm run lint
npm run test

4. Lint-staged

설치

npm install -D lint-staged

.husky/pre-commit

npx lint-staged

package.json

{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,css,md}": [
      "prettier --write"
    ]
  }
}

5. Pre-push Hook

# .husky/pre-push
npm run test
npm run build

6. Commitlint

설치

npm install -D @commitlint/cli @commitlint/config-conventional

commitlint.config.js

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'docs',
        'style',
        'refactor',
        'test',
        'chore',
      ],
    ],
  },
};

.husky/commit-msg

npx --no -- commitlint --edit $1

커밋 메시지 형식

feat: add user authentication
fix: resolve login bug
docs: update README
style: format code
refactor: simplify API logic
test: add unit tests
chore: update dependencies

7. 실전 예제: 풀 설정

package.json

{
  "scripts": {
    "lint": "eslint src",
    "lint:fix": "eslint src --fix",
    "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"",
    "format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"",
    "test": "jest",
    "prepare": "husky"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write",
      "jest --bail --findRelatedTests"
    ],
    "*.{json,css,md}": [
      "prettier --write"
    ]
  },
  "devDependencies": {
    "husky": "^9.0.0",
    "lint-staged": "^15.0.0",
    "@commitlint/cli": "^18.0.0",
    "@commitlint/config-conventional": "^18.0.0"
  }
}

.husky/pre-commit

npx lint-staged

.husky/pre-push

npm run test
npm run build

.husky/commit-msg

npx --no -- commitlint --edit $1

8. 실무 함정과 대응 전략

함정 1: --no-verify남용

# ❌ 나쁜 습관: "급해서" 매번 bypass
git commit --no-verify -m "fix: urgent hotfix"
git push --no-verify

문제: 훅을 무력화하면 훅을 만든 의미가 없습니다. 프로덕션에 린트 에러가 배포됩니다.

대응

  • --no-verify정말 긴급한 상황(훅 자체 버그, 외부 서비스 장애)에만
  • 코드 리뷰에서 --no-verify 커밋 발견 시 회고 주제로 다루기
  • CI/CD에서 동일한 검사 수행 (훅 우회해도 머지 전 차단)

함정 2: 느린 훅으로 개발 흐름 끊김

# ❌ 전체 테스트 실행 (5분 소요)
# .husky/pre-commit
npm test
npm run build

문제: 커밋 한 번에 5분 대기 → 개발자가 --no-verify 쓰기 시작

대응:

# ✅ 변경된 파일만 테스트
# .husky/pre-commit
npx lint-staged

# package.json
{
  "lint-staged": {
    "*.{js,ts}": [
      "eslint --fix",
      "jest --bail --findRelatedTests" // 관련 테스트만
    ]
  }
}
  • Pre-commit: 빠른 검사 (린트, 포맷, 관련 테스트)
  • Pre-push: 무거운 검사 (전체 테스트, 빌드)

함정 3: CI에서만 실패하는 코드

시나리오: 로컬 훅은 통과했는데 CI에서 실패

# 로컬: 오래된 node_modules
npm test  # 통과

# CI: 깨끗한 환경
npm ci && npm test  # 실패

원인: 로컬 캐시된 패키지 버전이 다름, .env 파일 차이

대응

  • CI와 로컬 훅을 동기화
    # .husky/pre-commit
    npm ci --prefer-offline || npm install
    npm test
  • 환경 변수 검증 (.env.example 제공)

함정 4: Windows 경로 문제

# ❌ Unix 스타일 경로 (Windows에서 실패)
# .husky/pre-commit
#!/bin/sh
./scripts/lint.sh

대응:

# ✅ Node.js 스크립트 사용 (크로스 플랫폼)
# .husky/pre-commit
npx lint-staged
node scripts/validate.js

함정 5: Husky가 설치 안 됨 (신입·CI)

증상: 신입 개발자 PC에서 훅이 작동 안 함

원인

  • npm install --production (devDependencies 제외)
  • .git 폴더가 없는 경로에서 설치 (Docker 빌드 등)

대응:

// package.json
{
  "scripts": {
    "prepare": "husky || true"  // 실패해도 install은 계속
  }
}
  • 저장소 clone 후 첫 npm install 시 자동 설치 확인
  • onboarding 문서에 npx husky install 명시

일시적 비활성화 (정당한 사용)

# ✅ 훅 자체를 디버깅할 때
git commit --no-verify -m "debug: test hook bypass"

# ✅ 외부 서비스(lint API) 장애로 훅 실패할 때
HUSKY=0 git commit -m "fix: bypass due to linter service outage"

9. CI/CD 통합

GitHub Actions

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm run lint
      - run: npm run format:check
      - run: npm test

정리 및 체크리스트

핵심 요약

  • Husky: Git Hooks 관리
  • Pre-commit: 커밋 전 검사
  • Lint-staged: 변경된 파일만
  • Commitlint: 커밋 메시지 규칙
  • Pre-push: 푸시 전 검사
  • 팀 공유: package.json

구현 체크리스트

  • Husky 설치
  • Pre-commit Hook 설정
  • Lint-staged 설정
  • Commitlint 설정
  • Pre-push Hook 설정
  • VS Code 통합
  • 팀원과 공유
  • CI/CD 통합

같이 보면 좋은 글


이 글에서 다루는 키워드

Husky, Git Hooks, Pre-commit, Lint-staged, Code Quality, Git, Frontend

자주 묻는 질문 (FAQ)

Q. 팀원도 자동으로 설정되나요?

A. 네, npm install 시 자동으로 설정됩니다.

Q. Windows에서도 작동하나요?

A. 네, 크로스플랫폼으로 작동합니다.

Q. Hook을 건너뛸 수 있나요?

A. 네, —no-verify 플래그를 사용하면 됩니다.

Q. 프로덕션에서 사용해도 되나요?

A. 네, 거의 모든 JavaScript 프로젝트에서 사용하고 있습니다.