JS 개발 환경에서 커밋 컨벤션 강제하기 (Conventional Commits with commitlint 👻)

커밋 메세지를 작성할 때, 커밋 메세지가 어떤 작업을 의미하는지 확실하게 하기 위해서 커밋 메세지에 prefix로 feat, chore, fix 같은 단어를 붙이는 경우가 많다.

이는 Conventional Commit 이라고 불리는데, 꼼꼼하게 커밋 메세지를 작성하는 유용한 방법이다.

https://www.conventionalcommits.org/en/v1.0.0/

 

Conventional Commits

A specification for adding human and machine readable meaning to commit messages

www.conventionalcommits.org

 

JS 개발 환경에서는 더 나아가 lint로 이 커밋 컨벤션을 강제할 수도 있는데 이번엔 commitlint를 사용하여 JS 개발 환경에서 커밋 메세지를 강제하는 방법에 대해서 알아보았다.

 

https://commitlint.js.org/

 

commitlint

commitlint Lint commit messages helps your team adhere to a commit convention

commitlint.js.org

 

commitlint를 사용하면 실제로 커밋 컨벤션에 어긋나는 커밋 메세지를 사용했을 때, 에러가 나도록 설정할 수 있다.

 

일단, commitlint를 설치해준다. 

npm install --save-dev @commitlint/{cli,config-conventional}

 

cli와 커밋 컨벤션 설정을 위와 같이 설치할 수 있다.

 

그리고 공식 문서에 따르면, 다음과 같은 스크립트를 실행하도록 안내하고 있는데

echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

 

이는 commitlint.config.js라는 파일에 다음과 같이 코드를 작성하도록 해준다.

(.commitlintrc나 .commitlintrc.json 등 다른 이름을 사용할 수도 있다)

module.exports = {
  extends: ['@commitlint/config-conventional'],
};

 

커밋 컨벤션에 대한 설정을 따로 하지 않고, 설치한 설정 파일을 연결해준 것이다.

직접 커밋 메세지 룰을 설정하는 방법은 공식문서를 참조하자.

 

이렇게 설정하면 커밋 컨벤션이 작동하도록 설정은 된 것인데, 개발 흐름상 실제 커밋을 진행할 때 commitlint가 실행되어 커밋 메세지를 검증할 수 있도록 해야한다.

 

때문에, husky를 함께 사용하는 경우가 많다.

https://typicode.github.io/husky/get-started.html

 

Get started | Husky

 

typicode.github.io

 

npm install --save-dev husky

 

설치 후 init해준다.

npx husky init

 

이제 실제 커밋 메시지가 커밋 시 검증될 수 있도록 .husky/commit-msg에 commitlint 실행 스크립트를 작성해주자.

 

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

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

 

현재 실행 중인 스크립트($0)가 있는 디렉터리에서 npx가 모듈을 실제로 설치 하지 않도록 하며(--no-install) commitlint를 실행한다.

--edit 옵션은 커밋 메시지 파일을 편집하여 검사하게 하는데, $1은 첫 번째로 작성된 인자를 의미한다.

즉, Git 훅에서 이 스크립트가 실행될 때, $1은 커밋 메시지를 의미하게 된다.

 

이렇게 husky까지 설정이 끝났다면, 실제 잘못된 커밋 스크립트를 작성해보자.

git commit -m 'test'

 

에러와 함께 커밋이 실패하고, 친절하게 커밋 컨벤션을 알려주는 링크를 보여준다.

https://github.com/conventional-changelog/commitlint/#what-is-commitlint

 

GitHub - conventional-changelog/commitlint: 📓 Lint commit messages

📓 Lint commit messages. Contribute to conventional-changelog/commitlint development by creating an account on GitHub.

github.com

 

다만, 이제 직접 커밋 메세지를 작성하면 feat: fix: chore: 와 같은 prefix를 작성해줘야 한다.

만약 오타라도 나면 짜증날 것 같기에, 이를 자동화해서 prefix를 직접 작성하지 않고 처리해주는 스크립트를 작성하면 어떨까? 하는 생각이 들어 직접 작성하였다.

 

 

zx와 inquirer를 사용해서 가능한 커밋 컨벤션을 선택하고 메세지를 작성하도록 하는 스크립트를 작성했다.

다음의 스크립트를 통해서 커밋 메세지를 작성하면, 커밋 컨벤션에서 실수를 할 일은 없을 것 같다 🙌

import { $, quote } from "zx";
import inquirer from "inquirer";
const promptCommitMessage = async () => {
  return inquirer.prompt([
    {
      type: "list",
      name: "type",
      message: "커밋 타입을 선택하세요:",
      choices: [
        { name: "feat: 새로운 기능", value: "feat" },
        { name: "fix: 버그 수정", value: "fix" },
        { name: "docs: 문서 수정", value: "docs" },
        { name: "style: 코드 스타일 수정", value: "style" },
        { name: "refactor: 코드 리팩터링", value: "refactor" },
        { name: "test: 테스트 추가/수정", value: "test" },
        { name: "chore: 기타 변경사항", value: "chore" },
      ],
    },
    {
      type: "input",
      name: "scope",
      message: "영향받는 범위(scope)를 입력하세요 (예: 컴포넌트 이름, 생략 가능):",
      validate: (input) =>
        input.length <= 10 ? true : "범위는 최대 10글자까지만 입력하세요.",
    },
    {
      type: "input",
      name: "subject",
      message: "변경 사항의 요약을 입력하세요:",
      validate: (input) =>
        input.length > 0 && input.length <= 100
          ? true
          : "요약은 1글자 이상, 100글자 이하로 입력하세요.",
    },
  ]);
};
// main 실행
(async () => {
  const { type, scope, subject } = await promptCommitMessage();
  const scopeStr = scope ? `(${scope})` : "";
  const commitMessage = `${type}${scopeStr}: ${subject}`;
  console.log("\n📝 생성된 커밋 메시지:");
  console.log(`  ${commitMessage}\n`);
  const { confirmCommit } = await inquirer.prompt({
    type: "confirm",
    name: "confirmCommit",
    message: "이 메시지로 커밋하시겠습니까?",
  });
  if (confirmCommit) {
    await $`git commit -m ${commitMessage}`;
    console.log("🚀 커밋 완료!");
  } else {
    console.log("⚠️  커밋이 취소되었습니다.");
  }
})();

 

해당 스크립트를 실행하여 커밋을 하면, 다음과 같이 prefix를 선택할 수 있고 커밋 컨벤션에서 실수를 할 일은 없을 것 같다 😊

 

위와 같이 git add .와 해당 스크립트 코드를 실행하도록 묶은 뒤 실행하면, 오타로 인해서 커밋 컨벤션이 어긋나는 일은 없을 것 같다.

 

 

 🙌 실제 작성한 코드

https://github.com/citron03/practice-next-15/commit/b9e4b945fcb5870d2a51a1803b4af4ec5b179cc6

 

feat: make commit convention lint setting & add commit helper script · citron03/practice-next-15@b9e4b94

citron03 committed Jan 21, 2025

github.com