프로젝트에 규칙을 강제로 부여해서 한 사람이 짠 것 같은 코드를 만들어보자

ClydeHan·2024년 10월 22일
27

ESLint

ESLint Image


📌 ESLint란 무엇인가?


ESLint는 JavaScript 코드에서 문법적 오류, 잠재적 버그코딩 스타일 규칙을 검토하고 강제하는 도구이다. 코드 품질 유지, 버그 예방, 코드 리뷰 간소화 등의 이점을 얻을 수 있다. 프로젝트에서 코드의 일관성을 유지하고, 규칙을 강제할 수 있으며, 특히 팀 프로젝트에서 코드 스타일을 통일하는 데 효과적이다. 잠재적인 오류나 버그를 사전에 감지하고, 올바른 코딩 관행을 따르도록 도움을 주고, 일관된 스타일을 강제함으로써, 코드 리뷰 시 스타일에 대한 논의도 줄일 수 있다.

💡 기본 설치

npm install eslint

📌 ESLint 설정


ESLint의 설정 파일(다양한 확장자로 사용 가능)인 .eslintrc ,.eslintrc.json, eslint.config.js 등은 다양한 구성 요소들로 이루어져 있으며, 각 요소는 프로젝트의 코드 규칙을 정의하고 관리하는 데 중요한 역할을 한다. ESLint 설정은 주로 JSON, YAML, 또는 JS 형식으로 작성되며, 각 구성 요소와 그 역할을 하나씩 정리해보자.

ESLint 최신 버전에서는 env, parserOptions, extends: "eslint:recommended” 등의 일부 속성들이 기본값으로 설정되어 더 이상 명시적으로 설정하지 않아도 된다.

💡 1. root (루트 설정)

root는 해당 ESLint 설정 파일이 프로젝트의 최상위 설정 파일임을 지정하는 플래그이다. 이를 통해 상위 디렉토리의 다른 ESLint 설정 파일을 무시하고, 이 설정 파일만 사용하도록 할 수 있다.

{
  "root": true
}

💡 2. plugins(플러그인)과 extends (확장 설정)

extends는 특정 규칙 세트를 적용하는 설정이며, 이를 extends 옵션에 추가하면 해당 규칙들이 적용된다. 반면에, plugin은 개별적으로 필요한 규칙들을 제공한다. 플러그인만 추가한다고 해서 규칙이 적용되는 것은 아니다. 어떤 규칙이 필요한지 직접 선택해야 한다. 플러그인은 여러 개의 extends를 제공할 수 있다.

{
  "plugins": ["react"],
  "extends": [
  "plugin:react/recommended"
  ],
}

쉽게 설명하자면, extends는 미리 정의된 규칙 세트를 한 번에 적용할 수 있도록 해준다. 마치 미리 준비된 세트 메뉴를 주문하는 것과 비슷하다. 설정 파일 하나만 추가하면 그 안의 규칙들이 모두 적용된다. plugins은 여러 개의 개별 규칙들을 제공하는데, 이 중에서 필요한 규칙들을 직접 골라서 사용할 수 있다. 그냥 플러그인을 설치했다고 해서 규칙이 바로 적용되는 게 아니라, 어떤 규칙들을 사용할지 직접 선택해야 한다. 플러그인은 때로는 설정 파일도 같이 제공해서, 그 설정 파일을 extends에 추가하면 해당 규칙들이 일괄 적용되도록 할 수도 있다.

즉, extends는 규칙들을 쉽게 묶어서 한 번에 적용하는 방법이고, plugins은 개별적인 규칙들을 제공하고 그중에 필요한 것만 선택해서 쓸 수 있는 도구이다.

💡 3. rules (개별 규칙 설정)

rules는 코드 품질, 스타일, 성능 등의 규칙을 개별적으로 설정할 수 있는 요소이다. 각 규칙은 'off', 'warn', 'error' 중 하나의 수준으로 설정할 수 있으며, 배열 형태로 추가적인 옵션을 함께 설정할 수 있다. extends의 규칙보다 우선으로 적용된다는 특징이 있다.

{
  "rules": {
    "no-console": "warn",  // console.log 사용 시 경고
    "eqeqeq": ["error", "always"],  // === 사용 강제
    "indent": ["error", 2],  // 들여쓰기 2칸으로 설정
    "quotes": ["error", "single"],  // 작은 따옴표 사용 강제
    "no-unused-vars": "error"  // 사용되지 않은 변수 금지
  }
}

