Go민보다 Go

프론트엔드 개발자

Create

[front] 프로젝트 초기 세팅: linter, formatter

SleepingOff 2024. 4. 19. 00:25

프로젝트 세팅에 있어서 ESlint, Prettier, Stylelint 까지 유명한 linter, formatter들이 있습니다. 

 

개발환경

  • React, TS, Scss, Storybook
  • yarn

스터디로 진행하고 있는 프로젝트에 넣을까 말까 고민을 했습니다. 그다지 규모가 크지도 않고, 협업을 하는 것도 아니기 때문입니다. 고민 끝에 한참 다른 기능을 개발 중에 넣기로 했습니다. 초기 세팅이 아닌 초기 세팅입니다ㅎㅎ

적용하려는 이유는 다음과 같습니다.

1. import order : import가 멋대로 들어오는 게 맘에 안 듭니다. 예쁘게 차곡차곡 자기 자리 찾아서 들어오면 좋겠습니다.

2. 접근성 강제하기: UI 들을 만들어가면서 접근성 고려를 해야 하는데, 자꾸 뒤로 우선순위가 밀리게 됩니다. 이러면 안된다고 생각할 차에, ESlint, Stylelint를 통해 접근성을 지적받을 수 있다길래 도입해보려고 합니다.

가볍게 조사하기

linter, formatter를 설치할 때는 서로 충돌이 날 가능성이 크므로 설정하는 순서를 먼저 찾아보았습니다.

ESlint 설치 -> 설정 -> Prettier 설치 -> 설정 -> ESlint와 Prettier 충돌 방지 플러그인 설치 -> ESlint 추가 설정 -> Stylelint 설치 + 충돌 방지 플러그인 설치

+ ESlint, Prettier, Stylelint에 대한 VSCode 확장팩 설치로 lint, format 자동화

일단 순서는 위와 같은데, 이를 그림으로 좀 그려봤습니다.

그리고 충돌 방지 플러그인도 있고 개발환경에 맞게 플러그인들을 좀 추가하고 싶어서 공식문서를 찾아봤습니다.

npm에 검색했는데 뭔가 이상해서 GPT한테도 물어봤습니다. 플러그인 참 많습니다. 여기에서는 플러그인들을 하나씩 찾아다니면서 무슨 일을 하는 플러그인인지 공부하지 않습니다. 아니 못 합니다. 그래서 사용하는 방법은 다음 두 가지 입니다.

1. 필요한 linter, formatter 기능 정의 -> 해당 기능을 하는 플러그인 검색 -> 플러그인 사용

2. 유명한 플러그인 설치 -> 필요한 linter, formatter 기능 설정 -> 필요없는 플러그인 제거

1번이 가장 정석입니다. 하지만, 저는 제가 원하는 기능이 뭔지 아직 정의하지 못 했습니다. 플러그인도 많고 옵션도 많은데, 그걸 언제 다 보고 언제 다 설정하나요!! 스터디 과제가 내일 마감입니다ㅎㅎ

플러그인 설치하기

그러니 저는 2번을 선택했습니다. 그리고 GPT를 적극 활용하기로 합니다.


나: react, vite, ts, storybook 환경에서 eslint, prettier, stylelint를 사용하려고 해. 각각 추천하는 플러그인 리스트를 알려줘

 
ChatGPT:

React, Vite, TypeScript, 및 Storybook 환경에서 ESLint, Prettier, 및 Stylelint를 효과적으로 사용하기 위해 추천하는 플러그인 리스트를 정리해보았습니다. 이 플러그인들은 코드 품질을 유지하고 일관된 코딩 스타일을 적용하는 데 도움이 됩니다.

1. ESLint 플러그인

ESLint는 JavaScript 코드에서 문제를 찾고 고치기 위한 도구로, TypeScript 및 React에 대한 지원도 포함되어 있습니다.

  • eslint-plugin-react: React 특유의 linting 규칙을 제공합니다.
  • @typescript-eslint/eslint-plugin: TypeScript 코드에 대한 linting 규칙을 제공합니다.
  • eslint-plugin-import: ES6+ import/export 구문에 대한 linting을 제공하며, 경로 및 이름의 올바른 사용을 보장합니다.
  • eslint-plugin-jsx-a11y: JSX 요소의 접근성 문제를 지적합니다.
  • eslint-plugin-react-hooks: React의 Hooks 사용에 대한 규칙을 강제합니다.

