Profile image

Kim Songhyun

카카오 프론트엔드 개발자 since 2018. 01.

사람은 누구나 실수를 한다. 프로그래머도 사람이므로 마찬가지로 실수를 한다. 실수라고 해봐야 사실 별 볼 일 없는 것들이 대부분이지만 어쨌든 실수는 작게는 프로그래머 한 명을 패닉에 빠뜨릴 수 있고 심하게는 서비스 팀 전체가 장애대응 모드에 들게 만들 수 있다.

주작이라고 하니 재미로만 봐주시길

수만 명, 많게는 수십만, 수백만 명이 사용하는 서비스는 기도 메타로만 돌아갈 수 없기 때문에 이런 실수를 방지해 줄 여러 안전장치들이 필요해졌고 그렇게 해서 나오게 된 것이 정적 분석이다. 정적 분석이란, 코드를 실제로 실행하지 않고 코드, 혹은 바이너리 자체의 내용만으로 코드의 버그나 결함을 잡아내는 분석 방식을 말한다. 자바스크립트 진영에서는 eslint를 사실상 표준으로 사용하고 있다.

코드적인 냄새 외에도, 여러 명의 작업자가 한 코드베이스를 수정할 때 동일한 코드 스타일을 유지할 수 있는 방법도 고안되었다. 에디터 자체가 지원하는 기능을 사용해도 좋지만, 모든 에디터에서 같은 설정파일을 사용하기 위해 eslint와 마찬가지로 코드 포매팅을 전문으로 하는 라이브러리를 사용할 수 있다. 이 글에서는 editorconfigprettier에 대해서 설명할 예정이다.

editorconfig

editorconfig는 언어에 상관 없이, 파일명 패턴만으로 파일의 인덴트 사이즈, 스페이스바 사용 여부 등 기초적인 코딩 스타일을 지정해 줄 수 있는 일종의 규약이다. 대부분의 에디터가 editorconfig 플러그인을 제공하므로, 해당 플러그인을 설치하면 어디서든 같은 코딩 스타일을 유지할 수 있다.

VS Code에도 있다

혹자는 이후 설명할 prettier를 사용하면 되는데 왜 editorconfig를 따로 사용하냐고 할 수도 있겠다. 일단 결론부터 말하자면 prettier 하나만 써도 결과적으로 같은 코드를 생산하기는 하지만 적용되는 시점이 다르기 때문에 둘 다 사용하는 것이 좋다. prettier는 저장 시점, 혹은 커밋 직전 시점에 마지막으로 코드를 포맷하는 것이고, editorconfig는 작성 시점에 일관적인 스타일을 제공한다고 생각하면 된다.

폴더 최상단에 .editorconfig 파일을 생성한 뒤, 아래 포맷에 맞추어 설정 파일을 작성한다.

1[파일 패턴]
2charset = utf-8
3end_of_line = lf
4# ...

파일 패턴은 우리가 유닉스에서 일반적으로 사용하는 와일드카드 패턴을 사용한다. 예를 들어 *.js는 폴더 내 모든 js 파일을 의미한다. 범용적으로 사용하는 옵션은 7개 + 루트 폴더 여부를 체크하는 옵션까지 총 8개이며, 그 이외에도 추가적인 옵션이 있지만 모든 에디터에서 동작을 보장하지는 않는다. 이후 설명할 prettier가 세밀한 포매팅 옵션을 추가로 제공하므로 여기에서는 범용 옵션만을 사용하자. 여기에서 전체 옵션 리스트를 확인할 수 있다.

prettier

이렇게 해서 앞으로 작성할 코드에 대해서는 일관적인 스타일을 적용할 수 있게 되었다. 하지만 editorconfig는 이미 작성한 코드에 대한 스타일 교정은 지원하지 않는다.

이렇게, 이미 작성한 코드에 대해서 추가적으로 코드 포매팅을 적용하기 위해서, 또 자바스크립트만의 추가적인 규칙 지정을 위해서 prettier를 사용한다.

1# 설치
2npm i -D prettier
3
4# prettier 설정 파일 추가
5echo {} > .prettierrc.json
6echo 'ignore-this-file.js' > .prettierignore
7
8# 코드 포매팅
9npx prettier --write .

editorconfig에 비하면 훨씬 많지만 prettier옵션 자체는 그렇게 많지 않은 편이기 때문에 직접 읽어보면서 세팅해보기를 권한다.

파일별로 각각 다른 포맷을 지정하기

prettieroverrides 속성을 이용해서 파일별로 각각 지정하고 싶은 포맷을 따로 지정해 줄 수 있다. 아래 설정 파일에서는 yaml/json 포맷의 파일에는 탭을 사용하지 않고, 나머지 파일에는 탭을 사용하도록 하고 있다.

1{
2 "useTabs": true,
3 "overrides": [{
4 "files": ["*.json", "*.babelrc", "*.yml", "*.yaml"],
5 "options": {
6 "useTabs": false
7 }
8 }]
9}

