사내 ESLint & Prettier 적용기

Aiden·2023년 3월 12일
1
post-thumbnail

최근 사내에 코드 리뷰 문화를 도입하게 되었는데, 팀원 간 컨벤션이 달라 리뷰가 어렵다는 피드백이 있었다.

실제로 살펴보니 유사한 동작을 수행하는 로직들 간에도 구현되어 있는 코드 스타일이 서로 달라 읽어내는 데 생각보다 많은 리소스가 소모되었다.

예를 들어 라인 별 코드 길이의 제한이 없어 무작정 옆으로 길어진 코드는 편집기의 횡단 스크롤을 끌어대며 읽어내야 하는가 하면, 어떤 코드는 짧은 로직들이 아래로 길어져 필요 이상의 라인 수를 차지하고 있는 식이었다.

이 뿐 아니라, 아주 유연하게(?) 세팅된 타입스크립트 설정 덕에 타입조차 달려있지 않은 변수들이나 함수들이 넘쳐나 기본적인 타입가드도 없이 프로퍼티를 참조하고 있는 로직들도 눈에 띄었다.

마치 수많은 개발자들이 각각의 사이드 프로젝트에서 작업한 결과물을 하나의 파일에 옮겨놓은 것만 같은 느낌이었다.

조금만 읽어보면 몇명의 개발자가 프로젝트를 작업했는지 알아차릴 정도로 코드 스타일이 제각각이었고 중구난방이었다.

따라서 시급히 LintFormatting, 컨벤션을 도입하고 정리하여야 할 필요성을 느끼게 되었다.

이를 위해 개발자 간 코드 스타일을 통일하고, 설정된 Lint 에 맞춰 작업하여 코드 퀄리티의 일관성과 가독성을 향상시키는 것이 최우선 목표였다.

또한, 통일된 코드 스타일과 Lint 위에서 작업해가면서 네이밍 규칙 혹은 구조와 같은 컨벤션을 정립해나가는 것도 자연스럽게 이후 스프린트의 부차적인 목표가 되었다.


먼저 ESLint 와 Prettier 의 개념을 살펴보며, 프로젝트에 적용하는 목적에 대해 분명히 할 필요가 있었다.

ESLint & Prettier Concepts

ESLint

“컨벤션을 위배하는 코드 혹은 안티패턴을 방지하여 코드 퀄리티를 보장한다.”

ESLint 는 위와 같이, 정의된 컨벤션을 기준으로 이를 준수하지 않은 코드나 통상적으로 지양하는 안티패턴사전에 검출하여 방지할 수 있도록 돕는 정적 분석 도구라 할 수 있다.

추가적으로 간단한 포맷팅 기능도 지원하고 있다.

Prettier

“코드 스타일이 통일되도록 돕는다.”

Prettier 는 CRLF, Spacing, Indentation 등을 통일하여 편집기에서 텍스트를 일관되게 작성할 수 있도록 하는 포맷팅 도구다.


결론적으로는 ESLint 를 통해 프로젝트 내 안티패턴을 검출하여 프로젝트의 코드 퀄리티를 전체적으로 향상시키고, Prettier 를 통해 팀원 간 코드 스타일을 통일하여 프로젝트의 일관성과 가독성을 달성할 수 있을 것이라 판단했다.

초반에는 익숙해지는 데 시간이 필요하겠지만, 장기적으로는 코드를 읽어내고 리팩토링하는 시간을 단축시켜 코드 리뷰에 소모되는 시간도 줄어들고 생산성이 크게 향상될 것이라는 기대도 있었다.


Set ESLint

먼저, ESLint 부터 적용하기로 했다.
기존에 ESLint 를 사용하고는 있었지만, 팀원들이 에디터에 개별적으로 설치한 Extension 을 사용하고 있었기 때문에 프로젝트에 고유한 Lint 설정은 없었다.

1. 프로젝트에 ESLint Library 를 설치한다.

npm install -D eslint
# or
yarn add -D eslint

코드가 실행될 때 필요한 의존성은 아니기 때문에 devDependencies 에 설치해주었다.


2. 에디터에 ESLint Extension 을 설치한다.

ESLint Extension 은 에디터(VSCode)에 직접 적용되어 동작하고, 해당 프로젝트의 ESLint를 우선적으로 찾아 참조한다.
만약, 프로젝트에 설치된 ESLint 가 존재하지 않는다면 Global 로 설치된 ESLint 를 찾아 참조하게 된다.

정리하면 ESLint 는 결국 에디터 레벨에 적용하여 사용하는 정적 분석 도구이므로,

프로젝트 레벨에서의 ESLint Library 와 에디터 레벨에서의 ESLint Extension 모두 설치가 필요하다.