예시에서 쓰인 규칙들은 ESLint 자체에서 제공하는 내장된 규칙들이다. 즉, 별도의 pluginsextends를 설정하지 않아도 ESLint 기본 규칙들은 사용할 수 있다.

  • 규칙 수준
    • "off": 규칙을 비활성화
    • "warn": 경고 메시지 출력
    • "error": 오류로 처리하여 빌드를 실패하게 만듦

💡 4. ignorePatterns옵션과 .eslintignore 파일 (무시할 파일/폴더 설정)

ignorePatterns는 ESLint가 특정 파일이나 폴더를 검사하지 않도록 할 수 있는 설정이다. node_modules 같은 폴더나 특정 빌드 아티팩트 파일을 ESLint 검사에서 제외할 수 있다. ESLint는 린트(lint)를 수행할 때 특정 파일들을 무시하고 싶다면 설정 파일의 ignorePatterns 옵션을 사용하면 된다.

{
  "ignorePatterns": ["build", "dist", "public"]
}

또는 .eslintignore 파일을 생성해도 동일하게 작동한다.

// 디렉토리 root에 .eslintignore 생성

build
dist
public

💡 5. overrides (개별 환경 설정)

overrides는 특정 파일이나 디렉토리에 대해 다른 ESLint 설정을 적용할 수 있도록 해주는 옵션이다. 기본적으로 설정한 규칙은 프로젝트 전반에 적용되지만, overrides를 사용하면 특정 파일 패턴에 대해 맞춤형 설정을 정의할 수 있다. 이 기능은 파일 타입별로 다른 규칙이 필요할 때 유용하다.

{
  "overrides": [
    {
      "files": ["*.ts", "*.tsx"],
      "rules": {
        "@typescript-eslint/no-unused-vars": ["error"],
        "no-console": "off"
      }
    }
  ]
}

이 예시에서는 .ts.tsx 파일에 대해서 TypeScript 관련 규칙을 적용하고 있다. 이 경우 @typescript-eslint/no-unused-vars 규칙을 설정하고 no-console을 끄고 있다.

ESLint Plugin


앞서 간단히 다뤘지만, Plugin을 어떻게 활용하느냐에 따라 ESLint의 기능과 방향성이 완전히 달라질 수 있기 때문에 해당 옵션에 대한 자세한 설명이 필요하다.

Plugin은 ESLint의 핵심 기능 중 하나로, 추가적인 규칙과 기능을 제공하는 확장 모듈이다. 기본적으로 ESLint가 제공하지 않는 새로운 규칙이나 환경 설정을 추가하거나, 특정 프레임워크와 라이브러리를 지원하는 역할을 한다.

ESLint Plugin은 다른 개발자들이 만들어 공개한 규칙의 모음이며, 기본 ESLint 규칙 외에 더 많은 규칙을 사용할 수 있게 해준다. 이러한 플러그인들은 보통 npm에 라이브러리 형태로 배포되며, npm install 명령어로 설치한 후 .eslintrc 설정 파일의 plugins 섹션에 추가하여 사용할 수 있다. 플러그인이 제공하는 규칙들은 해당 플러그인의 GitHub 저장소나 npm 페이지의 README 파일에서 확인할 수 있다.

플러그인은 기본 ESLint 규칙을 확장하는 역할을 하지만, 단독으로 작동하지 않는다. 플러그인을 설치해도 실제로 규칙을 적용하려면 해당 규칙을 명시적으로 활성화해야 한다. 즉, 플러그인은 규칙을 정의하고 이를 바탕으로 사용자가 직접 활성화할 수 있는 설정을 제공하는 방식이다.

보통 eslint plugin 라이브러리의 이름은 eslint-plugin-{플러그인 이름} 형식을 따르며, .eslintrc 설정 파일의 plugins{플러그인 이름}을 추가하여 사용할 수 있다.

📌 Plugins 설정 방법


ESLint 플러그인을 사용하는 방법은 간단하다.

설치: 플러그인은 보통 npm 또는 yarn을 통해 설치된다. 예를 들어, React와 관련된 ESLint 플러그인을 설치하려면 다음과 같이 실행한다.

npm install eslint-plugin-react --save-dev

.eslintrc 파일에서 설정: 설치가 완료되면 .eslintrc 파일에서 플러그인을 사용하도록 설정할 수 있다. 예를 들어, eslint-plugin-react를 추가하려면 다음과 같이 설정한다.

{
  "plugins": [
    "react"
  ],
  "rules": {
    "react/jsx-uses-react": "error",
    "react/jsx-uses-vars": "error"
  }
}
  • plugins: 플러그인의 이름을 추가한다. 여기서는 "react"를 추가했다.
  • rules: 플러그인이 제공하는 규칙을 명시적으로 활성화해야 한다. 규칙 이름은 일반적으로 플러그인명/규칙명 형식이다. 예를 들어, react/jsx-uses-react와 같은 규칙을 활성화할 수 있다.