eslint

마지막으로 eslint를 세팅해보자. 앞서 설명했듯 eslint는 정적 분석 도구로 prettiereditorconfig 같은 포매팅 툴과는 궤가 조금 다른데, 예를 들어 아래 코드를 보자.

1const a = 1
2a = 2;

이런 코드가 있을 때, 코드에 세미콜론을 찍지 않았으니 세미콜론을 찍어주는 역할이 prettier라면, const로 선언된 변수가 변경되고 있다고 경고해 주는 것이 eslint이다. 역할이 다르므로 둘 다 사용해야 한다.

1# 설치
2npm i -D eslint
3npm i -D eslint-config-prettier # eslint와 prettier를 함께 사용하는 경우 설치
4
5# 최초 설정
6npx eslint --init
7
8# 실행
9npx eslint .

lint는 설정이 워낙 방대해서 여기에 모든 케이스의 해결 방안에 대해서는 적을 수는 없다. 대신 내가 겪었던 케이스와 그 해결책을 몇 가지 공개해보려고 한다.

eslint와 prettier를 함께 사용하기

앞서 eslint는 코드 정적 분석 도구라고 설명했는데, 정적 분석은 크게 보면 사실 코드 포매팅이 잘 되어있는지 평가하는 과정도 포함한다. 이 역할이 prettier와 중복되기 때문에, 둘 중 한 쪽의 설정을 무효화하는 과정이 필요하다. prettier 공식 문서에서 추천하는 방법은 eslint-config-prettier를 이용하여 eslint의 코드 포매팅 체크를 무효화하는 것이다.

1npm i -D eslint-config-prettier
1{
2 "extends": [
3 "prettier"
4 ]
5}

jest와 함께 사용하기

jest는 여러 테스트 함수를 전역에 놓고 사용한다. eslint의 기본 설정은 미리 선언되지 않은 함수를 사용하는 것을 금지하고 있으므로 여기에서 에러가 발생한다(스크린샷의 no-undef 일부). 이를 해결하기 위해서는 플러그인을 하나 설치해햐 한다.

1npm i -D eslint-plugin-jest
1{
2 "env": {
3 "jest/globals": true
4 },
5 "plugins": ["jest"]
6}

전역 변수와 함께 사용하기

템플릿 엔진이나 webpack.DefinePlugin 등 js파일 외부에서 전역 변수를 선언하여 사용하는 경우 해당 전역변수가 선언되어 있지 않다는 오류를 뱉는다(스크린샷의 IS_DEV 관련 에러). 해당 변수가 전역변수이므로 외부에 선언되어 있다는 설정을 추가해 주면 해결된다.

1{
2 "globals": {
3 "IS_DEV": true
4 }
5}

체크하고 싶지 않은 설정 무효화하기

마지막으로 이 모든 경우에 해당하지 않으나 그냥 해당 규칙을 회피하고 싶은 경우이다. 나 같은 경우 함수 패러미터를 사용하지 않더라도 언젠가 사용할지 모르겠다는 생각이 들 때 그냥 남겨두는 편인데 eslint 추천 설정에서는 이것도 에러로 잡는다. 이 에러를 그냥 경고 표시로 바꿔보자.

에러 옆에 no-unused-vars라는 표시가 보이는가? 이 이름이 해당 에러를 일으키는 룰의 이름이다. eslint 공식 홈페이지에서 해당 룰을 검색해 볼 수 있다.

페이지에 들어가면, 해당 룰이 어떤 역할을 하는지, 어떤 코드에서 에러를 일으키고 어떤 코드일 때 정상적으로 통과하는지, 어떤 옵션을 줄 수 있고 이 옵션이 어떤 역할을 하는지가 자세히 설명되어 있다. 영어의 압박을 이겨낸다면 여러분에게 필요한 룰을 금방 익힐 수 있을 것이다.

다시 no-unused-vars로 돌아와서, 이 룰을 경고 표시로 바꾸고 몇 가지 옵션을 추가해 주자.

1{
2 "rules": {
3 "no-unused-vars": ["warn", { "vars": "all", "args": "after-used" }]
4 }
5}

에러의 레벨은 off, warn, error로 총 세 가지이며, 그 옆에 룰에 해당하는 옵션값을 전달하면 된다. 작성하지 않은 옵션은 기본값으로 들어간다.

이렇게 해서 모든 에러가 해결되었다.

정리

앞으로 이 레포에 접근 가능한 모든 유저가 같은 스타일의 코드를 유지하고, 장애가 발생하기 전에 그 요인을 최대한 줄일 수 있게 되었다. 코드베이스가 작을 때는 사람이 코드의 문제를 직접 추적하고 해결할 수 있지만, 코드의 규모가 커질수록 이런 도구의 도움 없이는 사전에 버그를 찾아내는 것이 점점 어려워진다. 자신이 개발 중인 기능의 규모가 크다면 이런 정적 분석 툴을 적극적으로 도입해보자.