← 전체 글

"[object Object] is Not Valid JSON" 등 JSON 문법 오류 고치기

"[object Object] is not valid JSON"이나 "expected a JSON object, array or literal"이 나오나? 그 원인, JSON 문법 수정, 후행 쉼표 수정, 깨진 JSON 자동 복구를 배운다.

코드를 실행하고, 네트워크 탭을 확인하면 이것이 보입니다: "[object Object]" is not valid JSON. 아니면 JSON 검증기가 Expected a JSON object, array or literal이라고 하거나, 파서가 마지막 요소 다음의 쉼표에서 막힙니다. 이 오류들은 개발자가 마주치는 가장 흔하면서도 가장 혼란스러운 JSON 실수 중 하나입니다. 이 가이드는 각 오류가 정확히 무엇을 의미하는지, 왜 일어나는지, 파서가 받아들이도록 JSON 문법을 어떻게 고치는지를 설명합니다.

「[object Object] is not valid JSON」은 무엇을 뜻하는가?

JavaScript가 JSON.stringify() 없이 객체를 문자열로 변환할 때, 결과는 텍스트 [object Object]입니다 — 이는 모든 객체의 .toString() 메서드의 기본 출력입니다. 그 문자열이 이어서 JSON.parse()로 전달되거나 요청 본문으로 전송되면 이 오류를 보게 됩니다:

SyntaxError: "[object Object]" is not valid JSON

파서는 리터럴 텍스트 [object Object]을 파싱하려 합니다. [을 보고 JSON 배열을 기대하다가 인용 없는 단어 object를 만나 실패합니다. 대괄호는 실제 존재 — 그것은 문자열 [object Object]의 첫 글자 — 이지만, 뒤에 오는 내용이 유효한 JSON 배열 문법이 아닙니다.

왜 JSON에 [object Object]가 나타나나?

근본 원인은 항상 같습니다: 파싱이나 전송 전에 코드 어딘가에서 JavaScript 객체가 문자열로 강제 변환된 것입니다. 가장 흔한 네 가지 패턴:

문자열 결합

객체와 문자열을 결합하면 JavaScript가 객체의 .toString()을 호출하고, 그것은 [object Object]를 반환합니다:

const user = { name: "Alice", age: 30 };

// ❌ 문자열 결합이 객체를 강제 변환
const body = "data=" + user;
console.log(body); // "data=[object Object]"

// ✅ JSON.stringify 사용
const body = "data=" + JSON.stringify(user);

JSON.stringify 없이 fetch / XMLHttpRequest 본문

일반 객체를 fetch 호출의 body로 넘기면 자동으로 문자열로 변환됩니다:

const payload = { action: "login", user: "alice" };

// ❌ fetch가 객체를 "[object Object]"로 강제 변환
fetch('/api/login', {
  method: 'POST',
  body: payload,
});

// ✅ 먼저 직렬화하고 Content-Type 설정
fetch('/api/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(payload),
});

템플릿 리터럴

객체를 템플릿 리터럴에 직접 삽입하면 .toString()이 호출됩니다:

const config = { theme: "dark", lang: "en" };

// ❌ 템플릿 리터럴에서의 강제 변환
const json = `{"config": ${config}}`;
// '{"config": [object Object]}'  ← 잘못된 JSON

// ✅ 객체만 stringify
const json = `{"config": ${JSON.stringify(config)}}`;
// '{"config": {"theme":"dark","lang":"en"}}'

중첩된 stringify

값이 이미 사전 직렬화된 JSON 문자열인 객체에 JSON.stringify()를 호출하면 이중 인코딩된 출력이 나옵니다 — JSON 안에 등장하는 것은 그것이 나타내는 객체가 아니라 문자열 리터럴입니다:

const inner = JSON.stringify({ id: 1 }); // '{"id":1}'
const outer = JSON.stringify({ data: inner });
// '{"data":"{\"id\":1}"}' — data 필드는 문자열, 객체가 아님

// 나중에 외부 문자열을 JSON.parse() 한 뒤 data를 다시 JSON으로 쓰려면
// result.data를 한 번 더 JSON.parse() 해야 합니다.

[object Object] JSON 오류 고치는 법

해결책은 언제나 같습니다: 객체를 문자열로 사용하거나 네트워크로 전송하기 전에 JSON.stringify()로 직렬화하는 것입니다. 빠른 진단: 문제의 변수를 문자열로 사용하는 모든 자리를 코드에서 검색해 그 자리에 JSON.stringify()를 추가하세요.

// 체계적인 수정 패턴
function toJsonBody(obj) {
  if (typeof obj === 'string') {
    // 이미 문자열 — 사용 전 유효한 JSON인지 검증
    JSON.parse(obj); // 유효하지 않으면 throw
    return obj;
  }
  return JSON.stringify(obj);
}

「Expected a JSON Object, Array or Literal」의 의미