3. .eslintrc.js 파일을 세팅한다.

이제 프로젝트에 적용될 ESLint Rule 을 정의한다.

// .eslintrc.js

module.exports = {
    root: true,
    env: {
        node: true,
        jest: true,
    },
    parser: '@typescript-eslint/parser',
    parserOptions: {
        project: './tsconfig.json',
        sourceType: 'module',
    },
    plugins: ['@typescript-eslint/eslint-plugin'],
    extends: ['plugin:@typescript-eslint/recommended', 'prettier'],
    ignorePatterns: ['.eslintrc.js'],
    rules: {
        '@typescript-eslint/interface-name-prefix': 'off',
        '@typescript-eslint/explicit-function-return-type': 'off',
        '@typescript-eslint/no-explicit-any': 'off',
        '@typescript-eslint/explicit-module-boundary-types': 'off',
        '@typescript-eslint/no-unused-vars': 'off',
        '@typescript-eslint/ban-types': 'off',
        '@typescript-eslint/no-empty-function': 'off',
    },
}
  • root
    • default 는 true
    • false 일 경우 PC 의 파일 시스템 디렉토리에서도 ESLint 를 찾는다.
  • env
    • browser 혹은 node 와 같은 환경을 명시한다.
    • console, require 와 같이 사전에 선언되지 않은 전역 변수 환경을 정의한다.
  • parser
    • default 는 espree
    • 코드를 분석할 Parser 를 명시한다.
    • Plugin 을 사용한다면, Plugin 에서 제공하는 Parser 를 사용한다.
    • 예시에서는 @typescript-eslint 에서 제공하는 Parser 를 명시하였다.
  • parserOptions
    • Parser 가 참조할 수 있는 언어 옵션을 지정한다.
    • project 를 통해 프로젝트에서 사용하고 있는 타입스크립트 설정을 참조하도록 경로를 지정해주었다.
    • sourceType 을 통해 Parser 의 Export 타입을 설정해주었다.
  • plugins
    • ESLint 에서 사용할 서드파티 플러그인을 정의한다.
      • eslint-config-airbnb-base : 에어비앤비 린트 플러그인
      • eslint-config-next : Next.js 전용 린트 플러그인
      • eslint-plugin-react : 리액트 전용 플러그인
      • eslint-plugin-prettier : 린트 위에 사용할 Prettier 플러그인
      • eslint-config-prettier : 린트 설정과 중복되는 부분을 Prettier 에서 제외하는 플러그인
      • @typescript-eslint/eslint-plugin : 타입스크립트 전용 린트 플러그인
    • 플러그인은 이보다 훨씬 다양하며, 필요한 플러그인은 패키지매니저를 통해 설치한다.
    • 프로젝트에서 함께 사용될 플러그인을 배열에 추가할 수 있다.
    • 예시에서는 타입스크립트 전용 린트 플러그인을 추가해주었다.
  • extends
    • Rule 설정이 저장되어 있는 외부 파일로, 플러그인에서 사용할 Rule 을 지정해주어야 한다.
    • 플러그인은 단순한 Rules Set 이므로, 플러그인만 사용해서는 Rule 이 적용되지 않는다.
    • 위 예시 파일에서는 plugin:@typescript-eslint/recommended 가 적용되었으며, 설정한 플러그인에서 권장하는 타입스크립트 Rule 이 적용된다.
    • 추가적인 변경이 필요할 시에는 rules 에서 커스터마이징 한다.
  • ignorePatterns
    • Lint 가 적용될 파일 및 디렉토리에서 제외시킬 수 있는 옵션이다.
  • rules
    • 직접 Lint rule 을 커스터마이징한다.
    • extends 에서 적용한 외부 Rules 를 커스터마이징할 수 있다.

개별 설정은 각 플러그인의 ESLint Plugin Docs 를 확인하면 상세하게 설명되어 있다.

PS..

처음에는 타입스크립트 관련한 다양한 플러그인들과 Rule 들을 Extends 하여 에어비앤비와 같은 Lint 도 적용시켜보았으나..

ESLint 실행 후 4000 개가 넘는 오류 로그에 그만 넋을 잃고 말았다 🫠

기존에 엄격한 타입스크립트 설정이나 Lint 가 적용되지 않은 상태에서 작성된 코드베이스라 한순간에 적용하기는 어려울 것이라 판단되었고,
그렇다고 각각의 옵션을 OFF 하여 사용하는 것은 Lint 의 의미가 없다고 생각했다.

결국 팀원들과 논의 후 처음에는 조금 유연하게 설정하여 적용하고, 추후 수정 및 리팩토링해나가며 Lint 레벨을 높여가기로 결정했다.


