ESLint로 소스 코드에 숨어있는 문제 찾기
최근에 가장 많이 쓰이는 자바스크립트 린터(linter)인 ESLint에서 대해서 알아보겠습니다.
Lint? ESLint?
프로그래밍에서 린트(lint)라는 개념이 생소한 분들을 위해서 lint가 무엇인지에 대해서 먼저 짚고 넘어가겠습니다. 린트(lint)는 소스 코드에 문제가 있는지 탐색하는 작업을 의미하며, 린터(linter)는 이 작업을 도와주는 소프트웨어 도구를 의미합니다. 자바스크립트와 같이 컴파일 과정이 없는 인터프리터 언어의 경우, 런타임 에러가 발생할 확률이 높기 때문에, 이 린트 작업을 통해 사전에 에러를 최대한 잡아주는 것이 중요합니다. 예전에는 JSLint나 JSHint와 같은 린터들이 많이 사용되었으나, 최근에는 ESLint라는 린터가 개발자들 사이에서 많은 인기를 끌고 있습니다. 현재는 Facebook, Airbnb를 비롯해 Netflix, MongoDB, Paypal, Disqus 등 수많은 기업들이 ESLint를 자바스크립트 린터로 사용하고 있습니다.
ESLint 설치 및 기본 설정
먼저 실습을 원하는 경로에서 빈 NPM 프로젝트를 하나 생성하고 eslint
라는 패키지를 개발 의존성으로 설치합니다.
$ npm init -y
$ npm i -D eslint
설치가 끝나면 eslint
실행 파일이 node_modules
디렉터리 안에 생성될 것입니다.
ESLint 설정 파일을 자동으로 작성하기 위해서 터미널에서 다음 커맨드를 실행합니다.
$ node_modules/.bin/eslint --init
? How would you like to use ESLint? To check syntax and find problems
? What type of modules does your project use? JavaScript modules (import/export)
? Which framework does your project use? None of these
? Does your project use TypeScript? No
? Where does your code run? Node
? What format do you want your config file to be in? JavaScript
Successfully created .eslintrc.js file in /temp/learn-eslint
그러면 ESLint 설정 파일을 만들기 위해서 여러 가지 질문들을 나올 것인데요. 본인의 프로젝트의 상황에 맞게 답변을 하시면 됩니다. 실습 프로젝트에서는 최대한 간단한 설정을 위해서 위와 같이 선택을 하였습니다.
그러면 다음과 비슷한 ESLint 설정 파일이 프로젝트 최상위 경로에 생성될 것입니다.
- .eslintrc.js
module.exports = {
env: {
es6: true,
node: true,
},
extends: "eslint:recommended",
globals: {
Atomics: "readonly",
SharedArrayBuffer: "readonly",
},
parserOptions: {
ecmaVersion: 2018,
sourceType: "module",
},
rules: {},
};
위의 설정 파일에서 가장 많이 건드리게 될 부분은 extends
와 rules
입니다.
extends
옵션은 다른 ESLint 설정을 확장해서 사용할 때 사용합니다.
여기서는 ESLint에서 추천하는 규칙들이 적용된 eslint:recommended
라는 설정이 사용되었습니다.
참고로, 실제 프로젝트에서는 ESLint 기본 설정보다는 airbnb
와 prettier
의 설정을 많이 확장해서 사용하는 것 같습니다.
rules
옵션은 본인의 프로젝트에서 자체적으로 덮어쓰고 싶은 규칙들을 정의할 때 사용합니다.
정의가 가능한 규칙들의 전체 리스트는 너무 방대하므로 ESLint의 공식 레퍼런스 참조 바랍니다.
ESLint 터미널에서 사용
실습 프로젝트에 자바스크립트 파일을 하나를 생성하고, ESLint로 검사할 코드를 작성합니다.
- index.js
var foo = bar;
그 다음, 터미널에서 eslint
커맨드 뒤에 이 파일의 이름을 넘겨서 실행을 합니다.
그러면 ESLint가 해당 파일을 검사하고 위와 같이 코드의 문제점을 터미널에 출력해줍니다.
$ node_modules/.bin/eslint index.js
/temp/learn-eslint/index.js
1:5 error 'foo' is assigned a value but never used no-unused-vars
1:11 error 'bar' is not defined no-undef
1:15 error Unnecessary semicolon no-extra-semi
✖ 3 problems (3 errors, 0 warnings)
1 error and 0 warnings potentially fixable with the `--fix` option.
ESLint는 소스 코드에 문제가 있는지 검사를 해줄 뿐만 아니라 가능한 경우에는 코드를 교정까지 해줍니다.
위 커맨드에 --fix
옵션을 붙여서 실행하면 no-extra-semi
항목을 ESLint가 자동으로 고쳐줍니다.
$ node_modules/.bin/eslint index.js --fix
/temp/learn-eslint/index.js
1:5 error 'foo' is assigned a value but never used no-unused-vars
1:11 error 'bar' is not defined no-undef
✖ 2 problems (2 errors, 0 warnings)
다시 소스 파일을 열어보면 원래 제일 뒤에 세미콜론이 두개였었는데 한개로 줄어들었음을 알 수 있습니다.
- index.js
var foo = bar;
ESLint 프로젝트 셋업
실제 프로젝트에서는 파일이 많을 것이므로 위와 같이 매번 터미널에서 ESLint를 실행하는 것은 비현실적일 것입니다. 그래서 일반적으로 ESLint를 사용할 때는 프로젝트 레벨에서 설정을 해두고 사용하는 경우가 대부분입니다.
먼저 프로젝트의 전체 파일을 상대로 ESLint를 실행하는 NPM 스크립트를 추가합니다.
- package.json
"scripts": {
"lint": "eslint ."
},
자 이제 부터 터미널에 npm run lint
라고 입력하면 프로젝트의 전체 파일을 상대로 ESLint가 실행될 것입니다.
다음으로 .eslintignore
파일을 생성하여 ESLint를 실행할 때 예외 시킬 파일이나 디렉터리를 지정해줍니다.
예를 들어, node_modules
디렉터리의 경우 외부 라이브러리의 소스 코드를 담고 있기 때문에 린트를 해줄 필요가 없습니다.
- .eslintignore
node_modules
ESLint 강제 하기
모든 개발자가 본인이 작성한 소스 코드에 대해 린트를 수행하고 발견된 문제를 해결 후에 커밋(commit)을 해주면 참 좋겠지만 현실은 그렇지가 않습니다. 소스 코드를 커밋할 때 ESLint를 강제로 실행하여 문제가 있다면 코드 저장소(repository)에 유입되는 것을 차단할 수 있는 유용한 방법이 있습니다.
우선, 실습 프로젝트에 lint-staged
와 husky
라는 패키지를 개발 의존성으로 설치합니다.
$ npm i -D lint-staged husky
lint-staged
는 스테이징 영역에 있는 파일을 상대로 린트를 해주는 도구이고, husky
는 git 커맨드 실행 시에 특정 스크립트를 실행주는 도구입니다.
패키지 설치가 끝나면, package.json
파일에 다음 설정을 추가해줍니다.
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
이렇게 설정을 하면 개발자가 git commit
명령어를 날릴 때 마다, 스테이징 영역에 있는 파일을 상대로 린트를 해주고 가능하면 코드 교정까지 해줍니다.
정말 그러한지 확인을 위해 실습 프로젝트에 자바스크립트 파일을 하나를 생성하고, 린팅 에러가 발생하도록 코드를 작성합니다.
- index2.js
function foo(bar) {
console.log(baz);
}
그리고 이 파일을 커밋을 하기 위해서 git add .
, git st
, git commit
커맨드를 차례로 날리면…
$ git add .
$ git st
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: index2.js
$ git commit
husky > pre-commit (node v10.15.1)
↓ Stashing changes... [skipped]
→ No partially staged files found...
❯ Running tasks...
❯ Running tasks for *.js
✖ eslint --fix
git add
✖ eslint --fix found some errors. Please fix them and try committing again.
/temp/learn-eslint/index2.js
1:10 error 'foo' is defined but never used no-unused-vars
1:14 error 'bar' is defined but never used no-unused-vars
2:15 error 'baz' is not defined no-undef
✖ 3 problems (3 errors, 0 warnings)
husky > pre-commit hook failed (add --no-verify to bypass)
린트 결과 문제가 3가지가 발생했기 때문에 코드 커밋을 거부 당합니다!
다음처럼 린팅 에러를 모두 해결 후에 다시 코드를 커밋하면,
- index2.js
export function foo(bar) {
console.log(bar);
}
이제서야 정상적으로 코드가 커밋이 가능하다는 것을 알 수 있습니다.
$ git add .
$ git commit
husky > pre-commit (node v10.15.1)
↓ Stashing changes... [skipped]
→ No partially staged files found...
✔ Running tasks...
[master e130e4b] Add index2
1 file changed, 3 insertions(+)
create mode 100644 index2.js
마치면서
이상으로 자바스크립트 린터인 ESLint를 어떻게 사용하고 프로젝트에 설정해야되는지에 대해서 살펴보았습니다. 고품질의 코드를 작성하기 위해서는 단위 테스트 단계 뿐만 아니라 이 린트 단계도 워크 플로우(Workflow)의 일부로 녹여야 한다고 생각합니다. 또한 이렇게 린트 단계를 자동화하면 프로젝트의 전반적인 개발 생산성 측면에서 상당한 이점을 기대할 수 있습니다.