본문 바로가기
카테고리 없음

ESLint 에 대하여

by SuldenLion 2026. 5. 26.
반응형

ESLint — JavaScript 코드 품질의 수호자

프론트엔드 개발 · 프론트엔드 도구 시리즈 #4
버그가 되기 전에 잡아내고, 코드 스타일을 자동으로 통일하며, 팀의 모든 개발자가 같은 기준으로 코드를 작성하게 만드는 ESLint. 기본 설정부터 Flat Config, 커스텀 규칙까지 완전히 마스터합니다.


1. ESLint란 무엇인가

ESLint는 JavaScript(와 TypeScript) 코드를 정적 분석해 문제를 찾아내는 린터(Linter) 입니다. 2013년 Nicholas C. Zakas가 만들었으며, 현재 프론트엔드 생태계에서 가장 널리 쓰이는 코드 품질 도구입니다.

린터가 잡아내는 문제는 두 가지입니다.

코드 오류(Bugs): 런타임에 문제를 일으킬 가능성이 있는 코드 패턴

  • == 대신 === 사용 권고
  • undefined 변수 참조 감지
  • 사용하지 않는 변수 경고
  • async 함수에서 await 없이 Promise 반환

스타일 문제(Style): 가독성과 일관성에 관한 규칙

  • 들여쓰기 방식
  • 세미콜론 유무
  • 따옴표 종류 (단일 vs 이중)

참고: ESLint v8까지는 스타일 규칙도 다뤘지만, v9부터는 스타일 포매팅을 Prettier에 위임하는 방향으로 정리되고 있습니다. ESLint는 코드 품질에, Prettier는 포매팅에 집중합니다.


2. 설치 및 초기 설정

# 설치
npm install -D eslint

# 대화형 초기화 (권장)
npx eslint --init
# 또는 (v9+)
npm init @eslint/config@latest

초기화 마법사가 프로젝트 유형을 물어보고 기본 설정을 생성해줍니다.

기본 CLI 명령어

# 특정 파일/디렉토리 린트
npx eslint src/
npx eslint src/app.js
npx eslint "src/**/*.{js,jsx,ts,tsx}"

# 자동 수정 가능한 문제 수정
npx eslint --fix src/

# 설정 디버그
npx eslint --print-config src/app.js   # 적용되는 설정 출력

# 캐시 사용 (대규모 프로젝트에서 속도 개선)
npx eslint --cache src/

3. Flat Config (v9 신규 방식)

ESLint v9부터 Flat Config가 기본 설정 방식이 되었습니다. 기존 .eslintrc.* 형식을 대체합니다.

기존 방식 (.eslintrc.js) — 레거시

// .eslintrc.js (v8 이하, 레거시)
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['react', '@typescript-eslint'],
  rules: {
    'no-console': 'warn',
    'no-unused-vars': 'error',
  },
  overrides: [
    {
      files: ['*.ts', '*.tsx'],
      rules: { 'no-unused-vars': 'off' },
    },
  ],
};

Flat Config (eslint.config.js) — 현재 표준

// eslint.config.js (v9+, 현재 표준)
import js from '@eslint/js';
import globals from 'globals';

export default [
  // 1. 전역 무시 파일 설정
  {
    ignores: ['dist/**', 'node_modules/**', '*.min.js'],
  },

  // 2. 기본 JS 권장 설정
  js.configs.recommended,

  // 3. 커스텀 설정
  {
    files: ['src/**/*.{js,jsx}'],
    languageOptions: {
      ecmaVersion: 2024,
      sourceType: 'module',
      globals: {
        ...globals.browser,
        ...globals.node,
      },
    },
    rules: {
      'no-console': 'warn',
      'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
      'prefer-const': 'error',
      'no-var': 'error',
    },
  },

  // 4. 테스트 파일 별도 설정
  {
    files: ['**/*.test.{js,ts}', '**/*.spec.{js,ts}'],
    languageOptions: {
      globals: globals.jest,
    },
    rules: {
      'no-console': 'off',
    },
  },
];

