ESLint v10 — eslintrc의 시대가 끝났다

9 min read
ESLintFlat ConfigJavaScript린터
ESLint v10 — eslintrc의 시대가 끝났다

.eslintrc, 이제 진짜 안 된다

ESLint v9에서 flat config가 기본이 됐을 때, .eslintrc는 "권장하지 않음" 상태였다. v10에서는 완전히 제거됐다. .eslintrc.json, .eslintrc.js, .eslintignore 파일이 프로젝트에 있어도 ESLint가 읽지 않는다. 비유하면 구형 열쇠가 아예 자물쇠에 안 맞게 된 것이다.

설치.sh
npm install eslint@10 --save-dev

제거된 것들

v10에서 사라진 레거시 목록이 길다. 한눈에 정리하면 이렇다.

제거 항목설명
.eslintrc.* 파일flat config(eslint.config.js)만 인식
.eslintignore 파일config 내 ignores 배열로 대체
/* eslint-env */ 주석languageOptions.globals로 대체
--no-eslintrc CLI 플래그의미 없어짐
--env CLI 플래그config에서 직접 설정
--rulesdir CLI 플래그플러그인으로 대체
ESLINT_USE_FLAT_CONFIG 환경변수항상 flat config
LegacyESLint APIESLint 클래스 사용

아직 .eslintrc를 쓰고 있다면, 더 이상 미룰 수 없다. ESLint 공식 마이그레이션 가이드의 config 변환 도구를 활용하면 된다.

마이그레이션-도구.sh
npx @eslint/migrate-config .eslintrc.json

[💡 잠깐! 이 용어는?] Flat Config: ESLint v9부터 도입된 새로운 설정 형식이다. eslint.config.js 파일에 배열로 설정을 정의하며, extends 체이닝 대신 스프레드 연산자로 합성한다. 설정의 흐름이 위에서 아래로 직관적이어서 "flat(평탄한)"이라는 이름이 붙었다.


설정 탐색 방식의 변화

v10에서 가장 실질적인 변화다. 이전에는 ESLint가 eslint.config.js를 **현재 작업 디렉토리(cwd)**에서 찾았다. v10부터는 린트 대상 파일의 디렉토리에서 위로 올라가며 찾는다.

프로젝트-구조.txt
monorepo/
├── eslint.config.js          ← 루트 설정
├── packages/
│   ├── web/
│   │   ├── eslint.config.js  ← web 전용 설정
│   │   └── src/
│   │       └── App.tsx       ← 이 파일은 web/eslint.config.js 적용
│   └── api/
│       └── src/
│           └── index.ts      ← 이 파일은 루트 eslint.config.js 적용

모노레포에서 각 패키지마다 다른 린트 규칙을 적용하고 싶었다면, 이전에는 설정 안에서 files 패턴으로 분기해야 했다. 이제는 각 패키지에 eslint.config.js를 두면 자동으로 해당 설정이 적용된다. 각 방이 자기 조명을 갖는 것과 같다.

주의할 점이 있다. v9에서 --flag v10_config_lookup_from_file 플래그로 이 동작을 미리 써본 경우, v10에서는 해당 플래그를 제거해야 한다. 이제 기본 동작이니까.


JSX 참조 추적

React 프로젝트에서 골치 아팠던 문제가 해결됐다. v9까지는 JSX에서 컴포넌트를 사용해도 ESLint의 스코프 분석이 이를 "참조"로 인식하지 못했다.

Card.tsx
import { Card } from "./card.jsx"
 
export function createCard(name: string) {
  return <Card name={name} />
}

v9에서는 Card가 사용되었는데도 no-unused-vars 규칙이 "Card is defined but never used"라고 경고했다. 그래서 eslint-plugin-reactjsx-uses-vars 같은 워크어라운드 플러그인이 필요했다.

v10부터는 ESLint 코어가 JSX 식별자를 참조로 추적한다. <Card>를 만나면 Card 변수가 사용된 것으로 인식한다. 반대로, 정의되지 않은 컴포넌트를 JSX에서 쓰면 no-undef가 정상적으로 잡아낸다.

eslint.config.js — 워크어라운드 제거
import react from "eslint-plugin-react"
 
export default [
  {
    plugins: { react },
    rules: {
      // v10에서는 더 이상 필요 없다
      // "react/jsx-uses-vars": "error",  ← 제거
    }
  }
]

eslint:recommended 업데이트

eslint:recommended에 3개의 규칙이 새로 추가됐다.

규칙설명
no-unassigned-vars선언만 하고 값을 할당하지 않은 변수 감지
no-useless-assignment사용되지 않는 값 할당 감지
preserve-caught-errorcatch 블록에서 에러 객체를 무시하는 패턴 감지

기존 프로젝트에서 v10으로 올리면 이 규칙들 때문에 새로운 에러가 나올 수 있다. 당장 수정이 어렵다면 config에서 "off"로 끄면 된다.

eslint.config.js
import js from "@eslint/js"
 
export default [
  js.configs.recommended,
  {
    rules: {
      "no-unassigned-vars": "off",
      "no-useless-assignment": "off",
      "preserve-caught-error": "off"
    }
  }
]

