Introduction
- 일관성 있는 형식으로 코드 작성 및 잠재적 에러를 발견하여 코드 퀄리티를 높이기 위하여 Code Formatter와 Linter를 도입했습니다.
- 이러한 도구를 사용하여 코딩 스타일에 대한 고민을 덜고, 코드 작성에만 집중할 수 있게 되었습니다.
- ESLint에도 코드 포맷팅 기능이 있지만, Prettier의 자동 코드 포맷팅 기능이 더 강력하기 때문에
eslint-config-prettier 플러그인을 사용하여 eslint에서 formatting관련 rule들을 모두 해제하였습니다.
- 즉 Linting은 ESLint가, Code Formatting은 Prettier가 담당할 수 있도록 했습니다.
- 커밋 시 ESLint와 Prettier가 자동적으로 적용될 수 있도록 Git Hook 중 pre-commit을 사용했습니다.
- Git Hook을 좀 더 편리하게 사용할 수 있는 도구인 Husky와 Lint-staged를 활용하여 커밋 시 ESLint와 Prettier가 적용된 후 커밋이 될 수 있도록 설정하였습니다.
Prittier, ESLint 파일
prettier 설정 파일
{
"bracketSpacing": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"printWidth": 100,
"endOfLine": "auto"
}
eslint 설정 파일
'use strict';
module.exports = {
env: {
browser: true,
es2022: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:import/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'plugin:react/jsx-runtime',
'airbnb',
'plugin:jsx-a11y/recommended',
'plugin:import/typescript',
],
plugins: ['react', '@typescript-eslint', 'import'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest',
},
rules: {
'no-warning-comments': [
'warn',
{
terms: ['TODO', 'FIXME', 'XXX', 'BUG'],
location: 'anywhere',
},
],
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
pathGroups: [
{
pattern: 'react*',
group: 'external',
position: 'before',
},
],
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
},
],
'react/function-component-definition': [
'error',
{
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function',
},
],
'no-console': 'off',
'react/jsx-filename-extension': ['warn', { extensions: ['.tsx', '.jsx'] }],
'import/prefer-default-export': 'off',
'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
'react/require-default-props': 'off',
'react/prop-types': 'off',
'consistent-return': 'off',
curly: ['error', 'all'],
eqeqeq: ['error', 'smart'],
'@typescript-eslint/naming-convention': [
'error',
{ format: ['camelCase', 'UPPER_CASE'], selector: 'variable', leadingUnderscore: 'allow' },
{ format: ['camelCase'], selector: 'parameter', leadingUnderscore: 'allow' },
{ format: ['camelCase', 'PascalCase'], selector: 'function' },
{ format: ['PascalCase'], selector: 'interface' },
{ format: ['PascalCase'], selector: 'typeAlias' },
],
'@typescript-eslint/array-type': ['error', { default: 'array' }],
'no-unused-vars': ['error', { argsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_' }],
'react/react-in-jsx-scope': 'off',
},
settings: {
'import/resolver': {
typescript: {},
},
},
};
Extends 목록의 설정들
plugin:jsx-a11y/recommended
- 모듈
- 설명
- JSX를 정적으로 평가(static evaluation)하여 애플리케이션의 접근성 문제를 찾아주는 플러그인입니다.
- 예
alt-text 규칙을 통해 img 태그 등에 alternative text를 명시하지 않을 시 에러를 발생시켜, 사용자가 요소를 볼 수 없는 상황일 경우 대체할 정보를 표시할 수 있도록 합니다.
no-noninteractive-element-interactions룰을 통해 interactive element가 아닌 div 태그 등에 mouse나 keyboard event handler를 할당하지 못하도록 에러를 발생시킵니다.
plugin:import/typescript
- 모듈
- eslint-plugin-import
- eslint-import-resolver-typescript
- 설명
- eslint-import-resolver-plugin은 eslint-plugin-import에 TypeScript 지원을 추가합니다.
- 예
.cts/.mts/.ts/.tsx/.d.cts/.d.mts/.d.ts 확장자를 가진 파일을 import/require 할 수 있습니다.
- tsconfig.json에 정의된 paths를 사용할 수 있습니다.
plugin:@typescript-eslint/recommended
- 모듈
- typescript-eslint
- @typescript-eslint/parser
- 설명
- ESLint, Pretter가 TypeScript를 지원할 수 있도록 하는 도구입니다.
- 예
- TypeScript는 number, string, boolean 등으로 초기화 된 변수나 매개 변수에 명시적으로 유형을 선언하지 않아도 타입을 유추할 수 있습니다.
plugin:import/recommended
- 모듈
- 설명
- 적절하게 import를 할 수 있도록 유효성을 검사하는 규칙을 가지고 있는 ESLint 플러그인입니다.
- file path나 스펠링이 틀린 것도 잡아낼 수 있습니다.
- 예
- no-extraneous-dependencies: 외부 패키지 사용을 금지하는 규칙입니다.
eslint:recommended
- 모듈
- 설명
- ESLint 팀에서 best practice로 간주하는 규칙이 포함된 built-in configuration입니다.
- 예
- no-const-assign: const variable에 재할당을 금지하는 규칙입니다.
plugin:react/recommended
- 모듈
- 설명
- 예
- button-has-type: 명시적으로 type attribute를 쓰지 않고 button element를 사용하는 것을 금지하는 규칙입니다.
plugin:react/jsx-runtime
- 모듈
- 설명
- React 17의 새로운 JSX transform을 사용하는 경우, 이 플러그인을 extends에 명시하여 관련 규칙을 비활성화해야 합니다.
- 예
- react-in-jsx-scope: JSX를 사용할 때 React를 꼭 import 해와야 하는 규칙입니다.
plugin:react-hooks/recommended
- 모듈
- eslint-plugin-react-hooks
- 설명
- React Hook의 2가지 규칙을 적용하는 ESLint plugin입니다.
- 최상위 레벨에서 hook을 호출해야 합니다.
- React function(function components, custom hook)에서 hook을 호출해야 합니다.
plugin:prettier/recommended
- 모듈
- 설명
- Prettier와의 충돌을 방지하기 위해 code formatting과 관련된 ESLint rule을 비활성화합니다.
airbnb
- 모듈
- 설명
- ECMAScript6+, React가 포함된 Airbnb의 ESLint rules에 대한 ESLint configurations입니다.
eslint, eslint-plugin-import, eslint-plugin-react, eslint-plugin-react-hooks, and eslint-plugin-jsx-a11y를 필요로 합니다.
- 예
- 익명 함수를 사용해야 하는 경우(inline callback 등) 함수 선언문이 아닌 화살표 함수 표현식을 사용하도록 강제합니다.
plugin:storybook/recommended
- 모듈
- 설명
- Storybook을 Best practice로 사용하기 위한 공식적인 ESLint plugin입니다.
- 예
- storybook/default-exports: Story file에는 default export가 있어야 한다는 규칙입니다.
Rules 목록
| Rules | Description | Reason |
|---|
| no-warning-comments | 설정에 명시된 특정한 단어를 포함하고 있는 주석에 대해 보고하는 규칙 | 커밋하기 전에 ‘TODO’, ‘FIXME’, ‘XXX’, ‘BUG’ 주석에 있을 경우 보고 받아서 주석을 제거할 수 있게 이 규칙을 적용했습니다. |
| import/order | require나 import 문의 순서 컨벤션을 정하고 그것을 강제하는 규칙 | require나 import 문의 순서 컨벤션을 정하고 그것을 강제하는 규칙 |
| react/function-component-definition | 함수 컴포넌트를 사용할 때 특정한 함수 타입을 적용하도록 도와주는 규칙 | 컨벤션에서 함수를 arrow function을 사용해서 정의하기로 결정했기 때문에, 함수 컴포넌트 정의시 arrow function을 사용하도록 이 규칙을 적용했습니다. |
| no-console | console의 사용을 금지하는 규칙 | console.log를 사용하여 에러를 확인하는 것은 git commit 전 확인하는데 유용하므로, console을 사용하기로 결정해서 이 규칙을 비활성화 했습니다. |
| react/jsx-filename-extension | JSX 문법 포함하는 파일이 .jsx 확장자를 사용하도록 권장하는 규칙 | jsx 확장자를 통해 파일에서 JSX 문법을 사용하는지 한번에 알아볼 수 있으므로 이 규칙을 적용했습니다. |
| import/prefer-default-export | export하는 파일 내에서 default export를 사용하도록 권장하는 규칙 | default export를 사용하면 intellisense가 잘 작동하지 않아서 불편하기 때문에, default export를 사용하지 않기로 결정하여 이 규칙을 비활성화 했습니다. |
| no-plusplus | 단항 연산자 ++, —를 허용하지 않는 규칙 | 단항 연산자는 자동 semi-colon(;) 삽입 대상이어서 공백의 차이로 인해 소스 코드의 의미 체계가 달라질 수 있으므로, 단항 연산자를 사용하지 않도록 이 규칙을 적용했습니다. 그러나 for 문에서의 단항 연산자는 semi-colon 없이 사용하므로, allowForLoopAfterthoughts 옵션을 통해 단항 연산자를 허용했습니다. |
| react/require-default-props | 컴포넌트의 props가 required 타입이 아닐 때 대응되는 defaultProps값을 설정하도록 강제하는 규칙 | 프로젝트에서 TypeScript를 사용하기 때문에, defaultProps 값이 필요 없으므로 이 규칙을 비활성화 했습니다. |
| react/prop-types | 컴포넌트의 props에 대한 타입인 propTypes을 설정하도록 강제하는 규칙 | 프로젝트에서 TypeScript를 사용하기 때문에, propTypes 값이 필요 없으므로 이 규칙을 비활성화 했습니다. |
| curly | 모든 block 문에 대해 일관적인 괄호({}) 스타일을 강제하는 규칙 | 모든 block 문을 괄호로 감싸는 일관적인 스타일을 적용하여 코드의 가독성을 높이기 위해 이 규칙을 적용했습니다. |
| eqeqeq | strick equality operator(===, ≠=)를 사용하도록 강제하는 규칙 | 타입 변환을 일으키는 eqaulity operator(==, ≠) 대신에 stict equality operator(===, ≠=)를 사용하는 것이 안전하기 때문에 이 규칙을 적용했습니다. smart 옵션을 사용하여 literal 값을 비교할 때, typeof 연산자의 반환값을 평가할 때, null과 비교할 때의 3가지 경우에 이 규칙이 적용되지 않도록 했습니다. |
| @typescript-eslint/naming-convention | 코드 전반에 네이밍 컨벤션을 강제하는 규칙 | 기존에 논의한 네이밍 컨벤션을 강제함으로써 코드의 가독성이 좋아지므로 이 규칙을 적용했습니다.사용하지 않는 변수나 매개변수를 정의할 때 underscore(_)로 시작하는 이름을 붙이기 때문에, 변수와 매개변수에 leadingUnderscore 옵션을 사용했습니다. |
| @typescript-eslint/array-type | 배열 타입을 일관적으로 정의하도록 강제하는 규칙 | 모든 배열 타입을 T[]의 형태로 정의함으로써 코드의 가독성이 좋아지므로 이 규칙을 적용했습니다. |
| no-unused-vars | 변수가 사용되지 않는 것을 허용하지 않는 규칙 | 사용되지 않는 변수는 코드에서 자리를 차지하고 가독성을 헤치므로, 이러한 경우를 방지하기 위해 이 규칙을 적용했습니다. 사용하지 않는 인수나 구조 분해 할당 시 사용하지 않는 변수를 underscore(_)로 정의하기 때문에, argsIgnorePattern: ‘^_’ 과 destructredArrayIgnoerPattern : ‘^_' 옵션을 사용했습니다. |
| react/react-in-jsx-scope | JSX를 사용할 때 scope 내에 React가 있는지 확인하는 규칙 | React 17 버전 이후부터 새로운 JSX transfom에서 자동적으로 react/jsx-runtime 함수를 불러오기 때문에, 이 규칙을 비활성화 했습니다. |
| arrow-body-style | arrow function의 내부를 괄호로 감싸도록 강제하는 규칙 | arrow function의 내부를 항상 괄호로 감싸는 일관적인 스타일을 적용하여 코드의 가독성을 높이기 위해 이 규칙을 적용했습니다. |
| import/extensions | source path를 import할 때 파일 확장자의 사용 여부를 결정하는 규칙 | typescript resolver가 import 경로에서 파일 확장자의 생략을 허용하므로, import 경로에 파일 확장자를 사용하지 않도록 이 규칙을 적용했습니다. 추후에 이미지 파일 import 시 이미지 파일임을 구분하기 위해 특정 파일 확장자를 사용할 예정입니다. |