ESLint flat config 규칙 적용 안됨 해결: eslint.config.js 기준

··설명 약 10분·작성: 해비·블로그 소개

ESLint flat config 규칙이 파일에 적용되지 않으면 무엇을 점검하나요?

ESLint 규칙이 적용되지 않으면 어떤 설정이 어떤 파일에 매칭되는지부터 확인하세요. flat config 파일의 위치, files·ignores 범위와 명령의 실행 폴더를 대조합니다.

적용 조건·범위: TypeScript 프로젝트는 TypeScript용 플러그인과 설정을 별도로 구성해야 합니다. 개발환경 과정에서는 JavaScript/Vite 기준의 적용 원리를 먼저 확인합니다.

본문 근거: eslint.org/docs/latest/use/configure/configuration-files · eslint.org/docs/latest/use/configure/migration-guide

독립 실습: CLI 규칙 적용 확인

새 빈 폴더에 세 파일을 저장합니다. 이 예제는 Vite 서버 없이 ESLint CLI만 실행합니다. src/check.js의 unused는 실패를 재현하기 위해 남긴 변수입니다.

package.json 전체 코드

{
  "name": "eslint-independent",
  "private": true,
  "type": "module",
  "scripts": {
    "lint": "eslint ."
  },
  "devDependencies": {
    "eslint": "10.11.0",
    "@eslint/js": "10.0.1",
    "globals": "17.12.0"
  }
}

eslint.config.js 전체 코드

import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import globals from 'globals';

export default defineConfig([
  { ignores: ['dist/**'] },
  js.configs.recommended,
  {
    files: ['src/**/*.js'],
    languageOptions: { globals: globals.browser },
    rules: { 'no-console': 'warn' },
  },
]);

src/check.js 전체 코드

const message = 'Lint practice';
console.log(message);
const unused = true;
pnpm install
pnpm lint

기대 결과: no-console 경고와 no-unused-vars 오류로 lint가 실패해야 합니다. src/check.js 마지막 unused 선언을 지운 사본에서 다시 실행하면 warning만 남고 기본 종료 코드는 0입니다. console.log까지 제거하면 남은 message도 미사용이 되므로 두 줄을 함께 바꾸거나 그대로 둡니다.

변형 과제: files를 src/**/*.mjs로 바꿔 .js 파일에 규칙이 적용되지 않는 상태를 관찰한 뒤 되돌립니다. 경고만 있는 경우와 error가 있는 경우의 종료 코드를 구분합니다.

flat config란

현재 ESLint의 기본 설정 체계는 flat config입니다.

대표 설정 파일 이름은 다음과 같습니다.

eslint.config.js
eslint.config.mjs
eslint.config.cjs

프로젝트 루트에서 ESLint가 설정 파일을 찾을 수 있어야 합니다.

가장 작은 설정부터 시작하기

예제:

import js from '@eslint/js';
import { defineConfig } from 'eslint/config';

export default defineConfig([
  js.configs.recommended,
  {
    files: ['src/**/*.js'],
    rules: {
      'no-console': 'warn',
    },
  },
]);

이 설정은 기본 recommended 규칙을 사용하고 src 아래 JavaScript 파일에 no-console 경고를 추가합니다.

package.json script

{
  "scripts": {
    "lint": "eslint ."
  }
}

실행합니다.

pnpm lint

에디터에 표시되는 밑줄보다 CLI 결과를 기준으로 검증합니다.

규칙이 적용되지 않는 가장 흔한 이유

files 패턴이 대상 파일과 맞지 않습니다

{
  files: ['src/**/*.js'],
  rules: {
    'no-console': 'warn',
  },
}

대상 파일이 .jsx나 .ts라면 이 설정이 적용되지 않을 수 있습니다.

실제 프로젝트 확장자에 맞게 범위를 작성합니다.

{
  files: ['src/**/*.{js,jsx}'],
}

JSX를 실제로 파싱하려면 files 패턴만 바꾸는 것으로는 충분하지 않습니다. languageOptions.parserOptions.ecmaFeatures.jsx를 true로 설정하고, React를 사용하는 경우 해당 프레임워크의 lint 설정도 별도로 구성합니다. 아래 TaskFlow 최종 예제는 JSX를 사용하지 않는 JavaScript/Vite 앱입니다.

