[Next.js ESLint] TypeError: Converting circular structure to JSON --> starting at object with constructor 'Object' | property 'configs' -> object with constructor 'Object' | property 'flat' -> object with constructor 'Object'... 에러

2025. 12. 14. 21:19

새로운 Next.js 프로젝트를 설치하고 린트 적용 중에 발생한 에러:

Oops! Something went wrong! :(

ESLint: 9.39.1

TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    |     property 'configs' -> object with constructor 'Object'
    |     property 'flat' -> object with constructor 'Object'
    |     ...
    |     property 'plugins' -> object with constructor 'Object'
    --- property 'react' closes the circle

 

기존 나의 eslint.config.mjs:

import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import unusedImports from "eslint-plugin-unused-imports";
import tseslint from "typescript-eslint";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
  baseDirectory: __dirname,
});

const eslintConfig = [
  // 1) Next 기본 권장 규칙
  ...compat.extends("next/core-web-vitals", "next/typescript"),

  // 2) 전역 ignore
  {
    ignores: [
      "node_modules/**", 
      ...

Next.js cli로 설치하면 자동으로 생성되는 eslint.config.mjs에다가 내가 필요한 린트 설정을 추가한 상황이었다.

 

이와 관련된 이슈 토론: https://github.com/eslint/eslint/issues/20237

 

Bug: Converting circular structure to JSON when showing config error · Issue #20237 · eslint/eslint

Environment Node version: v24.7.0 npm version: 11.6.1 Local ESLint version: 9.36.0 Global ESLint version: none Operating System: Fedora 42 GNU/Linux What parser are you using? Default (Espree) What...

github.com

 

Next.js 공식문서의 ESLint 설명: https://nextjs.org/docs/app/api-reference/config/eslint

 

Configuration: ESLint | Next.js

Learn how to use and configure the ESLint plugin to catch common issues and problems in a Next.js application.

nextjs.org

 

Next.js는 애플리케이션에서 흔히 발생하는 문제를 쉽게 포착할 수 있도록 eslint-config-next 라는 ESLint 설정 패키지를 제공합니다. 이 패키지에는 @next/eslint-plugin-next 플러그인과 eslint-plugin-react, eslint-plugin-react-hooks 에서 권장하는 규칙 세트가 포함되어 있습니다.

또한, 이 패키지는 주로 세 가지 구성을 제공한다고 한다:

구성 설명
eslint-config-next Next.js, React, React Hooks 규칙이 포함된 기본 구성입니다. JavaScript 및 TypeScript 파일을 모두 지원합니다.
eslint-config-next/core-web-vitals 기본 구성의 모든 것을 포함하며, Core Web Vitals에 영향을 미치는 규칙을 경고(warnings)에서 오류(errors)로 상향 조정합니다. 대부분의 프로젝트에 권장됩니다.
eslint-config-next/typescript TypeScript 프로젝트를 위해 typescript-eslint에서 제공하는 TypeScript 관련 린팅 규칙을 추가합니다. 기본 또는 core-web-vitals 구성과 함께 사용하세요.

 

그래서 나는 eslint-config-next에서 포함하는 패키지들이 궁금했다.

npm info eslint-config-next dependencies

위 명령어를 통해 eslint-config-next가 포함하는 패키지를 확인할 수 있었다:

{
  globals: '16.4.0',
  'typescript-eslint': '^8.46.0',
  'eslint-plugin-react': '^7.37.0',
  'eslint-plugin-import': '^2.32.0',
  'eslint-plugin-jsx-a11y': '^6.10.0',
  '@next/eslint-plugin-next': '16.0.10',
  'eslint-plugin-react-hooks': '^7.0.0',
  'eslint-import-resolver-node': '^0.3.6',
  'eslint-import-resolver-typescript': '^3.5.2'
}

그에 따라, 세팅 초반에 내가 eslint 설정하면서 설치했던 eslint-plugin-import나, eslint-import-resolver-typescript 같은 패키지가 불필요해져서 삭제하였다.

 

다시 Next 공식 문서 내용으로 돌아와서,
eslint.config.mjs 세팅을 다음과 같이 하라는 내용이 있었다. 이를 참고하여 나의 에러를 해결할 수 있었다.

import { defineConfig, globalIgnores } from 'eslint/config'
import nextVitals from 'eslint-config-next/core-web-vitals'
 
const eslintConfig = defineConfig([
  ...nextVitals,
  // Override default ignores of eslint-config-next.
  globalIgnores([
    // Default ignores of eslint-config-next:
    '.next/**',
    'out/**',
    'build/**',
    'next-env.d.ts',
  ]),
])
 
export default eslintConfig

복잡하게 FlatCompat을 사용하지 않고, eslint-config-next에서 nextVitals를 import 하여 Flat Config 형식으로 적용한 것을 볼 수 있다. 또한, defineConfig와 globalIgnores를 사용하고 있다.

그에 따라 나의 eslint.config.mjs도 다음과 같이 수정하여 문제를 해결하였다:

import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
import tseslint from "typescript-eslint";
import unusedImports from "eslint-plugin-unused-imports";

const eslintConfig = defineConfig([
  // 1) Next 기본 권장 규칙
  ...nextVitals,
  ...nextTs,

  // 2) 전역 ignore
  globalIgnores([
    "node_modules/**",

 

정리하면,

github 이슈 토론에서도 언급 됐듯이 Next.js의 공식 린팅 설정 패키지인 eslint-config-next의 최신 버전이 Flat Config를 지원하도록 업데이트되었기 때문에, 레거시 설정을 변환하는 FlatCompat을 사용할 필요가 없어졌다. eslint-config-next에서 nextVitals와 nextTs를 직접 import하여 순환 참조를 유발했던 레거시 설정 변환 과정 자체가 생략되었고, 결과적으로 충돌이 해결되었다.

 

문제가 발생했던 이유를 Gemini의 도움으로 정리하면:

  • 원인 1: 순환 참조 객체 생성 (FlatCompat의 부작용) FlatCompat을 통해 Next.js 레거시 설정(extends("next/..."))을 Flat Config로 변환하는 과정에서, 특히 React와 같은 내부 플러그인 객체들이 순환 참조(Circular Reference)를 포함하는 복잡한 구성 객체를 생성했습니다.
  • 원인 2: ESLint의 오류 처리 버그 (충돌의 직접적인 이유) 구성 파일에 유효성 검사 오류가 감지되었을 때, ESLint는 오류 보고를 위해 이 순환 참조 객체를 JSON.stringify()를 이용해 문자열화하려 했고, 이 과정에서 TypeError: Converting circular structure to JSON 오류가 발생하며 ESLint 프로세스 자체가 충돌했습니다.

(참고로 FlatCompat은 .eslintrc나 .eslintrc.js 등과 같은 레거시 설정을 ESLint 9의 Flat Config 형식으로 변환하는 래퍼 역할을 한다.)