개요
최근 husky의 v5 버전이 나오면서 라이센스가 변경된 관계로(참고: https://taegon.kim/archives/10276) 코드 베이스의 git hooks를 관리하는 툴을 simple-git-hooks로 변경하는 작업을 진행했습니다. 이 글을 쓰는 시점에는 다시 husky v6 버전이 MIT 라이센스로 출시되었습니다만, 다시 husky를 이용할지는 조금 더 생각해 봐야겠습니다.
이 글은 git hooks 관리 툴 변경을 적용하면서, 관련된 여러 가지 커밋 검증 툴을 적용하는 방법에 대해 공유하려는 목적으로 정리한 글입니다.
관련 개발 툴 목록은 다음과 같습니다.
- simple-git-hooks: https://github.com/toplenboren/simple-git-hooks
- lint-staged: https://github.com/okonet/lint-staged
- commitlint: https://github.com/conventional-changelog/commitlint
simple-git-hooks
simple-git-hooks는 node 로 작성된 git hooks 관리 툴입니다. package.json
에 다음과 같이 각 git hooks에 들어갈 내용을 기술하고 simple-git-hooks
스크립트를 실행하면 해당 내용을 .git/hooks
에 파일로 작성해 주는 툴입니다.
참고로 .git/
디렉토리는 git repository에서 관리할 수 없기 때문에, 간편한 git hooks 공유를 위해서 이러한 툴이 필요합니다.
// package.json
... 생략
"scripts": {
// ...생략
"simple-git-hooks": "simple-git-hooks",
"postinstall": "npm run simple-git-hooks",
// ...생략
},
// ...생략
"simple-git-hooks": {
"pre-commit": "npm run lint-staged",
"commit-msg": "npm run commitlint"
},
// ...생략
package.json 파일에 위 내용을 기술하고 나서 .git/hooks에 pre-commit 훅 파일과 commit-msg 훅 파일을 생성하려면 터미널에서 다음과 같은 명령을 실행합니다.
npm run simple-git-hooks
실수로 이 명령을 실행하지 않으면, 훅 파일이 생성되지 않아서 훅이 동작하지 않습니다. 그래서 git clone
후에 필수로 실행해야 하는 npm install
명령 이후, 자동으로 훅을 생성하도록 postinstall
스크립트도 함께 작성해주었습니다.
실제 생성된 .git/hooks/pre-commit
파일을 확인해보면 다음과 같습니다.
#!/bin/sh
npm run lint-staged
이제 pre-commit 훅이 설정되었으므로, 앞으로는 사용자가 git commit 명령을 실행하면 pre-commit 훅이 명령을 가로채서 npm run lint-staged를 실행하고 나서, 실제 git commit을 실행합니다.
lint-staged
lint-staged
는 git repository에서 staging 상태인 파일들에 대해서만 lint를 해주는 툴입니다.
보통 lint 스크립트는 eslint --fix src/
와 같이 전체 소스코드를 대상으로 동작하도록 만드는데, commit 할 때마다 전체 소스 코드에 대해 eslint를 실행시키는 것은 실행 시간이 길어지기 십상이라 개발자 경험에 심각한 악영향을 끼칩니다.
lint-staged
는 staging 상태인 파일 경로를 설정된 명령어에 argument로 전달하기 때문에, commit에 포함되는 파일만 정확히 검사가 가능해집니다.
설치 방법은 다음과 같습니다.
npm install --save-dev lint-staged
글에서 적용하고 있는 git repository는 typescript를 사용하는 관계로 package.json에 다음과 같이 설정을 추가했습니다.
// package.json
// ...생략
"scripts": {
// ...생략
"lint-staged": "lint-staged",
// ...생략
},
// ...생략
"lint-staged": {
"{src,test}/**/*.ts": "eslint --fix"
},
이렇게 설정하면 npm run lint-staged
스크립트는 git staging 상태인
src/
혹은 test/
하위의 .ts
문자열로 경로가 끝나는 어떤 파일에 대해 eslint --fix <해당 파일 경로>
를 실행하는 스크립트가 됩니다.
여기까지 설정했다면, 이전의 .git/hooks/pre-commit
과 연계되어, 커밋을 하기 전에 커밋에 들어가는 파일들에 대해서 eslint --fix
동작을 실행하는 것이 git repository에 설정된 것입니다.
commitlint
commitlint는 커밋 메시지에 대해서 lint를 할 수 있게 해주는 툴입니다.
깔끔한 커밋 메시지와 함께 git history를 관리하면 유지 보수에 큰 도움이 됩니다.
또한, 커밋 메시지를 규격화해서 작성하면 커밋 메시지를 바탕으로 CHANGELOG
나 Release Notes
를 자동으로 작성하게 할 수도 있고, semver
같은 툴을 이용하면 커밋 메시지에 따라 자동으로 버전을 올려주는 커밋이 생성되는 기능 등을 툴로 구현할 수 있게 됩니다.
반대로 말하면 이러한 툴들이 올바르게 동작하게 만들려면 커밋 메시지에 오타가 있는지, 규격에 맞게 작성되었는지 등을 검사해야 합니다.
이때 필요한 툴이 commitlint
입니다. 설치 방법은 다음과 같습니다.
npm install --save-dev @commitlint/cli @commitlint/config-conventional
루니버스 개발팀은 커밋 메시지 작성 시 conventional commits
(https://www.conventionalcommits.org/ko/v1.0.0-beta.4/) 규격을 따르고 있어서, 해당 규격에 맞는 preset을 함께 설치했습니다.
package.json
수정사항은 다음과 같습니다.
// ...생략
"scripts": {
// ...생략
"commitlint": "commitlint -e",
// ...생략
},
// ...생략
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
// ...생략
여기까지 설정했다면, 이제부터는 git commit 시 .git/COMMIT_EDITMSG 파일에 커밋 메시지가 규격에 맞지 않게 작성된 경우 다음과 같이 오류가 발생하게 됩니다. (참고: -e 옵션에 값을 주면 다른 경로를 지정할 수 있습니다.)
❯ git commit -m "foo: bar"
> test-service@1.0.0 lint-staged
> lint-staged
ℹ No staged files match any configured task.
> test-service@1.0.0 commitlint
> commitlint -e
⧗ input: foo: bar
✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]
✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
commitlint의 자세한 커밋 메시지 검증 룰은 https://www.npmjs.com/package/@commitlint/config-conventional 을 참고하면 되고, 커스터마이징이 필요한 경우 https://commitlint.js.org/ 를 참고하면 됩니다.
맺음말
lint 툴을 이용해 엄격한 룰을 적용하여 repository 내 코드 스타일을 통일하면, 코드 리뷰가 쾌적해집니다.
코드 리뷰 시에는 코드뿐만 아니라 커밋 메시지 등도 코드 리뷰 대상에 포함되기 때문에, 지금까지 설명했던 커밋 검증 툴을 이용해 커밋 시 자동으로 한 번씩 검증하도록 설정해 둔다면, 사소한 이슈로 왈가왈부하는 일이 줄어들어, 시간과 감정 소모를 줄이고 코드 리뷰를 진행할 수 있을 것입니다.