TypeScript 프로젝트는 TypeScript용 플러그인과 설정을 별도로 구성해야 합니다. 개발환경 과정에서는 JavaScript/Vite 기준의 적용 원리를 먼저 확인합니다.

ignores로 제외됐습니다

{
  ignores: ['dist/**'],
}

검사하려는 파일이 ignore 패턴에 포함됐는지 확인합니다.

뒤쪽 설정이 앞쪽 설정을 덮습니다

flat config는 설정 객체 배열을 사용합니다.

같은 파일에 여러 설정이 적용되면 뒤쪽 값이 최종 결과에 영향을 줄 수 있습니다.

export default [
  {
    rules: {
      'no-console': 'warn',
    },
  },
  {
    files: ['src/debug.js'],
    rules: {
      'no-console': 'off',
    },
  },
];

src/debug.js에서는 no-console이 꺼집니다.

config 파일 위치 확인

다른 폴더에서 ESLint를 실행하면 예상한 config를 찾지 못할 수 있습니다.

현재 위치를 확인합니다.

Windows PowerShell:

Get-Location

그리고 package.json과 eslint.config.js가 어느 루트에 있는지 비교합니다.

workspace 프로젝트에서는 루트와 앱 폴더의 역할을 명확히 해야 합니다.

CLI와 VS Code가 다르게 보일 때

VS Code ESLint extension 상태와 CLI는 별개로 확인합니다.

pnpm lint

CLI가 정상인데 에디터 표시가 이상하면 extension 설정과 workspace를 확인합니다.

반대로 에디터에서 경고가 없더라도 CLI가 실패한다면 CI나 배포 과정에서도 실패할 수 있습니다.

따라서 완료 기준은 CLI입니다.

문제 재현

src/debug.js:

const message = 'TaskFlow';
console.log(message);

const unused = true;

실행:

pnpm lint

예상 결과:

no-console 경고
no-unused-vars 오류 또는 경고

규칙 강도는 프로젝트 정책에 맞게 조정합니다.

오류 해결 순서

1. 실행 위치
2. eslint.config.* 존재 여부
3. 대상 파일 확장자
4. files 패턴
5. ignores
6. config 배열 순서
7. plugin 설치 상태
8. CLI 직접 실행
9. VS Code extension 상태

자주 발생하는 실수

.eslintrc 예제를 그대로 섞습니다

기존 eslintrc와 flat config는 구조가 다릅니다. 현재 프로젝트가 어떤 체계를 사용하는지 먼저 확인합니다.

env를 그대로 사용합니다

flat config에서는 languageOptions.globals 같은 구조를 사용합니다.

에디터에 밑줄이 없으니 정상이라고 판단합니다

완료 기준은 CLI 실행입니다.

pnpm lint

dist까지 검사합니다

빌드 산출물은 ignores로 제외합니다.

학습 완료 체크리스트

  • eslint.config.js의 역할을 설명할 수 있습니다.
  • flat config가 배열 구조라는 것을 이해했습니다.
  • files와 ignores를 구분할 수 있습니다.
  • 뒤쪽 설정이 결과에 영향을 줄 수 있음을 이해했습니다.
  • CLI와 VS Code extension 상태를 분리해 확인할 수 있습니다.
  • 규칙이 적용되지 않을 때 재설치보다 설정 범위를 먼저 확인합니다.

선택 실습: 같은 프로젝트로 연습하기

TaskFlow는 작업을 관리하는 예제 앱입니다. 이 선택 실습에서는 앱을 실행·개발하는 환경과 설정을 준비합니다.

이번 글에서는 TaskFlow에 eslint.config.js를 연결하고 flat config의 files, ignores, rules가 실제 CLI 검사에 어떻게 적용되는지 확인합니다.

선택: 연재 프로젝트와 전체 자료

연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.

TaskFlow 프로젝트 적용