자주 쓰는 Plugin


Next.js의 기본 ESLint 설정을 세팅했다면, .eslintrc.json extends"next/typescript"가 추가된다. 해당 extend에 "typescript-eslint", typescript-eslint/parser, "react" plugin이 포함되어 있으니, 해당 plugin들의 설치와 extends, plugins추가를 생략하고 rules를 사용할 수 있다.

📌 typescript-eslint


typescript-eslint는 TypeScript 코드를 검사하는 도구이다. 기본적으로 ESLint는 자바스크립트 코드를 검사하고, TypeScript는 다른 구조로 코드를 분석하는데, 이 둘이 다르게 동작하면서 서로 호환되지 않는 문제가 있다. typescript-eslint는 이 문제를 해결해 두 도구가 함께 사용할 수 있도록 만든 것이다. 즉, TypeScript 코드를 ESLint에서 검사할 수 있게 도와주는 다리 역할을 하는 도구가 typescript-eslint이다.

@typescript-eslint/naming-conventiontypescript-eslint 라이브러리에서 제공하는 규칙이다. 기본 ESLint나 React 플러그인보다 더 세밀하게 이름 규칙을 정의할 수 있는 것이 특징이다. 이 플러그인을 사용하면 변수, 함수, 클래스, 인터페이스 등 다양한 코드 요소에 대해 구체적인 네이밍 규칙을 설정할 수 있어, 특히 팀의 코딩 스타일을 통일하고 코드의 가독성을 높이는 데 유용하다.

기본 ESLint나 React 플러그인에서도 이름 관련 규칙을 설정할 수 있지만, TypeScript의 타입 특성까지 고려한 상세한 규칙을 적용하기 위해 @typescript-eslint/naming-convention이 널리 사용되는 것이다.

💡 typescript-eslint/parser

typescript-eslint를 사용한다는 것은 TypeScript 파일을 ESLint로 분석한다는 의미이다. 이를 위해 @typescript-eslint/parsereslintrc.json "parser"에 반드시 추가해야 한다. 이 파서는 TypeScript 코드를 이해하고 분석할 수 있도록 돕기 때문에, JavaScript에 사용하는 기본 파서로는 TypeScript의 고유 문법을 처리할 수 없기 때문이다.

npm install --save-dev @typescript-eslint/parser
{
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "extends": [
    "plugin:@typescript-eslint/recommended"
  ]
}

💡 선택자(selector)와 형식(format) 옵션

@typescript-eslint/naming-convention은 선택자(selector)와 형식(format)을 사용해 정의된다. 선택자는 이름 규칙이 적용될 대상(예: 변수, 함수, 클래스 등)을 지정하고, 형식은 그 대상이 따라야 할 이름 형식을 정의한다.

예를 들어 변수camelCase 또는 UPPER_CASE로 작성하도록 규칙을 설정할 수 있고, 클래스는 항상 PascalCase로 작성하도록 강제할 수 있다.

💡 형식(format)

format 옵션은 변수, 함수, 클래스 등의 이름을 정할 때 따라야 할 다양한 형식을 정의한다. 예를 들어, 다음과 같은 형식이 있을 수 있다.

  • camelCase: 표준 camelCase 형식 - 문자 사이에 밑줄이 허용되지 않으며, 연속된 대문자가 허용된다(예: myID, myId 모두 유효).
  • PascalCase: camelCase와 동일하지만, 첫 글자가 대문자여야 한다.
  • snake_case: 모든 문자가 소문자여야 하며, 단어 사이에 밑줄이 사용된다.
  • strictCamelCase: camelCase와 동일하지만, 연속된 대문자가 허용되지 않는다(예: myId는 유효하지만 myID는 유효하지 않다).
  • StrictPascalCase: strictCamelCase와 동일하지만 첫 글자가 대문자여야 한다.
  • UPPER_CASE: 모든 문자가 대문자여야 하며, 단어 사이에 밑줄이 사용된다.

💡 선택자(selector)

selector 옵션은 이름 규칙을 강제하려는 대상(변수, 함수, 클래스 등)을 지정하는 설정이다. 예를 들어, 선택자로 function이나 variable을 지정하면 함수나 변수가 이름 규칙에 맞는지 검사하게 된다. 선택자는 하나 또는 여러 개를 배열로 전달할 수 있다.