이 오류는 Firefox의 내장 JSON 뷰어(그리고 일부 JSON 검증기)에서 나오며, JSON의 최상위 값이 여섯 가지 유효한 JSON 타입 중 어느 것도 아니라는 뜻입니다. 흔한 원인:

  • 입력이 빈 문자열.JSON.parse("")는 빈 문자열이 유효한 JSON 값이 아니라서 이 오류를 던집니다.
  • 입력이 undefined. JSON.stringify(undefined)undefined를 반환하고(문자열 "undefined"이 아님), JSON.parse(undefined)는 throw합니다.
  • 응답이 JSON이 아니라 HTML. 서버 오류 페이지는 흔히 HTML을 반환합니다. 첫 글자 <는 유효한 JSON이 아닙니다. JSON 자체가 깨졌다고 단정하기 전에 Network 탭의 Content-Type 헤더와 원시 응답 본문을 확인하세요.
  • 파일 시작 부분의 BOM. JSON 파일 앞에 붙은 UTF-8 BOM(바이트 0xEF 0xBB 0xBF)은 엄격한 파서에서 이 오류를 일으킵니다. RFC 8259는 JSON에서 BOM을 명시적으로 금지합니다.
// 파싱 전에 빈 값 / undefined 방어
function safeParse(text) {
  if (!text || typeof text !== 'string') {
    throw new TypeError(`Cannot parse ${typeof text} as JSON`);
  }
  return JSON.parse(text.trimStart()); // trimStart로 우연한 BOM/공백 제거
}

JSON 마지막 요소 뒤의 쉼표: 후행 쉼표 오류

또 하나 매우 흔한 JSON 문법 오류는 후행 쉼표 — 객체나 배열의 마지막 항목 뒤에 오는 쉼표입니다:

// ❌ 객체의 후행 쉼표
{
  "name": "Alice",
  "age": 30,
}

// ❌ 배열의 후행 쉼표
["red", "green", "blue",]

JSON 문법은 쉼표가 두 항목을 분리하는 데 쓰이며 — 양쪽에 값이 있어야 한다고 규정합니다. 뒤에 아무것도 없는 쉼표는 문법 위반입니다. JavaScript 자체는 (ES5 이후) 후행 쉼표를 허용하기 때문에 개발자들이 무심코 적는 일이 흔합니다.

프로그램적으로 후행 쉼표를 찾아 고치려면:

// ⚠️ 정규식 수정 — 단순한 경우엔 동작하지만 문자열을 망가뜨릴 수 있음
const fixed = raw.replace(/,\s*([}\]])/g, '$1');

// ✅ 더 나음: 문법을 이해하는 복구 파서를 사용
// 예: npm의 json-repair 라이브러리

이 특정 오류에 대한 자세한 내용은 JSON의 후행 쉼표: 왜 throw하고 어떻게 고치나을 참고하세요.

JSON 문법 고치기: 완전한 체크리스트

JSON 수정 도구로 손을 뻗기 전에, 이 체크리스트로 오류 유형을 식별해 보세요:

구조

  • 모든 {에는 짝이 되는 }가, 모든 [에는 짝이 되는 ]가 있어야 합니다. 닫히지 않은 괄호는 가장 흔한 절단형 오류 중 하나입니다.
  • 후행 쉼표 없음 — 객체의 마지막 키-값 쌍이나 배열의 마지막 항목 뒤에 쉼표를 두지 마세요.
  • 빠진 쉼표 없음 — 객체나 배열의 항목(마지막 제외) 뒤에는 반드시 쉼표가 있어야 합니다.

문자열

  • 모든 문자열은 큰따옴표 사용. 작은따옴표 문자열('Alice')은 JavaScript 문법이지 JSON이 아닙니다.
  • 모든 객체 키는 큰따옴표 문자열. 맨몸 키({ name: "Alice" })는 JavaScript 객체 리터럴 문법이지 JSON이 아닙니다.
  • 문자열 내부의 제어 문자는 이스케이프. JSON 문자열 내부의 원시 개행, 탭, 널 바이트는 불법입니다 — 대신 \n, \t, \u0000 같은 이스케이프를 사용하세요.

  • 불리언은 소문자:true / false, 이며 True / False / TRUE가 아닙니다.
  • Null은 소문자:null, None, nil, NULL이 아닙니다.
  • undefined 금지. JSON에는 undefined 타입이 없습니다. null을 쓰거나 키를 완전히 생략하세요.
  • NaN이나 Infinity 금지. 이 JavaScript 값들은 JSON 표현이 없습니다. JSON.stringify({x: Infinity})는 조용히 {"x":null}을 만듭니다.

여분 내용

  • 주석 금지.// 줄 주석 /* 블록 주석 */은 JSON 명세에 포함되지 않습니다.
  • 뒤따르는 내용 금지. 유효한 JSON 텍스트는 정확히 한 개의 값입니다 — 앞에는 (선택적인 공백 외에는) 아무것도 없고 뒤에도 어떤 것도 올 수 없습니다.

JSON 수정기 사용: 언제 자동화하나

일회성 디버깅이라면 손으로 JSON을 고치는 게 빠릅니다. 하지만 외부 소스(API, AI 모델 출력, 사용자 입력)에서 JSON을 받는 운영 파이프라인에는 JSON 수정기가 필요합니다: 유효한 콘텐츠를 망가뜨리지 않으면서 흔한 문법 오류를 프로그램적으로 복구하는 코드나 도구입니다.

