"[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 허용 목록 덕분에 이는 약속이 아니라 브라우저가 강제하는 보장입니다. 직접 확인하기
실제 원인
어느 쪽이 답인 경우가 많은지, 그 순서대로 정리했습니다.
-
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; -
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')); -
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. 통제할 수 없는 경계에서는 합리적인 방어입니다. 다만 내 코드 안에서는 둘 중 어느 쪽이 오는지 알고 있는 편이 낫습니다.