Flat Config의 핵심 변화

파일 기반 배열 구조: 설정이 배열로 순서대로 적용됩니다. 뒤의 설정이 앞의 설정을 덮어씁니다.

extends 대신 배열 스프레드: 공유 설정을 ...plugin.configs.recommended처럼 배열에 펼칩니다.

env 대신 globals: 전역 변수는 globals 패키지를 통해 명시적으로 선언합니다.

parser 대신 languageOptions.parser: 파서 설정이 명확한 위치에 있습니다.


4. 핵심 규칙 이해하기

규칙 심각도 수준

'no-console': 0          // off: 비활성화
'no-console': 1          // warn: 경고 (빌드 중단 안 함)
'no-console': 2          // error: 오류 (CI에서 빌드 중단)

'no-console': 'off'      // 문자열도 가능
'no-console': 'warn'
'no-console': 'error'

// 옵션이 있는 규칙
'no-unused-vars': ['error', { varsIgnorePattern: '^_' }]

꼭 알아야 할 핵심 규칙

코드 품질:

{
  // 오류 가능성
  'no-undef':              'error',  // 선언되지 않은 변수 사용 금지
  'no-unused-vars':        'error',  // 사용하지 않는 변수 금지
  'no-unreachable':        'error',  // 도달 불가능한 코드 금지
  'no-duplicate-case':     'error',  // switch 중복 case 금지
  'no-empty':              'error',  // 빈 블록 금지
  'no-constant-condition': 'error',  // 항상 참/거짓인 조건 금지

  // 모던 자바스크립트
  'prefer-const':          'error',  // 재할당 없으면 const 사용
  'no-var':                'error',  // var 대신 let/const
  'prefer-arrow-callback': 'warn',   // 콜백에 화살표 함수 권장
  'object-shorthand':      'warn',   // { foo: foo } 대신 { foo }
  'prefer-template':       'warn',   // 문자열 연결 대신 템플릿 리터럴

  // 비동기
  'no-async-promise-executor':  'error',  // new Promise(async () => {}) 금지
  'require-await':              'warn',   // await 없는 async 함수 경고
  'no-return-await':            'warn',   // return await 불필요 경고
  'prefer-promise-reject-errors': 'warn', // reject에 Error 객체 사용 권장
}

규칙 인라인 비활성화

// 한 줄 비활성화
const password = '1234'; // eslint-disable-line no-hardcoded-credentials

// 다음 줄 비활성화
// eslint-disable-next-line no-console
console.log('디버그용');

// 블록 비활성화
/* eslint-disable no-console */
console.log('시작');
console.log('끝');
/* eslint-enable no-console */

// 파일 전체 비활성화 (권장하지 않음)
/* eslint-disable */

5. 플러그인과 공유 설정

플러그인

플러그인은 ESLint에 새로운 규칙을 추가합니다.

npm install -D eslint-plugin-import
npm install -D eslint-plugin-jsx-a11y
npm install -D eslint-plugin-unicorn
// eslint.config.js
import importPlugin from 'eslint-plugin-import';
import a11yPlugin  from 'eslint-plugin-jsx-a11y';

export default [
  {
    plugins: {
      import:  importPlugin,
      'jsx-a11y': a11yPlugin,
    },
    rules: {
      // import 관련 규칙
      'import/no-duplicates':       'error',   // 중복 import 금지
      'import/no-unused-modules':   'warn',    // 사용 안 하는 모듈 경고
      'import/order': ['warn', {               // import 순서 정렬
        'groups': ['builtin', 'external', 'internal', 'parent', 'sibling'],
        'newlines-between': 'always',
      }],

      // 접근성 규칙
      'jsx-a11y/alt-text':         'error',   // img에 alt 필수
      'jsx-a11y/anchor-is-valid':  'error',   // a 태그 href 필수
    },
  },
];

공유 설정 (Shared Configs)

여러 규칙 묶음을 한 번에 적용하는 미리 만들어진 설정입니다.