좋은 JSON 수정기는 다음을 처리합니다:

  • 객체와 배열의 후행 쉼표
  • 작은따옴표 문자열 → 큰따옴표 문자열
  • 따옴표 없는 키 → 큰따옴표 키
  • Python 리터럴(True, False, None) → JSON 등가 값
  • JavaScript의 // 줄/* 블록 */ 주석 → 제거
  • Markdown 코드 펜스(```json ... ```) → 제거
  • 닫히지 않은 괄호 → 자동 닫기

핵심 요구사항은 수정이 문법을 인지해야 한다는 것입니다 — 도구가 어떤 토큰이 구조 구분자이고 어떤 토큰이 문자열 값 안에 있는지를 파악하므로, 우연히 ,}이나 True을 포함한 문자열 내용을 절대 망가뜨리지 않습니다.

JavaScript에서 JSON 복구를 구현하는 단계별 안내는 JavaScript에서 깨진 JSON을 다루는 방법을 보세요.

JSON.stringify 함정 피하기 (순환 참조 포함)

항상 JSON.stringify로 직렬화하기 시작해도 여전히 사람들을 걸려 넘어뜨리는 두 가지 상황이 있습니다:

  • 순환 참조는 throw합니다. const a = {}; a.self = a; 같은 값은 TypeError: Converting circular structure to JSON을 줍니다. 좋은 대안 셋: 본 객체를 추적하는 커스텀 replacer로 역참조를 떨궈내거나, 인메모리 깊은 복사용으로 structuredClone(value)로 바꾸거나, 진정으로 순환 그래프를 직렬화하고 나중에 재구성해야 한다면 flatted 같은 라이브러리를 사용하세요.
  • 손실되는 타입.JSON.stringify는 조용히 undefined, 함수, Symbol 값을 버리고, NaN / Infinitynull로 바꿉니다. 이들을 표현 가능한 것으로 stringify 전에 변환하세요. 파싱 후가 아니라.

빠른 참조: 흔한 JSON 오류와 수정

// 1. [object Object] — 객체가 문자열로 강제 변환
❌ fetch(url, { body: myObject })
✅ fetch(url, { body: JSON.stringify(myObject) })

// 2. 후행 쉼표
❌ { "a": 1, "b": 2, }
✅ { "a": 1, "b": 2 }

// 3. 작은따옴표
❌ { 'name': 'Alice' }
✅ { "name": "Alice" }

// 4. 따옴표 없는 키
❌ { name: "Alice" }
✅ { "name": "Alice" }

// 5. Python 불리언
❌ { "active": True }
✅ { "active": true }

// 6. JSON 안의 주석
❌ { "debug": true // log everything }
✅ { "debug": true }

// 7. 빈 문자열
❌ JSON.parse("")
✅ JSON.parse(text || "null")

// 8. HTML 오류 페이지를 "JSON"으로
❌ JSON.parse("<!DOCTYPE html>...")
✅ response.json() 호출 전 response.ok와 Content-Type 확인

자주 묻는 질문

「[object Object] is not valid JSON」이 무슨 뜻인가요?

JavaScript 객체가 기본 .toString()으로 문자열화되어 리터럴 텍스트 [object Object]을 만들고, 그 텍스트가 이후 JSON으로 파싱되거나 전송되었습니다. 해결책은 값을 문자열로 사용하기 전에 JSON.stringify()로 직렬화하는 것입니다.

fetch 요청에서 [object Object] 오류를 어떻게 고치나요?

body: JSON.stringify(payload)로 설정하고 Content-Type: application/json 헤더를 추가하세요. 원시 객체를 body로 넘기면 [object Object]로 강제 변환됩니다.

「Expected a JSON object, array or literal」은 무엇인가요?

Firefox와 일부 검증기가 최상위 값이 여섯 가지 JSON 타입 중 하나가 아닐 때 출력합니다 — 보통 입력이 비었거나, undefined이거나, JSON 대신 HTML이거나, 바이트 순서 표지로 시작하기 때문입니다. 먼저 response.okContent-Type 헤더를 확인하세요.

가장 흔한 JSON 문법 오류는 무엇인가요?

후행 쉼표, 작은따옴표, 따옴표 없는 키, Python 리터럴(True/None), 주석, 그리고 NaN/Infinity. 각각 전용 가이드가 있습니다: 후행 쉼표, unexpected-token 오류, unexpected end of input, 그리고 잘못된 제어 문자.

JSON 문법을 온라인으로 수정 — 설정 필요 없음

지금 당장 고쳐야 할 깨진 JSON 문자열이 있다면 JSON Fix가 위의 모든 오류를 자동으로 복구합니다. 아무리 깨져 있어도 JSON을 붙여넣고 Repair & Format을 클릭하면 문법적으로 올바르게 정리된 JSON이 돌아옵니다. 도구는 전부 브라우저에서 동작합니다: 어떤 데이터도 서버로 전송되지 않으므로 민감한 페이로드에도 안전합니다.