본문으로 건너뛰기
jsonbeautifiers
한국어
V8 Node v24.15.0, V8 13.6.233.17에서 파서를 실행해 확인했습니다.

"[object Object]" is not valid JSON

JSON.parse는 문자열을 기대합니다. 객체를 건네면 JavaScript가 먼저 그 객체를 문자열로 바꾸는데, 기본 변환은 "[object Object]"라는 글자 그대로의 텍스트를 만듭니다. 그래서 파서가 1번 위치의 o에서 실패합니다. 데이터는 멀쩡합니다. 있어서는 안 되는 건 parse 호출 쪽입니다.

이렇게 적힌 것을 보게 될 수도 있습니다

  • Unexpected token o in JSON at position 1

V8은 2022년에 JSON 오류 메시지 대부분을 다시 썼습니다. 검색 결과에 나오는 건 여전히 옛 문구가 대부분이지만, 지금의 어떤 런타임도 그 문구를 내보내지 않습니다.

JSON을 붙여넣고 어디서 깨지는지 정확히 확인하기

입력

붙여 넣은 것은 여러분의 브라우저를 떠나지 않습니다. connect-src 허용 목록 덕분에 이는 약속이 아니라 브라우저가 강제하는 보장입니다. 직접 확인하기

실제 원인

어느 쪽이 답인 경우가 많은지, 그 순서대로 정리했습니다.

  1. 01 그 값은 이미 파싱되어 있었음

    axios, jQuery.ajax를 비롯한 많은 API 클라이언트는 Content-Type이 그렇게 말하면 JSON을 대신 파싱해 줍니다. 그 결과를 다시 파싱하는 것이 이 오류를 부릅니다.

    깨지는 예

    const res = await axios.get('/api/user');
    const user = JSON.parse(res.data);   // res.data is already an object

    되는 예

    const res = await axios.get('/api/user');
    const user = res.data;
  2. 02 stringify 없이 객체를 저장함

    localStorage와 sessionStorage는 문자열을 저장합니다. 객체를 넘기면 String(obj)이 불려서 "[object Object]"라는 글자 그대로의 텍스트가 기록됩니다. 그 시점에 데이터는 사라진 것이지, 단순히 읽을 수 없게 된 게 아닙니다.

    깨지는 예

    localStorage.setItem('user', user);
    const back = JSON.parse(localStorage.getItem('user'));

    되는 예

    localStorage.setItem('user', JSON.stringify(user));
    const back = JSON.parse(localStorage.getItem('user'));
  3. 03 템플릿 문자열 안에 객체를 넣음

    `{"user": ${user}}` 식으로 요청 본문을 만들면 객체가 아니라 "[object Object]"가 들어갑니다. 객체를 만든 뒤 전체를 stringify하세요.

    깨지는 예

    body: `{"user": ${user}}`

    되는 예

    body: JSON.stringify({ user })

자주 묻는 질문

"Unexpected token o in JSON at position 1"은 어떻게 됐나요?
같은 실수에 대한 2022년 이전 V8 문구였습니다. 그 o는 "[object Object]" 문자열의 두 번째 글자였습니다. 지금 메시지는 대신 문제가 된 텍스트를 인용하고, 그편이 훨씬 쓸모 있습니다.
방어적으로 쓰려면?
typeof value === "string" ? JSON.parse(value) : value. 통제할 수 없는 경계에서는 합리적인 방어입니다. 다만 내 코드 안에서는 둘 중 어느 쪽이 오는지 알고 있는 편이 낫습니다.

지금 고치기

위의 도구에 페이로드를 붙여넣거나, 이 일을 위해 만들어진 도구로 바로 가세요.

페이로드 살펴보기