본문 바로가기
웹 프론트엔드/Git

깃 커밋 컨벤션 VSCode에 적용하기

by canoe726 2022. 7. 9.
728x90

깃 커밋 컨벤션 (Git Commit Convention) ?

개인 프로젝트를 하거나 여러 사람들과 협업할 때 무조건 커밋로그를 남기게 되어있습니다. 사람마다 커밋을 작성하는 방식이 모두 다릅니다.

 

서로 의미를 정하지 않은 의미의 이모지를 넣거나, 무조건 소문자로 쓰거나, fix code 와 같이 매우 추상적으로 작성하는 경우도 있습니다.

 

그렇게 작성하다 나중에 특정 커밋을 취소하거나 누가 어떤 파일 수정하였는지 추적하고 싶을때가 종종 생깁니다. 하지만 작성되어 있는 커밋로그가 fix code, refactor code 의 연속이라면..? (절망에 빠지게 됩니다.)

 

그런 상황을 방지하고 원활한 협업을 위해서는 커밋 컨벤션이 필요합니다!

 

 

그럼 어떤 컨벤션을 선택해야할까?

제가 위와 같은 질문을 받았다면 "상황이나 취향에 따라 다릅니다!" 라고 답변했을 것 같네요.

 

세상에는 여러 커밋 컨벤션이 존재합니다. 그 중에서 두 가지를 소개하겠습니다.

 

1. Conventional Commits

저는 여러 컨벤션을 리서치 한 결과 conventional commits 가 실제로 사용하기 괜찮다고 생각했습니다.

 

그 이유는 두 가지 정도로 요약됩니다.

 

1) 외워야할 규칙이 적다 (자주쓰는 feat, fix, refactor, test 정도만 알아도 문제 없습니다)

 

2) 따로 설치할 필요가 없다

 

 

실제로 사용하는 방법도 매우 간단합니다. 실제 규칙은 다음과 같습니다.

 

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

 

-  접두사 목록 요약본

  • feat : 새로운 기능 추가
  • fix : 버그 수정
  • docs : 문서의 수정
  • style : (코드의 수정 없이) 스타일(style)만 변경(들여쓰기 같은 포맷이나 세미콜론을 빼먹은 경우)
  • refactor : 코드를 리펙토링
  • test : Test 관련한 코드의 추가, 수정
  • chore : (코드의 수정 없이) 설정을 변경
  • ! : 급한 변경 사항인 경우에 추가 (접두사, () 뒤 / 콜론 이전)
  • () : 추가 요약 정보가 필요할 경우 (접두사 뒤 / !, 콜론 이전)
  • BREAKING CHANGE : 급한 변경 footer에 추가

 

버튼 컴포넌트를 프로젝트에 추가한다고 했을 때 커밋 예시는 다음과 같습니다.

 

feat: 버튼 컴포넌트 생성
...
fix: 버튼 컴포넌트에 투명색 조절 속성 추가
...
refactor: destructuring assignment 연산자로 클린코드 작성
...
test: 버튼 단위 테스팅
...

 

optional body, optional footer 는 hotfix 와 같은 긴급 수정이 발생했을 때 설명을 적거나 깃허브 PR 을 할 때 자동생성되는 글자 이외에는 많이 사용할일이 없겠다고 생각했었습니다.

 

 

 

Conventional Commits

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

www.conventionalcommits.org

 

2. gitmoji

두 번째로는 깃모지입니다. 유명하기도 하고 일단 심미적으로 매우 좋습니다.

 

제가 깃모지를 사용해서 깃 커밋을 작성하지 않는 이유도 2가지 입니다.

 

1) npm 설치를 해야한다. (npm install gitmoji-cli)

 

2) 이모티콘이 너무 많아서 딱 보고 어떤 수정을 하였는지 파악하기 힘들 것 같다.

 

 

gitmoji

:truck: Move or rename resources (e.g.: files, paths, routes).

gitmoji.dev

 