시작 체크포인트: 이전 글의 적용·완료 확인과 이전 회차 시작 ZIP을 기준으로 아래 변경 파일을 수정합니다. 메인 앱은 루트에서 실행하고, labs는 해당 폴더의 package.json과 잠금 파일을 기준으로 따로 실행합니다.

이전 완료본과 달라진 파일: apps/web/eslint.config.js, apps/web/package.json, docs/development-environment.md, package.json, README.md. 전체 코드에서 같은 경로를 찾아 기존 내용을 대조합니다. ZIP의 자동 생성 lockfile도 해당 패키지 폴더와 함께 유지합니다.

JavaScript 파일·package.json과 CLI 종료 코드를 알고 시작합니다. 기존 TaskFlow 실행은 필요하지 않습니다. Node.js 24와 pnpm은 공통 준비 글 기준입니다.

TaskFlow의 설정 파일은 apps/web/eslint.config.js이며, src 경로는 apps/web 기준입니다. 루트의 pnpm lint는 @taskflow/web의 lint script를 실행합니다.

이번 글에서는 TaskFlow에 eslint.config.js를 연결하고 flat config의 files, ignores, rules가 실제 CLI 검사에 어떻게 적용되는지 확인합니다.

다음 명령으로 TaskFlow를 실행할 수 있어야 합니다.

pnpm install
pnpm dev

TaskFlow eslint.config.js

JavaScript Vite 예제에서는 다음처럼 구성할 수 있습니다.

import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import globals from 'globals';

export default defineConfig([
  { ignores: ['dist/**'] },
  js.configs.recommended,
  {
    files: ['src/**/*.js'],
    languageOptions: { globals: globals.browser },
    rules: { 'no-console': 'warn' },
  },
]);

globals 패키지가 필요한 구성이라면 개발 의존성으로 설치합니다.

pnpm --filter @taskflow/web add -D eslint @eslint/js globals

이 글을 읽고 바로 확인할 것

독자 질문: eslint.config.js가 있는데 규칙이 적용되지 않을 때 무엇을 먼저 확인하나요?

얻을 결과: 검사 대상 경로와 ignores·설정 순서를 확인하고 CLI로 해당 파일의 규칙 적용을 판단할 수 있습니다.

직접 따라 할 순서

  1. apps/web/eslint.config.js의 files·ignores와 검사 대상 경로를 대조합니다.
  2. 대상 파일에 의도적으로 규칙 위반을 만든 뒤 pnpm lint 결과를 확인합니다.
  3. 위반을 수정하고 pnpm lint를 다시 실행합니다.

확인 결과 기준: 위반을 만들었을 때 CLI가 실패하고 수정 후 통과해야 규칙 적용을 확인한 것입니다. 에디터 밑줄 유무만으로 판정하지 않습니다.

편집 검증 범위: 이번 보강에서 위 명령과 화면은 다시 실행하지 않았습니다(미확인). 본문 아래의 기존 실행 검증표는 이전 작업에서 기록한 결과이며, 독자는 자신의 환경에서 위 기준으로 확인해 주세요.

다음 학습: npm install 의존성 충돌 해결: ERESOLVE 오류가 날 때 해결법

이전 체크포인트

dev-env-step-04-vite-env

이번 변경

eslint.config.js 추가
lint script 확정
dist ignore
src JavaScript 검사 범위 확정
CLI 검증 기준 확정

변경 파일

package.json
eslint.config.js

package.json:

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint ."
  }
}

실행 확인

pnpm lint
pnpm dev

lint가 실패하면 원인을 수정한 뒤 다시 같은 명령으로 확인합니다.

완료 체크포인트

dev-env-step-05-eslint-flat
전체 파일·코드 보기

실습 프로젝트 전체 코드

아래는 이번 회차 완료 시점의 전체 소스입니다. 파일 경로는 압축을 푼 체크포인트 폴더를 기준으로 합니다. 자동 생성되는 lockfile은 ZIP에 포함하며, node_modules·빌드 결과·실제 비밀값은 포함하지 않습니다. 메인 앱과 각 lab은 별도의 설치 범위를 유지합니다.

.gitignore

node_modules/
dist/
storybook-static/
.env.local
.env.*.local

apps/web/.env.example

