코드의 가독성을 높이고 실수를 미연에 방지하기 위해 linter와 formatter를 사용한다. 에디터의 익스텐션을 사용하면 손쉬운 설정으로 쾌적한 코딩 환경을 만들 수 있다.
하지만 협업을 해야 한다면 이야기가 달라진다. 나에게 편하고 익숙한 환경이 누군가에게는 어질어질하게 보일 수 있고, 서로 다른 linter는 각종 에러와의 사투를 유발할 수 있다. 이러한 불편을 미연에 방지하기 위해서 팀 단위로 논의하여 미리 lint와 formatter 규칙을 정한다.
대표적인 linter인 eslint
와 대표적인 formatter인 prettier
를 설치한다.
npm install -D eslint prettier eslint-config-prettier
eslint-config-prettier
는 eslint
와 prettier
의 설정 충돌을 방지해준다. 예를 들어, prettier
는 singleQuote를 사용하지만 eslint
는 금지한다면 둘이서 무한으로 즐길 것이다.
설정 파일을 만들어 필요한 규칙과 설정을 추가한다. 작성 양식은 파일 확장자에 따라 다른데, 각각의 공식문서에서 확인할 수 있다.
eslint - Configuration File Formats
prettier - Configuration File
// .eslintrc
{
// eslint가 es6를 파싱할 수 있게 설정
"env": { "es6": true },
"rules": {
// var 선언을 허용하지 않음
"no-var": "error",
// allow 메서드를 제외한 console 객체를 허용하지 않음
"no-console": ["error", { "allow": ["warn", "error", "info"] }]
}
}
// .prettierrc
{
// 줄 간격 100
"printWidth": 100,
// 작은따옴표 허용
"singleQuote": true,
// 화살표 함수에서 인자가 하나면 괄호 안함
"arrowParens": "avoid"
}
간단하게 이러한 옵션을 설정한 후 테스트해 보자. 테스트할 파일 하나를 만든다.
// index.js
var hello = "world";
var end = (x) => {
console.log("bye, world");
};
먼저 prettier
를 실행한다.
# prettier
npx prettier --write .
# result
.eslintrc 142ms
.prettierrc 17ms
index.js 30ms
package-lock.json 241ms
package.json 61ms
정리된 파일은 다음과 같다.
var hello = "world";
var end = (x) => {
console.log("bye, world");
};
설정에 따라 넓던 간격이 줄어들었고, 큰따옴표가 작은따옴표로 변경되었다. 또한 화살표 함수의 인자도 괄호가 벗겨졌다.
prettier
가 정상적으로 실행되었으니 eslint
를 확이해 보자.
# eslint
npx eslint .
# result
1:1 error Unexpected var, use let or const instead no-var
3:1 error Unexpected var, use let or const instead no-var
4:3 error Unexpected console statement no-console
✖ 3 problems (3 errors, 0 warnings)
예상대로 var
와 console
을 지적하는 에러가 발생했다. 수정한다면 아무것도 출력하지 않고 종료한다.
파일이 몇 개 되지 않을 때는 상관없지만, 그 양이 많아진다면 매번 모든 파일을 검사하는 것은 비효율적이다. 파일이 변경되었을 때만 적용되도록 --cache
옵션을 사용한다.
# prettier
npx prettier --write --cache .
# result
.eslintrc 6ms (cached)
.lintstagedrc 3ms (cached)
.prettierrc 3ms (cached)
index.js 3ms (cached)
package-lock.json 3ms (cached)
package.json 3ms (cached)
# eslint
npx eslint --cache . # .eslintcache가 생성됨
코드 작성을 끝낸 후 매번 CLI를 입력하기란 번거롭다. scripts
에 명령어를 등록하면 손쉽게 실행할 수 있다.
// package.json
{
"scripts": {
"format": "prettier --write --cache .",
"lint": "eslint --cache --max-warnings=0 ."
}
}
등록한 명령어는 npm run format
과 npm run lint
로 실행한다. scripts
내에서는 npx
없이 모듈명으로 실행할 수 있다. 추가로, lint
의 --max-warnings=0
은 경고 표시조차 없어야 통과시킨다.
명령어가 단순해졌지만, 이 역시 commit 전, push 전에 입력하기란 불편하고 비효율적이다. 이것은 git hook
을 이용해 자동화할 수 있다.
.git
폴더 안에는 git hook
을 위한 파일이 존재한다.
# .git/hooks
$ dir -l
applypatch-msg.sample
commit-msg.sample
fsmonitor-watchman.sample
post-update.sample
pre-applypatch.sample
pre-commit.sample
pre-merge-commit.sample
prepare-commit-msg.sample
pre-push.sample
pre-rebase.sample
pre-receive.sample
push-to-checkout.sample
update.sample
직접 수정하여 사용할 수 있지만 과정이 복잡하다. 이를 위한 편의성 라이브러리가 husky
이다. npm install -D husky
로 설치한다.
husky
에 hook을 등록하기 전에 husky install
을 실행하여 초기화해야 한다. 그러면 .husky
디렉토리가 생기고, 그 내부에 hook이 추가된다. 여기서 주의할 점은 패키지에 명시되어 있다고 해서 다른 팀원이 npm install
했을 때 자동으로 설치되지 않는다. 이런 점은 postinstall
스크립트로 해결할 수 있다.
// package.json
{
"scripts": {
"postinstall": "husky install"
}
}
postinstall
은 말그대로 npm install
이 종료된 후 실행되는 명령어이다. npm install
해보면 모든 설치가 끝난 후 위 스크립트가 실행되는 것을 볼 수 있다.
husky
를 설치했다면 pre-commit
과 pre-push
hook을 등록할 차례다. commit 전에는 prettier
를 실행해 코드를 정리하고, push 전에는 eslint
를 거쳐 에러를 잡아내도록 하자.
npx husky add .husky/pre-commit "npm run format"
npx husky add .husky/pre-push "npm run lint"
.husky
디렉토리 안에 pre-commit
과 pre-push
파일이 생성된다.
테스트를 위해 commit을 해보자.
$ git add .
$ git commit -m "prettier test"
> eslint-prettier-husky@1.0.0 format
> prettier --write --cache .
.eslintrc 2ms (cached)
.prettierrc 2ms (cached)
index.js 48ms
package-lock.json 1ms (cached)
package.json 1ms (cached)
[master d1de691] prettier test
commit이 되기 전에 prettier가 실행되었다. push도 확인해 보자
$ git push origin master
> eslint-prettier-husky@1.0.0 lint
> eslint --cache --max-warnings=0 .
D:\work-space\index.js
7:1 error Unexpected console statement no-console
✖ 1 problem (1 error, 0 warnings)
husky - pre-push hook exited with code 1 (error)
error: failed to push some refs to 'https://github.com/Real-Bird/linter-test.git'
eslint가 에러를 잡아내고 push하지 않았다. 에러 수정 후 다시 push하자.
$ git push origin master
> eslint-prettier-husky@1.0.0 lint
> eslint --cache --max-warnings=0 .
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 619 bytes | 68.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/Real-Bird/linter-test.git
39eb53b..7f8806d master -> master
성공적으로 push되었다!
하지만 여기에는 함정이 있다. pre-commit
하면서 formatter가 동작하지만, 수정된 파일이 스테이징되지 않는다. 이런 문제를 도와주는 라이브러리가 lint-staged
이다. npm install -D lint-staged
로 설치하고, config를 설정한다. 패키지에 "lint-staged"
를 추가하거나, 파일을 만든다. 여기서는 파일을 만들어 작성했다.
// .lintstagedrc
{
{
"*.js": "npm run format"
}
}
js
파일만 포맷팅을 거친다. 다양한 옵션들은 okonet/lint-staged에서 확인할 수 있다.
.husky/pre-commit
파일 내 npm run format
을 npx lint-staged
로 수정한다.
. "$(dirname -- "$0")/_/husky.sh"
# npm run format # remove
npx lint-staged
commit을 해 보면 포맷팅 후 스테이징까지 하는 모습이 보인다. 이것으로 협업을 위한 기본적인 자동화가 끝났다.
원티드 프리온보딩 인턴십 11차에 참여하여 배운 것을 복습하며 정리해 봤다. 강사님이 "자동화에 미친 개발자도 있다"라고 하셨는데, 이련 효율성 때문에 그런 사상(?)을 갖게 되는 것 같다. 혼자 개발하면서 익스텐션만 사용하니 별 생각 없었는데, 이번을 계기로 자동화에 대해서도 고려해 봐야겠다.