# 자주 쓰는 공유 설정들
npm install -D @eslint/js                    # 공식 기본 권장
npm install -D eslint-config-airbnb          # Airbnb 스타일 가이드
npm install -D eslint-config-airbnb-base     # (React 없는 버전)
npm install -D eslint-config-google          # Google 스타일 가이드
npm install -D eslint-config-standard        # Standard JS 스타일
// Airbnb 설정 사용 예 (Flat Config 호환 버전)
import airbnb from 'eslint-config-airbnb';

export default [
  ...airbnb,
  {
    rules: {
      // Airbnb 규칙 중 팀에 맞게 일부 조정
      'no-console': 'warn',
      'react/react-in-jsx-scope': 'off',  // React 17+ 자동 import
    },
  },
];

6. TypeScript와 ESLint

TypeScript 프로젝트에서 ESLint를 사용하려면 @typescript-eslint 패키지가 필요합니다.

npm install -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
# 또는 v7+에서는 typescript-eslint 통합 패키지
npm install -D typescript-eslint
// eslint.config.js
import tseslint from 'typescript-eslint';
import js from '@eslint/js';

export default tseslint.config(
  js.configs.recommended,
  ...tseslint.configs.recommended,  // TypeScript 권장 규칙

  // 타입 정보 사용 규칙 (tsconfig.json 필요)
  ...tseslint.configs.recommendedTypeChecked,

  {
    languageOptions: {
      parserOptions: {
        project: './tsconfig.json',    // 타입 정보 활성화
        tsconfigRootDir: import.meta.dirname,
      },
    },
    rules: {
      // TypeScript 전용 규칙
      '@typescript-eslint/no-explicit-any':    'warn',
      '@typescript-eslint/no-unused-vars':     ['error', { argsIgnorePattern: '^_' }],
      '@typescript-eslint/explicit-function-return-type': 'off',
      '@typescript-eslint/no-non-null-assertion': 'warn',

      // 타입 정보가 필요한 규칙
      '@typescript-eslint/await-thenable':          'error',
      '@typescript-eslint/no-floating-promises':    'error',
      '@typescript-eslint/no-misused-promises':     'error',
      '@typescript-eslint/require-await':           'warn',
      '@typescript-eslint/no-unnecessary-type-assertion': 'warn',

      // JS 기본 규칙 비활성화 (TS 버전으로 대체)
      'no-unused-vars': 'off',       // @typescript-eslint/no-unused-vars 사용
    },
  },
);

7. React + Next.js 설정

React 설정

npm install -D eslint-plugin-react eslint-plugin-react-hooks
// eslint.config.js
import tseslint   from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';
import hooksPlugin  from 'eslint-plugin-react-hooks';

export default tseslint.config(
  // ... 기본 설정

  {
    files: ['**/*.{jsx,tsx}'],
    plugins: {
      react:        reactPlugin,
      'react-hooks': hooksPlugin,
    },
    settings: {
      react: { version: 'detect' },  // React 버전 자동 감지
    },
    rules: {
      // React 기본
      ...reactPlugin.configs.recommended.rules,
      'react/react-in-jsx-scope':   'off',   // React 17+ 자동 import
      'react/prop-types':           'off',   // TypeScript 사용 시 불필요
      'react/display-name':         'off',
      'react/no-unescaped-entities': 'warn',
      'react/self-closing-comp':    'warn',  // <div></div> → <div />

      // React Hooks 규칙
      'react-hooks/rules-of-hooks':  'error',  // Hooks 규칙 강제
      'react-hooks/exhaustive-deps': 'warn',   // useEffect 의존성 검사
    },
  },
);

Next.js 전용 설정

npm install -D eslint-config-next
// eslint.config.js (Next.js)
import { FlatCompat } from '@eslint/eslintrc';
import nextPlugin from 'eslint-config-next';

const compat = new FlatCompat();

export default [
  // eslint-config-next는 아직 Flat Config 완전 지원 전
  ...compat.extends('next/core-web-vitals'),
  {
    rules: {
      '@next/next/no-html-link-for-pages': 'error',
      '@next/next/no-img-element': 'warn',  // Image 컴포넌트 사용 권고
    },
  },
];