modifiers는 선택자에 더 구체적인 조건을 추가하는 설정이다. 예를 들어, private, readonly, static 같은 접근성이나 속성을 지정해서 특정한 경우에만 이름 규칙을 적용할 수 있다. 예를 들어 { modifiers: ['private', 'readonly', 'static'] }라고 설정하면 private static readonly로 선언된 항목에만 규칙이 적용되고, 단순히 private로만 선언된 항목에는 적용되지 않는다.

선택자의 형식은 매우 많다. 그 중 대표적으로 많이 쓰이는 형식들은 아래와 같다.

  • variable: 변수에 대한 규칙을 정의할 때 사용된다.
  • function: 함수에 대한 규칙을 정의할 때 사용된다.
  • class: 클래스 선언에 대한 규칙을 정의할 때 사용된다.
  • method: 클래스 메서드에 대한 규칙을 정의할 때 사용된다.
  • parameter: 함수의 매개변수에 대한 규칙을 정의할 때 사용된다.

💡 규칙 설정 예시

module.exports = {
  "plugins": [
    "@typescript-eslint"
  ],
  "extends": [
    "plugin:@typescript-eslint/recommended"
  ],
  "rules": {
    "@typescript-eslint/naming-convention": [
      "error",
      {
        "selector": "variable",
        "format": ["camelCase"],
      }, // 변수 카멜 케이스 강제화
      {
        "selector": "function",
        "format": ["camelCase"],
      }, // 함수 카멜 케이스 강제화
      {
        "selector": "typeLike",
        "format": ["PascalCase"]
      } // 타입 파스칼 케이스 강제화
    ]
  }
};

💡 추가 옵션

@typescript-eslint/naming-convention은 추가적인 세부 옵션을 통해 더 구체적으로 설정할 수 있다. 대표적인 몇 가지는 아래와 같다.

  • leadingUnderscore / trailingUnderscore: 이름의 앞이나 뒤에 밑줄이 허용되는지 여부를 설정한다. 예를 들어, leadingUnderscore: 'allow'는 변수 이름이 _privateVariable처럼 밑줄로 시작할 수 있도록 허용한다.
  • custom: 특정 패턴을 강제하거나 금지하기 위한 사용자 정의 정규식을 설정할 수 있다. 이를 통해 특정 형식을 따르거나 피하도록 이름을 설정할 수 있다.
  • prefix / suffix: 식별자가 특정 접두사나 접미사를 가지도록 설정한다. 예를 들어, { prefix: ['I'] }로 설정하면 인터페이스 이름이 항상 'I'로 시작하도록 강제할 수 있다.

훨씬 더 많은 규칙들은 해당 라이브러리 링크를 통해 전부 확인 가능하다.

📌 eslint-plugin-react, eslint-plugin-react-hooks


eslint-plugin-react,eslint-plugin-react-hooks는 React 컴포넌트와 관련된 코드를 검사하는 데 사용되는 도구이다. 기본 ESLint 설정만으로는 React 특유의 코드 작성 방식에 대한 검사를 정확히 할 수 없기 때문에, 플러그인을 사용하여 React 컴포넌트의 라이프사이클, JSX 문법, hooks 사용에 대한 규칙을 설정하고 검사할 수 있다.

💡 주요 규칙

  • react/jsx-uses-vars: JSX 내에서 정의된 변수들이 실제로 사용되고 있는지 검사하여, 정의되었지만 사용되지 않은 경우를 잡아낸다.
  • react-hooks/rules-of-hooks: hooks는 반드시 최상위에서 호출되어야 하며, 조건문이나 반복문 내에서 사용되면 안 된다. 이 규칙은 hooks의 사용을 올바르게 제한하여 최적의 성능과 예측 가능한 동작을 보장한다.

이 외에도 JSX 속성에 대한 설정, 컴포넌트 이름 규칙, prop-types의 올바른 사용 등에 관한 세밀한 검사 규칙을 제공한다.

💡 규칙 설정 예시

module.exports = {
  "plugins": [
    "react",
    "react-hooks"
  ],
  "extends": [
    "plugin:react/recommended",
    "plugin:react-hooks/recommended"
  ],
  "rules": {
    "react/jsx-uses-vars": "error",
    "react-hooks/rules-of-hooks": "error"
  }
};

📌 eslint-plugin-import


eslint-plugin-import는 import/export 문에서 발생할 수 있는 여러 문제들을 사전에 방지하고, import 문들의 정렬을 자동화하여 코드 일관성을 유지할 수 있다.

