← 전체 글

JSON 위치 1의 Unexpected Token o: 원인과 수정

그 소문자 "o"는 "[object Object]"의 두 번째 글자. JSON.parse()에 문자열이 아니라 JavaScript 객체를 넘긴 것. 이 실수의 모든 변형과 각각의 한 줄 수정.

SyntaxError: Unexpected token o in JSON at position 1 —— 그 소문자 o[object Object] 의 두 번째 문자입니다. 이는 JSON 문자열이 아니라 JavaScript 객체를 직접 JSON.parse() 에 전달했다는 의미입니다. 이 글은 이 오류의 모든 변형과 각 경우를 1분 내에 수정하는 방법을 설명합니다.

오류 메시지에 "token o" 라고 나오는 이유

문자열이 아닌 값을 JSON.parse() 에 전달하면, JavaScript는 먼저 그것을 문자열로 강제 변환합니다. 일반 객체는 "[object Object]" 로 변환됩니다. 파서는 위치 0의 [ (유효 —— 배열의 시작)를 본 다음, 위치 1의 o (유효하지 않음 —— 유효한 배열 요소가 아님)를 만납니다. 그래서: Unexpected token o at position 1.

JSON.parse({})
// 동일: JSON.parse("[object Object]")
// SyntaxError: Unexpected token o in JSON at position 1

가장 흔한 원인: response.json() 을 await 하지 않음

실제 코드베이스에서 이 오류의 1순위 원인입니다. fetch()Response 객체는 문자열이 아니라 ReadableStream 래퍼입니다. 이것을 JSON.parse() 에 전달하면 "[object Response]" 로 강제 변환됩니다.

// ❌ 잘못 —— response 는 Response 객체이지 문자열이 아님
const response = await fetch('/api/user');
const data = JSON.parse(response);
// SyntaxError: Unexpected token o in JSON at position 1

// ✓ 올바름 —— 브라우저가 스트림을 JSON 으로 파싱하게 함
const data = await response.json();

마찬가지로 response.text()await 없이 호출하면 Promise 객체가 반환됩니다(이는 "[object Promise]" 로 강제 변환됨).

// ❌ await 누락 —— text 는 Promise 이지 문자열이 아님
const text = response.text();
const data = JSON.parse(text);
// SyntaxError: Unexpected token o in JSON at position 1

// ✓ 올바름
const text = await response.text();
const data = JSON.parse(text);

이 오류를 유발하는 다른 객체들

전달한 것JS 가 강제 변환한 문자열오류 토큰
{} 또는 모든 일반 객체"[object Object]"위치 1의 token o
undefined"undefined"위치 0의 token u
true / false"true" / "false" —— 사실 유효한 JSON!오류 없음
Response 객체"[object Response]"위치 1의 token o
Promise"[object Promise]"위치 1의 token o
null"null" —— 유효한 JSON오류 없음 (null 반환)

원인: 직렬화하지 않고 객체를 전달

관련된 실수: 이미 메모리에 있는 JavaScript 객체를 "파싱"하려는 것. JSON.parse()문자열 을 받습니다. 이미 객체를 갖고 있다면 파싱할 필요가 전혀 없습니다.

const config = { host: 'localhost', port: 3000 };

// ❌ 무의미 —— config 는 이미 객체임
const parsed = JSON.parse(config);

// ✓ 깊은 복사를 원하면 structuredClone 또는 stringify + parse
const clone = JSON.parse(JSON.stringify(config));

// ✓ 전송하려면 JSON.stringify
const body = JSON.stringify(config);

원인: Python 등 백엔드가 잘못된 Content-Type 을 반환

때때로 서버는 "ok""not found" 같은 평문 에러 메시지를 200 상태로, 그리고 Content-Type 헤더 없이 반환합니다. 프런트엔드는 JSON 이라고 가정하고 response.json() 을 호출하면 본문이 유효한 JSON 이 아니어서 unexpected token 오류가 발생합니다.

// 서버가 리터럴 문자열을 보냄: ok
// 브라우저가 이를 JSON 으로 파싱 시도:
JSON.parse("ok")
// SyntaxError: Unexpected token o in JSON at position 0

수정: response.json() 을 호출하기 전에 항상 response.okresponse.headers.get('Content-Type') 을 확인하세요:

const response = await fetch('/api/action');
if (!response.ok) {
  const text = await response.text();   // 안전 —— JSON 이 아닐 수 있음
  throw new Error(`HTTP ${response.status}: ${text}`);
}
const data = await response.json();

덜 흔한 [object X] 변형

toString()[object 로 시작하는 문자열을 반환하는 모든 값은 위치 1에서 같은 오류를 일으킵니다. [object Object] 외에 실제 코드에서 마주치는 것들:

  • [object Module] —— 동적 import 결과를 전달: const mod = await import('./data.json', { with: { type: 'json' } }). 원하는 것은 mod.default 이지 mod 가 아닙니다.
  • [object AsyncFunction] —— 비동기 함수를 호출 하지 않고 참조를 전달한 경우: fn 이 아니라 fn() (프라미스의 해결값)을 문자열화하세요.
  • [object HTMLDocument] —— document 를 실수로 전달(예: 오래된 디버거 스니펫에서).
  • [object FormData] —— fetch(url, { body: formData }) 는 괜찮지만, JSON.parse(formData) 는 안 됩니다 —— 먼저 Object.fromEntries(formData) 로 변환하세요.

빠른 수정 체크리스트

  • fetch + JSON.parse? await response.json() 로 교체하세요.
  • 중간에 await 가 빠졌는가? 파싱 전의 모든 async 호출은 await 되어야 합니다.
  • 이미 객체를 가지고 있는가? 파싱할 필요 없습니다 —— 직접 사용하거나 structuredClone() 으로 복제하세요.
  • 서버가 오류 시 JSON 이 아닌 응답을 반환하는가? 응답이 JSON 이 아닐 수 있을 때는 response.text() 로 읽고, response.json() 을 쓰지 마세요.

자주 묻는 질문

"Unexpected token o in JSON at position 1" 은 무슨 뜻인가요?

JavaScript 객체를 JSON.parse() 에 전달했다는 의미입니다. JavaScript는 객체를 "[object Object]" 라는 문자열로 강제 변환하고, 파서는 위치 0의 [ 는 수용하지만 위치 1의 o 는 거부합니다.

fetch 로는 어떻게 수정하나요?

JSON.parse(response) 를 호출하지 마세요. 대신 await response.json() 를 사용하면 응답 스트림을 읽고 자동으로 파싱해 줍니다. 체인의 각 단계가 await 되었는지 확인하세요.

이미 객체가 있는데 JSON.parse 가 필요한가요?

아니요. JSON.parse() 는 JSON 문자열 만 값으로 변환합니다. 이미 객체가 있다면 그대로 사용하거나, JSON.parse(JSON.stringify(obj)) 대신 structuredClone() 으로 깊은 복사를 하세요.

"Unexpected token o" 와 "[object Object] is not valid JSON" 은 같은 오류인가요?

근본 원인이 같습니다 —— 파싱 전에 객체가 문자열화된 것입니다. 최신 V8 빌드는 [object Object] is not valid JSON 표현을 출력하고, 이전 버전은 Unexpected token o 를 출력합니다. 전체 분석은 "[object Object] is not valid JSON" 수정하기 를 참고하세요.

원시 응답 확인하기

API 가 실제로 무엇을 반환하는지 모르겠다면, 원시 본문을 JSON Fix 에 붙여넣어 검증하세요. 유효한 JSON 이 아니면 검증기가 무엇이 잘못되었는지 정확히 알려줍니다.