--- id: wiki-2026-0508-husky title: Husky category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Husky Git Hooks, husky v9, lint-staged, pre-commit] duplicate_of: none source_trust_level: A confidence_score: 0.95 verification_status: applied tags: [git, hooks, devex, ci, lint-staged] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: javascript framework: husky-v9 --- # Husky ## 매 한 줄 > **"매 npm-friendly git hooks 의 facto-standard"**. Husky 는 매 `.husky/` directory 안에 매 plain shell script 로 hook 정의 — 매 `git config core.hooksPath` 으로 자동 설정. 매 v9 (2024-2026) 부터 매 거의 zero-config: `npx husky init` → 매 husky/_/h prepare-commit-msg 등 매 wrapper 자동 생성. 매 lint-staged 와의 매 pairing 이 매 표준 setup. ## 매 핵심 ### 매 핵심 개념 - **core.hooksPath = .husky/_**: Husky 가 매 이 path 로 git 을 redirect — 매 user 의 매 hook script 와 매 husky framework script 분리. - **Plain shell**: 매 `.husky/pre-commit` 의 매 첫 line shebang 없이 — Husky v9 가 매 `_/h` wrapper 통해 실행. - **Skip via env**: `HUSKY=0` 또는 `HUSKY_SKIP_HOOKS=1` — 매 CI 또는 emergency commit. - **prepare script**: `package.json` 의 `"prepare": "husky"` 가 매 install 시 자동 setup. ### 매 lint-staged 와의 결합 - 매 staged files 만 lint/format → 매 commit 속도 ↑. - 매 prettier --write + eslint --fix + 매 자동 re-stage. - 매 large monorepo 에서도 매 fast (only changed paths). ### 매 hook 우선순위 (자주 쓰는) 1. **pre-commit**: lint-staged + type-check (changed only). 2. **commit-msg**: commitlint (Conventional Commits 검증). 3. **pre-push**: full test suite + build smoke. 4. **post-merge**: pnpm install if `package.json` changed. ## 💻 패턴 ### Initial setup ```bash # in repo root pnpm add -D husky lint-staged pnpm pkg set scripts.prepare="husky" pnpm prepare npx husky init # creates .husky/pre-commit with `npm test` placeholder ``` ### .husky/pre-commit (lint-staged + type-check) ```sh npx lint-staged pnpm exec tsc --noEmit ``` ### lint-staged config (package.json) ```json { "lint-staged": { "*.{ts,tsx,js,jsx}": [ "eslint --fix --max-warnings=0", "prettier --write" ], "*.{json,md,yml,yaml}": ["prettier --write"], "*.css": ["stylelint --fix", "prettier --write"] } } ``` ### .husky/commit-msg (commitlint) ```sh npx --no -- commitlint --edit "$1" ``` ```js // commitlint.config.js module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'subject-max-length': [2, 'always', 100], 'scope-enum': [2, 'always', ['ui', 'api', 'docs', 'ci', 'deps']], }, }; ``` ### .husky/pre-push (test + build) ```sh pnpm test --run --silent pnpm build ``` ### Skip in CI / emergency ```bash # CI: prepare script no-op when not in dev install # package.json { "scripts": { "prepare": "node -e \"if (process.env.CI) process.exit(0)\" && husky" } } # Emergency commit (use sparingly) HUSKY=0 git commit -m "hotfix: critical patch" # or git commit --no-verify -m "..." ``` ### Monorepo: only run hooks if relevant ```sh # .husky/pre-commit CHANGED=$(git diff --cached --name-only) echo "$CHANGED" | grep -q "^apps/web/" && (cd apps/web && pnpm lint-staged) echo "$CHANGED" | grep -q "^apps/api/" && (cd apps/api && pnpm lint-staged) ``` ### Conditional hook based on branch ```sh # .husky/pre-push BRANCH=$(git rev-parse --abbrev-ref HEAD) if [ "$BRANCH" = "main" ]; then pnpm test --run pnpm build else pnpm test --run --bail=1 fi ``` ### Adopt in existing repo ```bash # After cloning, dependencies need install to run 'prepare' pnpm install # triggers `prepare` → husky sets core.hooksPath git config --get core.hooksPath # → .husky/_ ``` ### Husky + pnpm workspaces filter ```sh # .husky/pre-commit STAGED=$(git diff --cached --name-only) PKGS=$(echo "$STAGED" | awk -F/ '/^packages\// {print $2}' | sort -u) for p in $PKGS; do pnpm --filter "@repo/$p" lint-staged done ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | TS/JS project | Husky v9 + lint-staged | | Polyglot (Python, Go) | pre-commit framework (multi-lang) | | Heavy hooks (>10s) | move to pre-push, lighten pre-commit | | Solo dev hobby | optional — lint in CI alone may suffice | | Enterprise enforcement | husky + commitlint + branch protection | **기본값**: 매 TS/JS team — Husky v9 + lint-staged + commitlint. ## 🔗 Graph - 변형: [[pre-commit]] · [[lefthook]] - 응용: [[Lint-Staged]] · [[Conventional Commits]] - Adjacent: [[ESLint]] · [[Prettier]] · [[Biome]] ## 🤖 LLM 활용 **언제**: 매 hook script 작성, lint-staged config 생성, commitlint rule 제안, monorepo conditional logic. **언제 X**: 매 binary install verification — local 환경에서 매 직접 실행. ## ❌ 안티패턴 - **30s pre-commit**: 매 dev 가 매 --no-verify 습관화 → 매 hook 무용지물. - **Run full test in pre-commit**: 매 pre-push 로 옮기기. - **No CI fallback**: 매 hook 만 신뢰 → 매 --no-verify bypass 시 dirty commit. - **Husky 의 commit hook 안 비밀 커밋 검증 누락**: 매 detect-secrets / gitleaks pairing. - **Per-developer husky version drift**: 매 lockfile pin 필수. ## 🧪 검증 / 중복 - Verified (Husky v9 docs 2026, lint-staged v15+). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Husky v9 + lint-staged + commitlint patterns |