💡 주요 규칙

  • import/no-unresolved: 존재하지 않는 모듈을 import하려 할 경우 경고를 발생시켜, 잘못된 파일 경로나 오타로 인해 생길 수 있는 오류를 미리 방지한다.
  • import/order: import 문을 정해진 순서에 맞게 정렬하도록 강제한다. 예를 들어 외부 라이브러리, 내부 모듈, 스타일 시트 등으로 import 문을 구분하고 그 순서를 정하여 코드를 일관되게 유지할 수 있다.
  • import/newline-after-import: 마지막 import 문 이후에는 반드시 한 줄의 공백을 두도록 강제한다. 이는 가독성을 높이고 import 영역과 로직 영역을 명확히 구분하기 위해 사용된다.

💡 규칙 설정 예시

module.exports = {
  "plugins": [
    "import"
  ],
  "extends": [
    "plugin:import/errors",
    "plugin:import/warnings"
  ],
  "rules": {
    "import/no-unresolved": "error",
    "import/order": [
      "warn",
      {
        "groups": [
          "builtin",
          "external",
          "internal"
        ],
        "newlines-between": "always"
      }
    ],
    "import/newline-after-import": "warn"
  }
};

📌 eslint-plugin-check-file


eslint-plugin-check-file은 파일 구조와 파일 이름의 일관성을 유지하기 위해 사용되는 플러그인이다. 특히 대규모 프로젝트에서 파일의 이름 규칙이나 폴더 구조가 일관되지 않으면 유지보수가 어려워질 수 있기 때문에, 이 플러그인을 사용하여 파일의 이름과 구조를 검사하고 규칙을 강제한다.

💡 주요 규칙

  • check-file/filename-naming-convention: 지정된 파일에 대해 파일 이름이 일관된 패턴을 따르도록 강제한다.
  • check-file/folder-naming-convention: 지정된 폴더에 대해 폴더 이름이 일관된 패턴을 따르도록 강제한다.

💡 규칙 설정 예시

module.exports = {
  "plugins": [
    "check-file"
  ],
  "rules": {
    "check-file/naming-convention": [
      "error",
      {
        "regex": "^[a-z]+(?:-[a-z]+)*$",
        "match": true
      }
    ], // 파일 이름을 케밥 케이스로 강제화
    "check-file/folder-match-regex": [
      "error",
      {
        "regex": "^(components|utils)$",
        "match": true
      }
    ] // 특정 폴더 내 파일 형식 제한
  }
};

📌 eslint-plugin-tailwindcss


eslint-plugin-tailwindcss는 Tailwind CSS 클래스의 사용을 검사하는 플러그인이다. Tailwind의 클래스를 사용할 때의 실수를 방지하고, 프로젝트에서 일관성 있는 스타일링을 유지하기 위해 사용된다. 이 플러그인은 올바른 Tailwind CSS 클래스 이름을 사용하도록 보장하고, 잘못된 클래스 이름이나 잘못된 Tailwind CSS 설정을 미리 발견할 수 있다.

💡 주요 규칙

  • tailwindcss/classnames-order: Tailwind CSS 클래스 이름이 지정된 순서대로 배치되었는지 검사한다.
  • tailwindcss/no-custom-classname: Tailwind의 기본 클래스 이름 외에 임의의 사용자 정의 클래스 이름 사용을 방지한다.

💡 규칙 설정 예시

module.exports = {
  "plugins": [
    "tailwindcss"
  ],
  "extends": [
    "plugin:tailwindcss/recommended"
  ],
  "rules": {
    "tailwindcss/classnames-order": "warn",
    "tailwindcss/no-custom-classname": "off"
  }
};

📌 tanstack/eslint-plugin-query


tanstack/eslint-plugin-query는 TanStack Query (구 React Query) 관련 코드를 검사하기 위한 ESLint 플러그인이다. TanStack Query는 비동기 데이터 관리를 간소화하기 위해 사용되며, 이 플러그인은 사용자가 TanStack Query를 올바르게 사용하고 있는지 검사하여 코드의 품질과 일관성을 유지하도록 돕는다.

💡 주요 규칙

  • tanstack/query/exhaustive-deps :쿼리 함수에서 사용하는 모든 변수를 쿼리 키에 추가하도록 강제한다.
  • tanstack/query/stable-query-client : QueryClient를 애플리케이션의 전체 생명주기 동안 하나의 인스턴스만 생성되게 강제한다.

💡 규칙 설정 예시

module.exports = {
  "plugins": [
    "@tanstack/query"
  ],
  "extends": [
    "plugin:@tanstack/query/recommended"
  ],
  "rules": {
	  "tanstack/query/exhaustive-deps",
	  "tanstack/query/stable-query-client"
  }
};

참고 문헌


0개의 댓글