ESLint 설정 커스터마이징: 프로젝트별 규칙 정의
ESLint 설정을 커스터마이징하여 프로젝트에 맞는 코드 품질 규칙을 정의하는 방법을 설명합니다.
ESLint 설정 커스터마이징
Introduction
ESLint는 JavaScript/TypeScript 코드의 품질을 유지하고 일관된 코드 스타일을 강제하는 강력한 린팅 도구입니다. 프로젝트의 요구사항에 맞게 규칙을 커스터마이징하면 개발 생산성과 코드 품질을 크게 향상시킬 수 있습니다. 이 글에서는 ESLint 설정 커스터마이징과 실용적인 규칙 정의 방법을 다루겠습니다.
Environment
// package.json
{
"name": "my-project",
"devDependencies": {
"eslint": "^8.56.0",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0"
}
}npx eslint --version
# v8.56.0Problem
기본 ESLint 설정의 한계:
// .eslintrc.js (기본 설정)
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended'
]
};
// 문제점:
// 1. 프로젝트 맞춤 규칙 부족
// 2. import 정렬 미적용
// 3. 커스텀 네이밍 컨벤션 없음
// 4. 레거시 코드와 새 코드 구분 없음# 기본 설정으로 검사 시
npx eslint src/
# 결과: 247 warnings, 12 errors
# 프로젝트에 맞지 않는 규칙들 포함Analysis
ESLint 설정 구조를 분석했습니다:
// ESLint 설정 파일 우선순위
// 1. .eslintrc.js
// 2. .eslintrc.cjs
// 3. .eslintrc.yaml
// 4. .eslintrc.yml
// 5. .eslintrc.json
// 6. package.json의 "eslintConfig"
// flat config (ESLint 9+)
// eslint.config.js
// 규칙 충돌 해결
// extends 배열의 마지막 요소가 가장 높은 우선순위// 프로젝트 구조 확인
{
"src": {
"components": [],
"hooks": [],
"utils": [],
"types": []
}
}Solution
1단계: 기본 커스텀 설정
// .eslintrc.js
module.exports = {
root: true,
env: {
browser: true,
node: true,
es2022: true
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'prettier' // 반드시 마지막에 위치
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
settings: {
react: {
version: 'detect'
}
},
rules: {
// TypeScript 관련 규칙
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/consistent-type-imports': 'error',
// 일반 규칙
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-debugger': 'error',
'no-duplicate-imports': 'error',
'prefer-const': 'error',
'no-var': 'error'
},
overrides: [
{
files: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'no-console': 'off'
}
}
]
};2단계: Import 정렬 규칙
// .eslintrc.js (import 플러그인 추가)
module.exports = {
// ... 기존 설정
plugins: ['import'],
rules: {
// import 정렬 규칙
'import/order': ['error', {
groups: [
'builtin',
'external',
'internal',
['parent', 'sibling'],
'index',
'type'
],
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true
}
}],
'import/no-duplicates': 'error',
'import/no-unresolved': 'error',
'import/no-cycle': 'error',
'import/no-self-import': 'error'
}
};
// 결과:
// import React from 'react';
//
// import { Button } from '@/components/Button';
//
// import { useAuth } from '@/hooks/useAuth';
//
// import type { User } from '@/types/user';3단계: 커스텀 규칙 생성
// .eslintrc.js
module.exports = {
rules: {
// 네이밍 컨벤션 규칙
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'default',
format: ['camelCase']
},
{
selector: 'variable',
format: ['camelCase', 'UPPER_CASE']
},
{
selector: 'parameter',
format: ['camelCase'],
leadingUnderscore: 'allow'
},
{
selector: 'typeLike',
format: ['PascalCase']
},
{
selector: 'enumMember',
format: ['PascalCase']
},
{
selector: 'property',
format: null // 프로퍼티는 검사하지 않음
}
],
// 함수명 규칙
'func-names': ['error', 'always'],
// 컴포넌트 규칙 (React)
'react/function-component-definition': ['error', {
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function'
}]
}
};4단계: 프로젝트별 오버라이드
// .eslintrc.js
module.exports = {
overrides: [
// 테스트 파일
{
files: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts'],
env: {
jest: true
},
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'no-console': 'off'
}
},
// 설정 파일
{
files: ['**/*.config.js', '**/*.config.ts'],
env: {
node: true
},
rules: {
'@typescript-eslint/no-var-requires': 'off',
'no-console': 'off'
}
},
// 레거시 코드
{
files: ['src/legacy/**/*.js'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'no-undef': 'off',
'prefer-const': 'warn'
}
},
// 스크립트
{
files: ['scripts/**/*.js'],
env: {
node: true
},
rules: {
'no-console': 'off'
}
}
]
};Lessons Learned
- extends 순서:
prettier는 반드시 마지막에 위치하여 포맷팅 규칙이 충돌하지 않도록 해야 합니다 - 오버라이드 활용: 파일 유형별로 다른 규칙을 적용하여 프로젝트 구조에 맞는 설정이 가능합니다
- 커스텀 규칙: 프로젝트 컨벤션에 맞는 커스텀 규칙을 생성하면 코드 일관성을 유지할 수 있습니다
- IDE 통합: ESLint를 IDE와 통합하면 실시간으로 코드 품질 문제를 발견할 수 있습니다
- 점진적 적용: 기존 코드베이스에서는
--fix옵션을 활용하여 점진적으로 규칙을 적용해야 합니다
이 블로그는 외부 스폰서십, 제휴 마케팅 또는 광고 수익을 받지 않습니다.