Next.js 15+에서는 자체적으로 ESLint를 자동 설정합니다.

# Next.js 프로젝트에서
npx next lint         # 자동으로 ESLint 실행
npx next lint --fix   # 자동 수정 포함

8. ESLint와 Prettier의 관계

ESLint와 Prettier는 종종 충돌합니다. 둘 다 코드 스타일을 다루기 때문입니다.

충돌 해결

# ESLint의 포매팅 규칙을 비활성화해 Prettier에 위임
npm install -D eslint-config-prettier
// eslint.config.js
import prettierConfig from 'eslint-config-prettier';

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  // ... 다른 설정들

  // 반드시 마지막에 배치: ESLint의 포매팅 규칙 모두 비활성화
  prettierConfig,
];

eslint-plugin-prettier (대안적 접근)

Prettier를 ESLint 규칙으로 실행합니다. Prettier 포매팅 위반을 ESLint 오류로 표시합니다.

npm install -D eslint-plugin-prettier
import prettierPlugin from 'eslint-plugin-prettier';
import prettierConfig from 'eslint-config-prettier';

export default [
  // ...
  prettierConfig,
  {
    plugins: { prettier: prettierPlugin },
    rules: {
      'prettier/prettier': 'warn',  // Prettier 위반을 ESLint 경고로
    },
  },
];

현재 권장 방식: ESLint에서 스타일 규칙은 비활성화하고(eslint-config-prettier), Prettier는 별도 명령어나 pre-commit hook에서 실행합니다. 두 도구의 역할을 명확히 분리하는 것이 더 깔끔합니다.


9. IDE 통합과 자동 수정

VS Code 설정

# VS Code ESLint 확장 설치
code --install-extension dbaeumer.vscode-eslint
// .vscode/settings.json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"  // 저장 시 ESLint 자동 수정
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true
}

package.json 스크립트

{
  "scripts": {
    "lint":        "eslint src --ext .js,.jsx,.ts,.tsx",
    "lint:fix":    "eslint src --ext .js,.jsx,.ts,.tsx --fix",
    "lint:report": "eslint src --format json --output-file eslint-report.json"
  }
}

Pre-commit Hook (lint-staged + husky)

npm install -D husky lint-staged
npx husky init
// package.json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md,css}": [
      "prettier --write"
    ]
  }
}
# .husky/pre-commit
npx lint-staged

이제 git commit 시 변경된 파일에만 ESLint와 Prettier가 자동 실행됩니다.

CI 파이프라인

# .github/workflows/lint.yml
name: Lint

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci
      - run: npm run lint     # 실패 시 PR 블록

      # 리포트 저장 (선택)
      - run: npm run lint:report
      - uses: actions/upload-artifact@v4
        with:
          name: eslint-report
          path: eslint-report.json

10. 결론

ESLint는 JavaScript/TypeScript 프로젝트에서 코드 품질의 첫 번째 방어선입니다. 제대로 설정된 ESLint는 수많은 잠재적 버그를 PR 검토 전에 잡아내고, 팀 코딩 컨벤션을 자동으로 강제합니다.

설정 체크리스트

  • [ ] eslint.config.js (Flat Config) 사용
  • [ ] TypeScript 프로젝트라면 typescript-eslint 설정
  • [ ] React 프로젝트라면 react-hooks/exhaustive-deps 활성화
  • [ ] eslint-config-prettier로 Prettier 충돌 방지
  • [ ] VS Code 저장 시 자동 수정 설정
  • [ ] lint-staged + husky로 pre-commit hook 설정
  • [ ] CI 파이프라인에 lint 단계 포함

한 줄 요약: ESLint는 "코드 리뷰어가 지적하기 전에 미리 지적하는" 도구입니다. 팀 컨벤션을 코드로 문서화하고 자동으로 강제하는 가장 효율적인 방법입니다.


 

반응형

댓글