플러그인 개발자가 알아야 할 것

커스텀 룰을 만들어 쓰는 팀이라면, 제거된 API를 교체해야 한다.

제거된 API대체
context.getCwd()context.cwd
context.getFilename()context.filename
context.getSourceCode()context.sourceCode
context.parserOptionscontext.languageOptions.parserOptions
sourceCode.isSpaceBetweenTokens()sourceCode.isSpaceBetween()
sourceCode.getJSDocComment()대체 없음

자동 변환 도구가 제공된다.

플러그인-마이그레이션.sh
npx eslint-transforms v9-rule-migration ./rules/

[💡 잠깐! 이 용어는?] RuleTester: ESLint 규칙의 동작을 검증하는 테스트 유틸리티다. 유효한 코드(valid)와 규칙을 위반하는 코드(invalid)를 각각 정의하고, 규칙이 올바르게 보고하는지 확인한다. v10에서는 테스트 케이스 작성 요건이 더 엄격해졌다.


Node.js 버전 요구사항

지원비지원
v20.19.0 이상v20.18.x 이하
v22.13.0 이상v21.x 전체
v24 이상v23.x 전체

홀수 버전(v21, v23)은 Node.js의 비LTS 릴리스라 ESLint가 지원을 끊었다. CI에서 Node 버전을 확인하고, 필요하면 올려야 한다.


마이그레이션 체크리스트

v10으로 올릴 때 순서대로 확인할 항목이다.

  • Node.js v20.19.0 이상으로 업그레이드
  • .eslintrc.*eslint.config.js로 전환 (아직 안 했다면)
  • .eslintignore 삭제, config의 ignores로 이동
  • 코드 내 /* eslint-env */ 주석 전부 제거
  • JSX 워크어라운드 플러그인 규칙 제거 (jsx-uses-vars 등)
  • eslint:recommended 신규 규칙 에러 처리
  • 커스텀 룰에서 deprecated API 교체
  • CI의 --flag v10_config_lookup_from_file 플래그 제거
  • jiti 사용 시 v2.2.0 이상으로 업데이트

마무리

ESLint v10은 2~3년에 걸친 flat config 전환의 마침표다. .eslintrc가 완전히 사라지고, 설정 탐색이 파일 기반으로 바뀌면서 모노레포 지원이 자연스러워졌다. JSX 참조 추적이 코어에 들어온 것은 React 생태계에 소소하지만 확실한 개선이다.

  • 당장 할 일: .eslintrceslint.config.js 전환, Node.js 버전 확인
  • React 프로젝트: JSX 워크어라운드 플러그인 정리
  • 모노레포: 패키지별 eslint.config.js 배치 검토
  • 플러그인 개발: deprecated API 교체

한 마디로, 미뤄왔던 설정 정리를 할 시간이다.


참고:

관심 있을 만한 포스트

Native JSON Modules — 번들러 없이 JSON을 import하는 시대

Import Attributes와 함께 표준이 된 native JSON module. 어떻게 동작하고, 기존 번들러 방식과 뭐가 다른지 정리했다.

JavaScriptESM

Babel 7.29.0 — 10년 역사의 마지막 마이너, 그리고 8 RC1

2026년 1월 31일, Babel 7의 마지막 마이너 릴리스가 공개됐다. 이 버전이 갖는 역사적 의미와 Babel 8 RC1의 핵심 변화를 정리한다.

BabelJavaScript

Error.isError() — realm을 넘나드는 안전한 에러 검사 API

instanceof Error가 iframe과 worker에서 실패하는 이유, 그리고 이를 근본적으로 해결하는 Error.isError()의 동작 원리를 정리한다.

JavaScriptError.isError

V8의 Sea of Nodes 탈출기 — 왜 우아한 이론이 실전에서 무너졌는가

V8 팀이 10년간 사용한 Sea of Nodes IR을 포기하고 Turboshaft로 전환한 7가지 이유와 그 교훈을 정리한다.

V8컴파일러

jQuery 4.0 — 10년 만의 메이저 릴리스, 무엇이 바뀌었나

jQuery가 20주년을 맞아 10년 만에 메이저 버전을 출시했다. IE 지원 축소, ES 모듈 전환, Trusted Types 등 핵심 변경 사항을 정리한다.

jQueryJavaScript

V8 Mutable Heap Numbers — 숫자 하나 바꿀 때마다 새 객체를 만들던 비효율을 잡다

V8 엔진이 스크립트 컨텍스트의 숫자 변수를 매번 새 HeapNumber로 할당하던 방식을 제자리 수정(mutable)으로 바꿔 최대 2.5배 성능 향상을 달성했다.

V8JavaScript

V8 Explicit Compile Hints — 주석 한 줄로 JavaScript 시작 속도를 630ms 줄이는 법

Chrome 136에 도입된 V8의 Explicit Compile Hints 기능으로 JavaScript 초기 로딩 성능을 개선하는 원리와 사용법을 분석한다.

V8성능 최적화

Babel 8 Beta — CJS를 버리고 ESM 전용으로 간다

2년간의 알파를 거쳐 베타에 진입한 Babel 8의 핵심 변경사항과 마이그레이션 전략을 정리한다.

BabelESM