2. Prettier 플러그인

Prettier는 코드 포맷터로, 일관된 코드 스타일을 유지할 수 있도록 도와줍니다. ESLint와 함께 사용할 때는 충돌을 방지하기 위해 특별한 설정이 필요합니다.

  • eslint-config-prettier: Prettier와 충돌하는 ESLint 규칙을 비활성화합니다.
  • eslint-plugin-prettier: Prettier를 ESLint 규칙으로 실행하여 Prettier에서 발견한 문제를 ESLint 오류로 표시합니다.

3. Stylelint 플러그인

Stylelint는 CSS, SCSS 등의 스타일 시트에 대한 코드 품질과 일관성을 관리합니다.

  • stylelint-config-standard: 스타일 시트에 대한 기본적인 Stylelint 규칙을 제공합니다.
  • stylelint-prettier: Stylelint와 Prettier를 함께 사용할 때 충돌을 방지하는 데 도움이 되는 플러그인입니다.
  • stylelint-scss: SCSS 특유의 linting 규칙을 제공합니다.
  • stylelint-config-rational-order: 속성을 논리적인 순서대로 정렬하는 규칙을 제공합니다.

AI 시대에 살아서 참 다행입니다. 위에서 나온 리스트를 토대로 NPM trends에 검색해봅니다.

@typescript-eslint/eslint-plugin 이 친구는 별도로 정보가 안 나오긴 하는데, 공식문서에서도 잘 나와있어서 데려가기로 합니다.  관련 문서 바로가기 > https://typescript-eslint.io/packages/eslint-plugin/

 

@typescript-eslint/eslint-plugin | typescript-eslint

The TypeScript plugin for ESLint. ✨

typescript-eslint.io

 

stylelint-config-rational-order 이 친구는 업데이트가 5년전입니다. 다른 세 친구만 데려갑니다.

개발 세팅하기

위에서 써둔 순서대로 다음 명령어를 하나씩 실행하면 필요한 파일들을 설정해줍니다.

추가적으로 stylelihnt-a11y를 설치하여 css 속성에서 접근성을 높일 수 있도록 합니다.

yarn add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react-hooks --dev
yarn create @eslint/config
yarn add prettier eslint-config-prettier eslint-plugin-prettier --dev
yarn add stylelint stylelint-config-standard-scss stylelint-scss stylelint-prettier stylelint-a11y --dev

.eslintrc.json

