
๐ ๋ค์ด๊ฐ๋ฉฐ
์ง๋ ํฌ์คํ ์์ ESLint v9 ๊ณต์ ๋ฌธ์๋ฅผ ์ดํด๋ดค์ผ๋,
์ด๋ฒ ํฌ์คํ ์์๋ ๋ด๊ฐ ์ค์ ํ๋ก์ ํธ์์ ์ ์ฉํ๋ ค๊ณ ์ค์ ํ eslint.config.js๋ฅผ ๋ฐํ์ผ๋ก
์ ์ด๋ ๊ฒ ์ค์ ์ ํ๋์ง, ๊ฐ ์ต์ ์ด ์ด๋ค ์ญํ ์ ํ๋์ง๋ฅผ ์ ๋ฆฌํด๋ณด๋ ค ํ๋ค.
[Frontend] ESLint v9 ๊ณต์ ๋ฌธ์ ๋ฏ์ด๋ณด๊ธฐ
๋ชฉ์ฐจ (OPEN)๐ ๋ค์ด๊ฐ๋ฉฐ๐ฅ typescript-eslint๋๐ง defineConfig( )์ tseslint.config( )์ ๊ด๊ณ๐จ Flat Config์ ๋ฑ์ฅ๊ณผ ๋ณํ์ ๋ฐฐ๊ฒฝ๐ฉ Vite ํ ํ๋ฆฟ์ด ๋ง๋ค์ด์ค ๊ธฐ๋ณธ ESLint ์ค์ ๋ฏ์ด๋ณด๊ธฐ๐ฆ ์ฒ์ ๋์ก๋ ๋ค์ฏ ๊ฐ
sso-codingdiary.tistory.com
๐ฅ ํ๊ฒฝ
- ์คํ: Vite + React + TypeScript
- ESLint: v9 (Flat Config ๊ธฐ๋ฐ)
๐ง ์ต์ข ์ค์ ํ์ผ
import js from '@eslint/js'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import reactPlugin from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
// 1) ๋ฆฐํ
๋ฒ์ ์ต์ํ
globalIgnores(['dist', 'node_modules']),
// 2) React + TS ํ์ผ์๋ง ๋ฃฐ ์ ์ฉ
{
files: ['**/*.{ts,tsx}'],
// d.ts๋ ํ์
์ ์ธ ํ์ผ์ด๋ผ ๊ท์น ์ ์ฉ ๋์์์ ์ ์ธ
ignores: ['**/*.d.ts'],
// 3) ์ถ์ฒ ๊ท์น ์ธํธ ํ์ฅ
extends: [
js.configs.recommended,
...tseslint.configs.recommended, // TS ์ถ์ฒ ๋ฃฐ(๋ฐฐ์ด์ด๋ผ ์ ๊ฐ)
reactPlugin.configs.flat.recommended, // React ๊ธฐ๋ณธ ๊ถ์ฅ
reactHooks.configs['recommended-latest'], // Hooks ๊ท์น
reactRefresh.configs.vite, // Vite + Fast Refresh ์ ์ฝ
],
// 4) ์ฝ๋ ์ดํด ๋ฅ๋ ฅ ์ํฅ (TS ๋ฌธ๋ฒ/ํ์
์ธ์ง)
languageOptions: {
ecmaVersion: 2020,
sourceType: 'module', // import/export ๊ตฌ๋ฌธ ์ธ์
parser: tseslint.parser, // TS ๋ฌธ๋ฒ ํ์ฑ
parserOptions: { projectService: true }, // TSConfig ๊ธฐ๋ฐ์ ํ์
์ธ์ง ๋ฃฐ ON
globals: { ...globals.browser }, // window, document ๋ฑ ์ ์ญ ์๋ณ์ ์ธ์
},
// 5) React ๋ฒ์ ์ธ์ง (๋ฃฐ ๋์์ด ๋ฒ์ ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง)
settings: {
react: { version: 'detect' },
},
// 6) ์ปค์คํ
๋ฃฐ
rules: {
// ์ด์ ๋ฐฐํฌ ์ ๋จ๊ธฐ๊ณ ์ถ์ ์ฝ์๋ง ํ์ฉ
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-debugger': 'error',
// React 17+ ๋ถํฐ๋ import React ์๋ต ๊ฐ๋ฅํ๋ฏ๋ก ๋นํ์ฑํ
'react/react-in-jsx-scope': 'off',
// TS๋ก PropTypes ๋์ฒด
'react/prop-types': 'off',
// ๋ฏธ์ฌ์ฉ ๋ณ์ ์ก๋, ์๋์ ๋ฏธ์ฌ์ฉ(_prefix)์ ํ์ฉ
'@typescript-eslint/no-unused-vars': [
'warn',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
// any ๋จ๋ฐ ๋ฐฉ์ง (ํ ์ํฉ ๋ฐ๋ผ 'error'๋ก ๊ฒฉ์ ๊ฐ๋ฅ)
'@typescript-eslint/no-explicit-any': 'warn',
},
},
])
๊ฒฐ๋ก ๋ถํฐ ๋งํ์๋ฉด, ์์ ๊ฐ์ด ์ค์ ํจ์ผ๋ก์จ
๊ฒ์ฌ ๋ฒ์๋ฅผ ์ค์ฌ์ ์๋๋ฅผ ํ๋ณดํ๊ณ , ESLint๊ฐ TS ํ์ ์ ๋ณด๊น์ง ์ธ์งํด์ ๊ท์น์ ๋ ์ ํํ ์ ์ฉํ๋๋ก ํด์ฃผ์๋ค.
๐จ ์ค์ ์ค๋ช
1. globalIgnores(['dist', 'node_modules'])
ESLint๊ฐ ๊ฒ์ฌํ์ง ์์๋ ๋๋ ํด๋๋ฅผ ์ง์ ํด์ค๋ค.
๋ณดํต ๋น๋ ๊ฒฐ๊ณผ๋ฌผ(dist)๊ณผ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(node_modules)๋ ์๋ ์์ฑ๋ ์ฝ๋์ด๋ฏ๋ก
๋ฆฐํธํ ํ์๊ฐ ์์ด์ ์ ์ธํด์ฃผ์๋ค.
2. extends
์ด๋ค ๊ท์น ์ธํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฒ์ฌํ ์ง๋ฅผ ์ ํด์ฃผ๋ ๊ฒ์ผ๋ก,
๋ด ์ค์ ์ ์ฌ์ฉ๋ ๊ฐ๊ฐ์ ์๋ฏธ๋ ๋ค์๊ณผ ๊ฐ๋ค.
| ์ค์ | ์ค๋ช |
| js.configs.recommended | ์๋ฐ์คํฌ๋ฆฝํธ ๊ธฐ๋ณธ ๊ถ์ฅ ๊ท์น |
| tseslint.configs.recommended | ํ์ ์คํฌ๋ฆฝํธ ๋ฌธ๋ฒ + ํ์ ๊ธฐ๋ฐ ๊ท์น |
| reactPlugin.configs.flat.recommended | React ์ ์ฉ ๊ท์น |
| reactHooks.configs['recommended-latest'] | useEffect, useState ๊ฐ์ ํ ๊ด๋ จ ๊ท์น |
| reactRefresh.configs.vite | Vite์ Fast Refresh ์์ ์ฑ ์ฒดํฌ์ฉ |
3. languageOptions
ESLint๊ฐ ์ฝ๋๋ฅผ ์ด๋ค ๋ฌธ๋ฒ๊ณผ ํ๊ฒฝ์ผ๋ก ์ดํดํ ์ง๋ฅผ ์ค์ ํด์ค๋ค.
์๋์ ๊ฐ์ด ์ค์ ํด์ค์ผ๋ก์จ, ESLint๊ฐ 'TypeScript๋ฅผ ์ฌ์ฉํ๋ ๋ธ๋ผ์ฐ์ ๊ธฐ๋ฐ ํ๋ก์ ํธ'์์ ์ดํดํ ์ ์๊ฒ ํด์ฃผ์๋ค.
| ํค | ์ญํ |
| ecmaVersion: 2020 | ์ต์ JS ๋ฌธ๋ฒ ์ง์ |
| sourceType: 'module' | import/export ๊ตฌ๋ฌธ ์ฌ์ฉ ํ์ฉ |
| parser: tseslint.parser | TS ๋ฌธ๋ฒ ํด์ (๊ธฐ๋ณธ ESLint๋ TS ๋ชจ๋ฆ) |
| parserOptions.projectService: true | TS ํ์ ์ ๋ณด ๊ธฐ๋ฐ ๊ท์น ํ์ฑํ |
| globals.browser | window, document ๋ฑ ๋ธ๋ผ์ฐ์ ์ ์ญ ์๋ณ์ ์ธ์ |
4. settings.react.version: 'detect'
React ๊ด๋ จ ๊ท์น๋ค์ ๋ฒ์ ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ๋์ํ๊ธฐ ๋๋ฌธ์
๋ฒ์ ์ ์๋์ผ๋ก ๊ฐ์งํ๊ฒ ํด์ฃผ๋ ์ต์ ์ ์ถ๊ฐํด์ฃผ์๋ค.
์๋ฅผ ๋ค์ด, React 17 ์ดํ๋ถํฐ๋ JSX๋ฅผ ์ฌ์ฉํ ๋ import React from 'react'๊ฐ ํ์ ์๊ธฐ ๋๋ฌธ์
์ด ์ต์
์ด ์์ผ๋ฉด ๋ถํ์ํ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์๋ ์๋ค๊ณ ํ๋ค.
5. rules
ํ์์ ์์ฃผ ๊ฒช๋ ํจํด์ ์ง์ ์กฐ์ ํ ์ ์๋ ๊ฒ์ผ๋ก, ๋ด๊ฐ ์ฌ์ฉํ ๊ท์น์ ์๋์ ๊ฐ๋ค.
| ๊ท์น | ์๋ฏธ |
| no-console | ๊ฐ๋ฐ์ฉ ๋ก๊ทธ๋ ํ์ฉ, ๋ฐฐํฌ์ฉ์ ๊ฒฝ๊ณ |
| no-debugger | ๋ฐฐํฌ ์ฝ๋์์ ๋๋ฒ๊ฑฐ ๊ธ์ง |
| react/react-in-jsx-scope | React 17 ์ด์์์๋ JSX ์ฌ์ฉ ์ import React๊ฐ ํ์ ์์ผ๋ฏ๋ก ๋นํ์ฑํ |
| react/prop-types | TypeScript ํ๊ฒฝ์์๋ PropTypes ๋์ TS ํ์ ์ฌ์ฉ |
| @typescript-eslint/no-unused-vars | ์ฌ์ฉํ์ง ์๋ ๋ณ์ ์ก๊ธฐ (๋จ, _๋ก ์์ํ๋ฉด ๋ฌด์) |
| @typescript-eslint/no-explicit-any | any ํ์ ์ฌ์ฉ ์ ๊ฒฝ๊ณ (์ ์ง์ ๊ฐ์ ์ฉ) |
๐ ๊ฒฐ๋ก
๋ด ์ค์ ์ ํต์ฌ์ ์ ํํ ๊ฒ์ฌ + ๋น ๋ฅธ ์๋ + ํ์ค์ ์ธ ๊ท์น์ด์๋ค.
globalIgnores๋ก ๊ฒ์ฌ ๋ฒ์๋ฅผ ์ค์ด๊ณ ,
typescript-eslint๋ก ํ์ ๊ธฐ๋ฐ ๊ท์น์ ํ์ฑํ ํ๋ฉฐ,
extends๋ก React / TS ์ ์ฉ ๊ท์น์ ํ ๋ฒ์ ๋ง์์์ฃผ์๋ค.
ํ์คํ ๊ณต์ ๋ฌธ์๋ฅผ ํ ๋ฒ ๊ผผ๊ผผํ ๋ณธ ํ์ ์ง์ ์ค์ ์ ์์ฑํ๋๊น
๊ทธ๋์ ๋จ๋ค์ด ์์ฑํด๋ ์ค์ ์ ๊ฐ์ ธ์์ ์ฐ๋ฉด์
'๊ฐ์๊ธฐ ์ฌ๊ธฐ์๋ ์๋ฌ๊ฐ ์ ๋๋ ๊ฑฐ์ง ..' ํ๋ ์ํฉ์ด ์ค์ด๋ค ๊ฒ ๊ฐ๋ค.
'๐ป๊ณต๋ถ ๊ธฐ๋ก > ๐ Frontend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Frontend] 3D ๋ก๊ณ ํ์ด๋์ธ ์ต์ ํ (0) | 2025.11.14 |
|---|---|
| [Frontend] ๋ธ๋ ๋์์ ๋ชจ๋ธ ์ ์ํ๊ณ ์น์ผ๋ก ๋์ฐ๊ธฐ (1) | 2025.11.14 |
| [Frontend] ESLint v9 ๊ณต์ ๋ฌธ์ ๋ฏ์ด๋ณด๊ธฐ (0) | 2025.11.12 |
| [Frontend] React + TypeScript ํ๋ก์ ํธ ๋ง๋ค๊ณ GitHub์ ์ฌ๋ฆฌ๊ธฐ (0) | 2025.11.11 |
| [Frontend] localStorage VS sessionStorage VS cookies (0) | 2025.06.28 |