대충 커밋하지 않기 위해 Git Husky를 도입했다

정주·2026년 1월 5일

프로젝트

목록 보기
2/3

들어가며

프로젝트마다 커밋 컨벤션을 정해두고도, 프로젝트가 끝나고 커밋 로그를 보면 항상 띄어쓰기나 기호 같은 작은 디테일이 무너져 있는 사람이 있다. 그게 바로 나다.

이를 고치기 위해 팀 노션을 항상 열어두고 커밋을 작성했지만, 솔직히 너무 번거로웠다.

누가 강제로 커밋 좀 체크해 줬으면 좋겠다!

그래서 개인 프로젝트지만 일관적으로 커밋하는 습관을 갖기 위해서 깃허스키를 사용해 커밋 메세지를 컨벤션을 강제하고자 한다.

Git Husky

  • Git Husky
    • Git hook을 쉽게 설정하고 실행할 수 있도록 도와주는 도구이다.
  • Git hook
    • Git에서 특정 이벤트가 발생했을 때 자동으로 실행되는 스크립트이다.
    • 주로 사용되는 hook은 다음과 같다
      • pre-commit : 커밋 시작하기 바로 직전에 실행됨 → 코드 검사, 포맷팅, 테스트 실행 등에 사용
      • commit-msg: 커밋 메세지가 작성된 직후 실행됨 → 보통 커밋 메세지 검증에 사용함
      • pre-push : 푸시 직전에 실행됨

왜 나는 깃 허스키를 선택했는가?

커밋 직전, 커밋 메시지 작성 직후, 푸시 직전 등 여러 시점에서 자동으로 검증 로직을 실행할 수 있기 때문이다.

개인 프로젝트이지만 일관된 기준을 강제로 지키는 환경을 만들어보고 싶었다!

커밋 메세지 컨벤션 적용하기

1. 설치하기

    npm install --save-dev husky  

2. 허스키 초기화

   npx husky install 

3. .husky/commit-msg 파일 생성

#!/bin/sh 
. "$(dirname "$0")/_/husky.sh"

node .husky/validate-commit.mjs $1

한 줄씩 알아보기

  • #!/bin/sh

    • “이 파일은 sh(shell) 로 실행해야 한다”는 뜻이다
    • Git 훅은 ‘스크립트 파일’이라 커밋 전에 자동으로 실행되기 때문에 저 선언문이 없으면 실행되지 않거나 오류가 날 수 있다
  • . "$(dirname "$0")/_/husky.sh”

    • 허스키 내부 스크립트에서 불러오는 코드이다
    • Husky v9에서는 필수 설정으로 hook 실행에 필요한 환경을 세팅한다.
  • node .husky/validate-commit.mjs $1

    • validate-commit.mjs를 실행해서 커밋 메시지가 규칙에 맞는지 검사한다.

    • $1커밋 메시지가 저장된 파일 경로를 뜻한다.

      Git은 커밋 메시지를 작성할 때 임시로 .git/COMMIT_EDITMSG 파일에 저장한다

      따라서 깃은 커밋 메세지를 .git/COMMIT_EDITMSG 파일에 기록하고, commit-msg 훅 실행 시 해당 파일의 경로를 첫 번째 인자로 전달한다.

4. 권한 부여

// 터미널에 입력해야된다
chmod +x .husky/commit-msg

Git은 훅(hook) 파일이 “실행 가능한 파일(executable)”일 때만 자동으로 실행해준다.

만약 실행 권한이 없으면 이 파일은 그냥 텍스트 파일이라서 아무것도 실행되지 않는다.

따라서 실행 권한을 부여해야 한다.

5. ./validate-commit.mjs 파일 생성

import fs from 'fs'

const msgFile = process.argv[2]
const msg = fs.readFileSync(msgFile, 'utf-8').trim()

const commitRegex = /^(feat|fix|docs|style|refactor|test|chore)(\(#\d+\)): .+$/

if (!commitRegex.test(msg)) {
  console.error(`
❌ 커밋 메시지 규칙 위반!

형식:
  작업타입(#이슈번호): 작업 내용

예시:
  feat(#1): 로그인 로직 추가
  fix(#2): API 오류 수정
`)

  process.exit(1) // 실패 → 커밋 중단
}

한 줄씩 알아보기

  • import fs from 'fs'
    • fs 는 Node.js 기본 모듈 "File System"로, 파일 읽기, 쓰기, 삭제가 가능하다.
  • const msgFile = procress.argv[2]
    • Git 커밋 → Husky → validate-commit.mjs 로 실행된다.
      • 실행순서
        • process.argv[0] → node 실행 경로 ⇒ (/usr/local/bin/node)

        • process.argv[1] → 실행 중인 스크립트 파일 경로 ⇒ (scripts/validate-commit.mjs)

        • process.argv[2] → git이 전달한 커밋 메세지 파일 경로 ⇒ (.git/COMMIT_EDITMSG)

          → 이미 commit-msg에서 node .husky/validate-commit.mjs $1 로 스크립트 파일 경로를 넘겼기 때문에 node.js로 넘어오면 argv[2]가 된다.

    • 왜 process는 import하지 않는가?
      • Node.js에서 기본으로 제공하는 전역 객체이기 때문에 import가 필요 없다.
      • Node에서 기본 제공하는 전역 객체들 : process , __dirname , __filename , Buffer , global
  • if (!commitRegex.test(msg)) 에서 test를 import 안 했는데 어떻게 바로 사용가능하지?
    • test는 전역 변수인가? 아니다. commitRegex 가 RegExp 객체이다.
      • RegExp객체란?
        • 자바스크립트에서 정규 표현식(Regular Expression)을 다루는 객체이다. 커밋 메세지가 컨벤션에 맞는지 확인하는 것처럼 문자열에서 특정 패턴을 찾거나 검증할 때 사용한다.

        • RegExpnew 생성자를 사용하지 않고 /.../ ← 리터럴 방식으로 생성이 가능하다.

          따라서 commitRegex 는 리터럴 방식으로 변수값을 할당했기 때문에 RegExp 객체이다.

        • RegExp 에는 test 뿐만 아니라 다양한 메서드가 있으나 주로 test , exec() 를 사용한다.

          • test()true/false → 간단한 패턴 검증 시 주로 사용
          • exec()매치된 내용 배열 반환, 매치된 내용이 없으면 null 반환 → 더 상세한 정보 필요할 때 사용

커밋 메세지 컨벤션을 “ 작업타입(#이슈번호): 작업내용”으로 설정한 이유

우선 깃허브에서 이슈번호를 커밋에 넣으면 추적이 가능하기 때문에 추후 코드 리뷰 등 유지 보수 하기에 적합할 것이라고 생각했다.
가장 큰 이유는 개인 프로젝트라도 “작업타입”을 명시하면서 내가 작업한 부분을 분류하고 일관된 커밋 습관을 형성하기 위해서이다.

실행결과

규칙을 지키지 않으면 커밋 단계에서 에러가 발생하며, 커밋은 중단된다!

마치며

직접 스크립트를 만드는 방법도 있지만 라이브러리를 사용할 수 있다!

  • @commitlint/cli : 커밋 메시지 체크
  • @commitlint/config-conventional : 기본 컨벤션 규칙

참고자료

https://www.notion.so/husky-233ec690dcc680c2a8b7de3ee1cb142a

profile
💡프론트엔드 공부 기록

0개의 댓글