Set Prettier

Prettier 를 세팅하는 방법은 크게 2가지가 있다.

  • 에디터의 Extension 으로 설치
  • Prettier 플러그인 라이브러리를 직접 설치 후 eslintrc 에 세팅

전자는 프로젝트에 Prettier 를 세팅하는 방법이 아닌, 에디터 전역적으로 Prettier Rules 를 적용하는 방법이라 협업 시에는 권장되지 않는다.

후자의 방법은 프로젝트에 Prettier Rules 를 세팅하는 방법이므로, 다른 환경에서도 동일한 포맷팅을 유지할 수 있어 협업 시 유용하다.
ESLint 를 프로젝트에 고유하게 적용했던 원리와 같다.

따라서, 협업 시에는 후자의 방법을 사용하여 Prettier 를 적용하는 것을 권장한다.


Prettier Extension 과 .prettierrc 의 차이

Prettier 를 Extension 으로 설치하여 사용할 경우, 이는 현재 PC 의 에디터 레벨에서 적용되고, 패키지매니저를 통해 설치한 프로젝트 고유 Prettier 플러그인에는 적용되지 않는다.

따라서 패키지매니저를 통해 Prettier 를 설치했다면 .prettierrc 파일을 꼭 활용하여야 하며,
이 때 .prettierrc 파일이 존재한다면 Extension 의 Prettier Rules 는 무시된다.


1. 현재 프로젝트에 Prettier 를 설치한다.

npm install -D prettier
# or
yarn add -D prettier

2. ESLint 에 Prettier 플러그인을 적용한다.

  • eslint-config-prettier
    • ESLint 와 중복되는 Prettier Rules 를 OFF 한다.
  • eslint-plugin-prettier
    • Prettier 규칙을 ESLint 에 적용시킨다.
    • 코드 내에서 Prettier Rules 에 맞지 않는 부분을 Lint Error 로 표시한다.
npm install -D eslint-config-prettier
npm install -D eslint-plugin-prettier
# or
yarn add -D eslint-config-prettier
yarn add -D eslint-plugin-prettier
// .eslintrc.js

{
  "extends": ["plugin:prettier/recommended"]
}

.eslintrc.js 에 위 내용을 적용하면, 이 두 가지 플러그인을 모두 확장하여 사용 가능하다.

추가적으로, Prettier 관련 설정을 ESLint 의 extends 에 적용할 때는 원활한 포맷팅 적용을 위해 배열의 가장 마지막에 적용한다.

이제 상세한 Prettier Rules 는 .prettierrc 에 작성할 수 있다.


3. .prettierrc.js 파일을 세팅한다.

module.exports = {
    singleQuote: true,
    trailingComma: 'all',
    printWidth: 120,
    tabWidth: 4,
    useTabs: false,
    semi: false,
    bracketSpacing: true,
    endOfLine: 'auto',
    arrowParens: 'always',
};

Prettier 공식문서를 참고하면, 다양한 설정을 확인할 수 있다.

PS..

실제로는 eslint-plugin-prettier 를 설치하지 않고 .eslintrc.js 의 extends 에 prettier 를 추가하여 대체해주었다.

또한, Prettier 의 외부 Rule set 을 사용하지 않고, 기본적인 Prettier Rule 만을 우선 적용하기로 했다.
추가적으로 개발 과정에서 합의 후 추가되는 규칙은 .prettierrc.js 에 작성하여 적용하기로 결정했다.


이렇게 사내 프로젝트에 ESLint 와 Prettier 가 성공적으로 적용되었다.

도입을 결정한 초반에는 엄격한 타입스크립트 Lint 와 함께 다양한 Prettier Rule 을 적용해보고자 하는 포부가 있었지만..
Lint 나 컨벤션이 없는 환경에서 올려진 코드베이스를 한 순간에 변환하는 작업은 실제 운영되고 있는 서비스에서 리스크가 크고 공수가 많이 드는 작업이었다.

결국 기본적이고 단순한 설정들만을 초반에 적용하고, 해당 규칙들에 익숙해지면서 점점 난이도를 높여가는 방식으로 Lint Level 을 올려가는 것이 가장 효율적일 것 같다.

현재는 작업을 진행하면서 사용하지 않는 변수나 함수들을 제거하고, unsafe 한 타입이나 참조를 리팩토링하면서, 이후 추가될 Lint 를 준비하고 코드 퀄리티를 개선하고 있다.

다음 포스팅에서는 프로젝트에 적용된 Lint 를 자동화했던 경험에 대해 작성해볼 예정이다.

0개의 댓글