이렇게 커밋 컨벤션이 어떤 종류가 있는지 한번 알아보았습니다. 그렇다면 글의 메인 주제인 자동화에 대해서 이야기 해보려고 합니다.

 

 

깃커밋 자동화 할 수 없을까?

모두 머리로는 이해하고 있지만 실제로 행동하는 것은 전혀 다른 이야기입니다.

 

커밋 컨벤션 유효성 검증 자동화 프로그램이 필요한 순간입니다.

 

VSCode에서 커밋을 하려는 순간 커밋 컨벤션이 맞지 않으면 커밋을 하지않고 무엇을 고쳐야하는지 개발자에게 메시지를 출력해주는 프로그램이 필요합니다.

 

저는 husky + bash 코드 조합으로 위 문제를 해결하였습니다.

 

 

husky는 무슨 도구?

husky는 깃의 특정 워크플로우에 원하는 명령어들을 실행할 수 있게 도와주는 도구입니다.

 

husky 에 대해서 알아보기 전에 깃을 사용할 때 특정 워크플로우 (pre-commit, commit-msg, pre-rebase ...) 에서 갈고리(hooks)을 걸어 원하는 스크립트를 실행하는 git-hooks 에 대해서 알고 있으면 적용하기가 수월합니다. 

 

 

저는 커밋 메시지가 올바르지 않으면 수정한 파일들이 stage 상태에만 머물러 있게 만들어 commit과 push 일련의 명령어를 수행하지 하지 못하도록 만들고 싶었습니다.

 

VSCode 에서 `Ctrl+Enter` 또는 `git commit -m "..."` 명령어를 수행했을 때 검사를 수행할 수 있는 워크플로우는 commit-msg 입니다.

 

이제 사용할 도구와 특성을 모두 파악했으니 코드를 작성하면 되겠네요!

 

 

Q. gitmoji는 npm 설치때문에 선호하지 않는다고 했는데 설치가 필요없는 git-hooks 대신 왜 husky는 설치해서 사용하셨나요?

A. git-hooks 는 루트 디렉토리에서 .git/hooks/ 디렉토리 내에 특정 워크플로우에 실행할 파일을 모두가 동일하게 가지고 있어야 하는데 실수로 파일을 넣지 않은 경우 문제가 됩니다.

 

(.git 디렉토리는 git commit에 포함되지 않습니다. 수동으로 옮겨야하고 관리가 어렵습니다.)

 

하지만 npm 패키지 관리자를 사용해서 husky를 설치하고 원격 저장소에서 컨벤션 규칙 파일을 관리하면 모두가 동일한 컨벤션을 유지할 수 있습니다.

 

 

commit-msg.sh 파일이 핵심이다.

husky를 설치하고 아래 명령어를 수행하면 루트 디렉토리에 .husky/commit-msg 파일이 생기는데 해당 파일에 코드를 작성하면 됩니다.

 

npx husky add .husky/commit-msg

 

 

제가 작성한 commit-msg.sh 파일입니다.

 

정규표현식과 if statement를 잘 활용하면 커밋 유효성을 검증할 수 있습니다.

 

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

# 커밋 컨벤션
# 0. 검사 예외 조건 (자동 생성, 최초 커밋)
# - Merge branch*, Merge pull request*, initial*
# 1. 접두사의 글자는 소문자
# 2. 맨 마지막 글자 '.' 마침표 금지
# 3. 커밋 접두사 (규칙: '접두사' + '콜론' + ' ')
# - feat: 새로운 기능 추가
# - fix: 버그 수정
# - docs: 문서의 수정
# - style: (코드의 수정 없이) 스타일(style)만 변경(들여쓰기 같은 포맷이나 세미콜론을 빼먹은 경우)
# - refactor: 코드를 리펙토링
# - test: Test 관련한 코드의 추가, 수정
# - chore: (코드의 수정 없이) 설정을 변경
# 5. '!': 급한 변경 사항인 경우에 추가 (접두사, () 뒤 / 콜론 이전)
# 6. (): 추가 요약 정보가 필요할 경우 (접두사 뒤 / !, 콜론 이전)