{
  "env": {
    "browser": true,
    "es2021": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react-hooks/recommended",
    "plugin:prettier/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": ["react", "@typescript-eslint", "react-hooks", "prettier"],
  "rules": {
    "import/order": [
      "error",
      {
        "groups": [
          "builtin",
          "external",
          "internal",
          ["sibling", "parent"],
          "index",
          "object",
          "type"
        ],
        "pathGroups": [
          {
            "pattern": "*.scss",
            "group": "object",
            "position": "after"
          }
        ],
        "newlines-between": "always",
        "alphabetize": {
          "order": "asc",
          "caseInsensitive": true
        }
      }
    ],
    "react/react-in-jsx-scope": "off",
    "react/prop-types": "off",
    "@typescript-eslint/no-unused-vars": "warn",
    "no-console": "warn",
    "@typescript-eslint/explicit-module-boundary-types": "off",
    "prettier/prettier": [
      "error",
      {},
      {
        "usePrettierrc": true
      }
    ]
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}

.prettierrc

{
    "semi": true,
    "singleQuote": true,
    "trailingComma": "es5",
    "tabWidth": 2,
    "useTabs": false,
    "printWidth": 80,
    "bracketSpacing": true,
    "arrowParens": "avoid",
    "jsxSingleQuote": false
}

.stylelintrc.json

{
    "extends": [
        "stylelint-config-standard-scss",
        "stylelint-prettier/recommended"
    ],
    "plugins": [
        "stylelint-scss"
    ],
    "rules": {
        "scss/dollar-variable-pattern": "^\\$[a-z-]+$",
        "scss/percent-placeholder-pattern": "^%[a-z-]+$",
        "scss/at-function-pattern": "^@\\w+",
        "no-empty-source": null,
        "no-missing-end-of-source-newline": null,
        "at-rule-no-unknown": null,
        "scss/at-rule-no-unknown": true
    }
}

참고로 위의 설정 파일들을 추가할 때도 GPT한테 부탁했습니다. 그러다보니 현재 쓴 코드와 맞지 않거나 과하게 강제적인 속성들은 하나씩 제거하는 등의 작업을 할 예정입니다. 

여기까지 된 줄 알았지..?

이슈: ESlint는 9버전이란다

ESlint가 적용되지 않고 있어서 열심히 헤맸습니다. 결과론 ESlint 9.0.0 버전을 깔았기 때문입니다. 위의 설정들이 적용되는 것은 8.57.0까지입니다...ㅎㅎ

그래서 9버전에 맞춰서 열심히 뻘짓해가면서(다운그레이드하려고 했는데 몇 번 실패 + 오기 생김) 설정한 파일들입니다.

eslint.config.js

import pluginJs from '@eslint/js';
import { FlatCompat } from '@eslint/eslintrc';
import configs from './eslint-custom.config.js';

const __dirname = import.meta.dirname;

const compat = new FlatCompat({
  baseDirectory: __dirname, // optional; default: process.cwd()
  resolvePluginsRelativeTo: __dirname, // optional
  recommendedConfig: pluginJs.configs.recommended // optional unless you're using "eslint:recommended"
});
export default [
  pluginJs.configs.recommended,
  {
    ignores: ['design.js', 'config/*', 'eslint.config.js']
  },
  {
    files: ['**/*.ts', '**/*.tsx']
  },
  ...compat.plugins(
    'react',
    'react-hooks',
    'jsx-a11y',
    'import',
    '@typescript-eslint',
    'prettier'
  ),
  ...compat.config(configs)
];

참고로 ignores와 files를 저렇게 다른 객체로 선언해야 인식을 하더랍니다. 왜일까..

eslint-costom.config.js

const configs = {
  env: {
    browser: true,
    es2021: true,
    node: true
  },

  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true
    },
    ecmaVersion: 'latest',
    sourceType: 'module'
  },
  plugins: ['react', 'react-hooks', 'jsx-a11y', 'import', '@typescript-eslint'],
  rules: {
    'import/order': [
      'error',
      {
        groups: [
          'builtin',
          'external',
          'internal',
          ['sibling', 'parent'],
          'index',
          'object',
          'type'
        ],
        pathGroups: [
          {
            pattern: '*.scss',
            group: 'object',
            position: 'after'
          }
        ],
        'newlines-between': 'always',
        alphabetize: {
          order: 'asc',
          caseInsensitive: true
        }
      }
    ],
    'react/react-in-jsx-scope': 'off',
    'react/prop-types': 'off', // off로 해놔야 멀쩡히 돌아감...
    '@typescript-eslint/no-unused-vars': 'warn',
    'no-console': 'warn',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    'prettier/prettier': [
      'error',
      {},
      {
        usePrettierrc: true
      }
    ]
  },
  settings: {
    react: {
      version: 'detect'
    }
  }
};

export default configs;

참 9버전은 CLI 플래그도 달라서 vite로 들어갔던 script 명령어가 다음과 같은 경우 동작하지 않습니다.

    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",

그래서 다음과 같이 적용했습니다.

"lint": "eslint . --fix",

--fix는 ESlint 설정에 맞춰서 강제로 바꿔주는 것입니다. 저는 이미 작성한 코드가 ESlint가 먹지 않아서 생기는 수많은 경고와 에러들을 해소하기 위해 추가했습니다.

--ext ts,tsx 라는 플래그는 eslint.config.js의 files로 들어갔습니다. 나머지 플래그도 혹시 모르기도 하고 저한텐 필요없어서 일단 지웠습니다. 플래그가 좀 많이 변경되어서 필요하신 분들 찾아보시길..

결론

 GPT야 정신차려!!

아직 v9 나온지 얼마되지 않아서 자료가 없어 공식문서만 딥다 팠습니다. 공식문서만 보고 3시간 안에 끝내다니 성장했습니다. 공식문서도 못 봤던 게 엊그제 같은데... 참고로 GPT가 추천한 설정 파일이 ESlint 8.57.0까지만 되는 걸 GPT는 아직 잘 모르고, ESlint 버전 달라했더니 무조건 최신 버전만 추천해대길래 답답했습니다. 그래서 초반에 몇 번 GPT 패다가? 공식문서 보는 걸로 결정했더니 맘이 더 편했습니다..!!

728x90