VITE_API_URL=https://api.example.invalid
VITE_APP_NAME=TaskFlow

apps/web/eslint.config.js

import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import globals from 'globals';

export default defineConfig([
  { ignores: ['dist/**'] },
  js.configs.recommended,
  {
    files: ['src/**/*.js'],
    languageOptions: { globals: globals.browser },
    rules: { 'no-console': 'warn' },
  },
]);

apps/web/index.html

<!doctype html>
<html lang="ko">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>TaskFlow 개발환경</title></head>
<body><main id="app"></main><script type="module" src="/src/main.js"></script></body>
</html>

apps/web/package.json

{
  "name": "@taskflow/web",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite --host 127.0.0.1",
    "build": "vite build",
    "lint": "eslint ."
  },
  "devDependencies": {
    "vite": "8.3.0",
    "eslint": "10.11.0",
    "@eslint/js": "10.0.1",
    "globals": "17.12.0"
  }
}

apps/web/src/config/env.js

const apiUrl = import.meta.env.VITE_API_URL;
const appName = import.meta.env.VITE_APP_NAME;

if (!apiUrl) {
  throw new Error('VITE_API_URL 환경 변수가 필요합니다.');
}

export const env = {
  apiUrl,
  appName: appName || 'TaskFlow',
};

apps/web/src/main.js

import './style.css';
import { env } from './config/env.js';

const app = document.querySelector('#app');
const title = document.createElement('h1');
title.textContent = env.appName;
const status = document.createElement('p');
status.textContent = '개발환경 스타터가 실행 중입니다.';
const api = document.createElement('p');
api.id = 'api-url';
api.textContent = `API URL: ${env.apiUrl}`;
app.append(title, status, api);

apps/web/src/style.css