COMMIT_MSG_FILE=$1
FIRST_LINE=`head -n1 ${COMMIT_MSG_FILE}`
RES="needCheck" # needCheck, auto, initial, lintError*, clear

if [[ $FIRST_LINE =~ ^(Merge branch) ]] ||
   [[ $FIRST_LINE =~ ^(Merge pull request) ]]; then
  RES="auto"
fi

if [[ $FIRST_LINE =~ ^(initial) ]]; then
  RES="initial"
fi

if [ $RES == "needCheck" ]; then
  if [[ $FIRST_LINE =~ (\.)$ ]]; then
    RES="lintError1"
  fi

  if [[ ! $FIRST_LINE =~ ^(feat(\(.*\))?!?: ) ]] &&
     [[ ! $FIRST_LINE =~ ^(fix(\(.*\))?!?: ) ]] &&
     [[ ! $FIRST_LINE =~ ^(docs(\(.*\))?!?: ) ]] &&
     [[ ! $FIRST_LINE =~ ^(style(\(.*\))?!?: ) ]] &&
     [[ ! $FIRST_LINE =~ ^(refactor(\(.*\))?!?: ) ]] &&
     [[ ! $FIRST_LINE =~ ^(test(\(.*\))?!?: ) ]] &&
     [[ ! $FIRST_LINE =~ ^(chore(\(.*\))?!?: ) ]]; then
    RES="lintError2"
  fi

  if [[ ! $RES =~ ^(lintError) ]]; then
    RES="clear"
  fi
fi

if [[ $RES =~ ^(lintError) ]]; then
  if [[ $RES == "lintError1" ]]; then
    echo "CommitLint#1: 문장 마지막의 ('.') 마침표를 제거해주세요."
  fi
  if [[ $RES == "lintError2" ]]; then
    echo "CommitLint#2: 접두사, 콜론, 띄어쓰기 형태를 확인하세요. (feat: , fix: , docs: , style: , refactor: , test: , chore: )"
  fi
  exit 1
elif [[ $RES == "auto" ]]; then
  echo "Automatically generated commit message from git"
elif [[ $RES == "initial" ]]; then
  echo "Initial commit"
elif [[ $RES == "clear" ]]; then
  echo "Pass commit lint!"
fi

exit 0

 

+ gitflow를 사용해서 브랜치명을 초기화하지 않거나 git 에서 merge requests 기능을 사용하지 않는 경우에 해당 코드를 적용하면 오류가 발생할 수 있습니다. (적절하게 고쳐서 사용하면 됩니다.)

 

 

이 코드를 적용하고 VSCode에서 `Ctrl+Enter` 또는 커밋 푸쉬 버튼을 누르면 다음과 같이 에러를 찾아내는 것을 확인할 수 있습니다.

 

 

Test Case.

- 의미가 불분명한 접두사를 사용한 경우

- 문장의 마지막에 마침표를 찍은 경우

- 접두사를 사용하지 않은 경우

- 접두사가 올바르지 않게 입력된 경우

 

 

위와 같이 잘 작동하는 것을 확인할 수 있습니다.

 

프로젝트에 공통적으로 적용할 수 있는 컨벤션 자동화 스크립트가 있다면 동료들에게 👍 LGTM을 받을 수 있겠군요

 

 

- husky에 대해서 더 알고 싶다면?

 

1. 공식문서

 

Husky - Git hooks

 

typicode.github.io

 

2. 가비아 기술블로그

 

가비아 라이브러리

IT 콘텐츠 허브

library.gabia.com

 

3. 우아한형제들 기술블로그 (git hooks)

 

 

훅으로 Git에 훅 들어가기 | 우아한형제들 기술블로그

{{item.name}} 들어가며… 안녕하세요. 우아한형제들 CTO실 주문시스템개발팀의 라태웅입니다. 요새 Git은 어느 조직이건 개인이건 많이 사용하고 계신데요, 굉장히 많은 기능이 있죠. 이중 몰라도

techblog.woowahan.com

 

728x90

댓글