body { margin: 0; color: #202020; background: #f6f6f6; font: 16px/1.6 system-ui, sans-serif; }
main { max-width: 720px; margin: 64px auto; padding: 24px; }
h1 { font-size: 30px; font-weight: 500; }
p { overflow-wrap: anywhere; }

docs/development-environment.md

# TaskFlow 개발환경

이 스타터는 개발환경 진단을 위한 최소 Vite/vanilla JavaScript 앱입니다. 할 일 관리 기능이나 API 서버는 포함하지 않습니다.

Node.js 24.19.0, pnpm 11.19.0으로 검증했습니다. 루트는 pnpm만 사용하며 apps/*만 workspace입니다. labs의 npm/Yarn/pnpm 실습은 독립 프로젝트입니다.

## 2회차

메인 앱은 pnpm을 유지합니다. labs/yarn-modern은 독립 Yarn 실습입니다.

## 3회차

apps/web/.env.example을 apps/web/.env.local로 복사합니다. .invalid 주소는 표시만 하며 실제 API 호출은 하지 않습니다. 공개 샘플값만 제공하고 로컬 파일은 ZIP에서 제외합니다.

## 4회차

Vite 루트는 apps/web입니다. env.js가 필수 VITE_API_URL을 검사하고 화면에 공개 URL을 표시합니다.

## 5회차

ESLint 설정과 의존성은 apps/web에 있습니다. 루트 pnpm lint가 앱의 eslint .을 호출합니다.

package.json

{
  "name": "taskflow",
  "private": true,
  "packageManager": "pnpm@11.19.0",
  "engines": {
    "node": ">=24"
  },
  "scripts": {
    "dev": "pnpm --filter @taskflow/web dev",
    "build": "pnpm --filter @taskflow/web build",
    "lint": "pnpm --filter @taskflow/web lint"
  }
}

pnpm-workspace.yaml

packages:
  - "apps/*"

README.md

# TaskFlow 개발환경 05 완료 체크포인트

Node.js 24.19.0 / pnpm 11.19.0 검증 기준입니다. pnpm을 별도로 설치한 환경에서 실행하세요. ZIP에는 node_modules, dist, 로컬 환경 변수, 실제 비밀값이 없습니다.

1. 이 폴더에서 `pnpm install --frozen-lockfile`
2. PowerShell: `Copy-Item apps/web/.env.example apps/web/.env.local` 또는 macOS/Linux: `cp apps/web/.env.example apps/web/.env.local`
3. `pnpm dev` 후 http://127.0.0.1:5173 확인
4. `pnpm build`
5. `pnpm lint`

apps/web가 Vite 루트입니다. 환경 변수도 이 폴더에 둡니다. 표시용 API 주소에 실제 요청을 보내지 않습니다. labs는 메인 workspace에서 제외되어 각 폴더에서 별도 설치합니다.

원문 단계별 전체 파일과 실행 검증 범위는 docs/development-environment.md를 확인하세요.

labs/yarn-modern/.yarnrc.yml

nodeLinker: node-modules

labs/yarn-modern/package.json

{
  "name": "taskflow-yarn-lab",
  "private": true,
  "type": "module",
  "packageManager": "yarn@4.18.0",
  "scripts": {
    "dev": "node src/main.js",
    "lint": "node --check src/main.js"
  }
}

labs/yarn-modern/src/main.js

console.log('TaskFlow Yarn 독립 실습');

실행 검증 결과

검증 환경: Windows · Node.js 24.19.0 · pnpm 11.19.0 · Vite 8.3.0 · ESLint 10.11.0.

항목 확인 결과
pnpm install 및 --frozen-lockfile 재설치 성공
pnpm build 성공
pnpm dev 개발 서버 응답 HTTP 200 확인
Vite 환경 변수 로드 변환된 모듈의 VITE_API_URL 값 확인
pnpm lint 성공

ESLint CLI를 기준으로 확인했습니다. VS Code ESLint 확장 화면은 별도로 검증하지 않았습니다.

다음 학습

다음 글부터는 정상 개발환경을 일부러 깨뜨립니다.

먼저 npm의 peer dependency 충돌을 별도 lab에서 재현하고 ERESOLVE 오류를 읽는 방법부터 학습합니다.

다음 글: npm install 의존성 충돌 해결: ERESOLVE 오류가 날 때 해결법

공식 문서

체크포인트 다운로드

dev-env-step-05-eslint-flat 완료 ZIP 다운로드

이전 회차 시작 체크포인트 ZIP

글 소개·학습 준비·전체 목차
ESLint 설정 규칙 점검을 표현한 문서와 곰 표지

개발환경 · 5 / 11 · 필수 학습 · ESLint flat config 오류 해결

ESLint 설정 파일을 만들었는데 규칙이 적용되지 않는다면 패키지를 다시 설치하기 전에 “어떤 설정 객체가 어떤 파일에 적용되는가”부터 확인해야 합니다.

이번 글에서 배우는 내용

  • ESLint flat config 구조
  • eslint.config.js 위치
  • config 배열의 적용 순서
  • files와 ignores
  • rules 적용 여부 확인
  • CLI 결과와 VS Code 표시 차이
  • 특정 파일에 규칙이 적용되지 않는 문제
  • TaskFlow lint script 구성

선수 지식

Node.js·pnpm 설치와 공통 실행 기준은 pnpm 환경 준비에서 확인합니다.

목차

  1. flat config란
  2. 가장 작은 설정부터 시작하기
  3. package.json script
  4. 규칙이 적용되지 않는 가장 흔한 이유
  5. config 파일 위치 확인
  6. CLI와 VS Code가 다르게 보일 때
  7. 문제 재현
  8. 오류 해결 순서
  9. 자주 발생하는 실수
  10. 학습 완료 체크리스트
  11. 독립 실습: CLI 규칙 적용 확인
  12. TaskFlow 프로젝트 적용
  13. TaskFlow eslint.config.js
  14. 이 글을 읽고 바로 확인할 것
  15. 실습 프로젝트 전체 코드
  16. 실행 검증 결과
  17. 다음 학습
  18. 공식 문서

이 글이 도움이 되었나요?

조회 중

읽음 기록

읽음 — / 전체 11개 · 필수 6개

이 글을 쓴 해비

웹사이트를 제작·운영하고, 개인 서비스와 자동화·AI 도구를 만듭니다. 직접 만들며 배운 내용과 문제 해결 과정을 BlogFlow에 기록합니다.

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기