FormatArc JSON 포맷터 실행 결과FormatArc JSON 포맷터 실행 결과
게시일: 2026-03-18갱신일: 2026-07-31

JSON 파싱 오류 해결 - unexpected token 에러의 원인과 수정 방법

JSON을 다루다 보면 한 번은 마주치게 되는 것이 SyntaxError: Unexpected token 오류 메시지입니다. API 응답을 처리하려고 할 때, 설정 파일을 읽으려고 할 때 갑자기 이 오류가 나타나 작업이 멈춰 버립니다.

빠르게 오류 위치를 찾고 싶다면 JSON 포맷터에 붙여넣기만 하면 오류가 발생한 줄 번호와 원인이 표시됩니다.

원인만 알면 수정은 간단하지만, 오류 메시지만으로는 무엇이 잘못됐는지 판단하기 어려운 경우가 많습니다. 이 글에서는 JSON 파싱 오류를 읽는 방법과 자주 발생하는 원인 5가지를 구체적인 예제와 함께 설명합니다.

SyntaxError: Unexpected token 읽는 방법

브라우저나 Node.js에서 JSON을 파싱할 때 문법 오류가 있으면 다음과 같은 메시지가 반환됩니다.

SyntaxError: Unexpected token ' in JSON at position 42

이 메시지는 세 가지 정보를 담고 있습니다.

  • Unexpected token ' — 파서가 예상하지 못한 문자 '(작은따옴표)를 만났다
  • at position 42 — JSON 문자열의 처음부터 42번째 문자에서 오류가 발생했다
  • SyntaxError — 구문 자체가 깨져 있다(값의 문제가 아니라 작성 방식의 문제)

position 숫자는 바이트 위치가 아니라 문자 위치를 가리킵니다. 다만 줄바꿈 문자와 공백도 함께 세어지기 때문에 직접 세는 것은 현실적이지 않습니다. JSON 포맷터 같은 도구를 사용하면 해당 위치를 바로 찾을 수 있습니다.

자주 발생하는 원인 5가지

1. 후행 쉼표(trailing comma)

JavaScript에서는 배열이나 객체의 마지막에 쉼표를 붙여도 동작합니다. 하지만 JSON 사양에서는 후행 쉼표가 허용되지 않습니다.

{
  "name": "Alice",
  "age": 30,
}

마지막 30,의 쉼표를 삭제해야 합니다.

{
  "name": "Alice",
  "age": 30
}

에디터 포매터가 자동으로 후행 쉼표를 붙이는 설정이라면 JSON 파일에서는 꺼 두는 것이 좋습니다.

2. 작은따옴표

Python 딕셔너리나 JavaScript 객체에서는 '를 쓸 수 있지만, JSON에서는 큰따옴표 "만 유효합니다.

{'name': 'Alice'}

이것은 올바른 JSON이 아닙니다. 모두 큰따옴표로 바꿔야 합니다.

{"name": "Alice"}

Python에서 딕셔너리를 JSON 문자열로 만들 때는 str()이 아니라 json.dumps()를 사용하세요. str()은 작은따옴표로 출력해 버립니다.

3. 따옴표 없는 키

JavaScript에서는 객체의 키에 따옴표가 필요 없는 경우가 있습니다. JSON에서는 키를 반드시 큰따옴표로 감싸야 합니다.

{name: "Alice"}

올바르게는 이렇게 작성합니다.

{"name": "Alice"}

4. 주석

설정 파일에서 자주 발생하는 실수입니다. JSON 사양에는 주석 문법이 존재하지 않습니다.

{
  // 사용자 이름
  "name": "Alice"
}

///* */는 모두 제거해야 합니다. 참고로 tsconfig.json 등 일부 파일은 JSONC(JSON with Comments)라는 확장 사양으로 작성되어 주석이 허용됩니다. 다만 표준 JSON 파서로는 읽을 수 없습니다.

주석을 꼭 남기고 싶은 설정 파일이라면 JSON 대신 YAML로 관리하는 선택지도 있습니다. YAML은 # 문자로 주석을 기본 지원하며, YAML JSON 변환기로 두 형식을 쉽게 오갈 수 있습니다.

5. BOM(Byte Order Mark)

UTF-8로 저장한 파일의 맨 앞에 BOM()이 들어 있으면 파서가 이를 잘못된 문자로 감지합니다.

SyntaxError: Unexpected token  in JSON at position 0

at position 0이면서 보이지 않는 문자가 토큰으로 보고된다면 BOM일 가능성이 높습니다. 텍스트 에디터에서 BOM 없는 UTF-8로 다시 저장하거나, 프로그램 쪽에서 맨 앞의 BOM을 제거하는 처리를 넣어 주세요.

const cleaned = text.replace(/^/, '');
const data = JSON.parse(cleaned);

그 밖의 원인

대표적인 5가지 외에도 다음과 같은 원인으로 파싱 오류가 발생합니다.

괄호 짝 불일치

닫는 괄호 }]가 부족하면 대부분 파일 끝에서 오류가 보고됩니다. 중첩이 깊은 JSON에서는 짝이 맞지 않는 괄호를 눈으로 찾기 어렵습니다. 괄호 대응을 표시해 주는 포매터를 사용하면 시간을 절약할 수 있습니다.

문자열 안의 제어 문자

문자열 안에 가공되지 않은 줄바꿈이나 탭이 들어가면 오류가 됩니다. 줄바꿈은 \n처럼 이스케이프해야 합니다.

{"message": "Hello
World"}

올바르게는 다음과 같이 작성합니다.

{"message": "Hello\nWorld"}

0으로 시작하는 숫자

JSON 숫자는 맨 앞에 0을 붙일 수 없습니다. 007은 잘못된 값이며 7과 같은 표준 숫자 표기로 바꿔야 합니다.

undefined와 NaN

JavaScript의 undefinedNaN은 JSON 값으로 사용할 수 없습니다. undefined가 포함된 객체를 직렬화하면 대부분의 직렬화 도구는 해당 키를 생략하거나 오류를 던집니다. NaNInfinity도 마찬가지로 거부됩니다.

증상별 해결법: token u / token o / end of input

오류 메시지에 들어 있는 문자 자체가 원인을 크게 좁혀 주는 단서가 됩니다. 다음 세 가지 변형은 모두 JSON 파일의 내용이 아니라 "파서에 넘기기 전" 단계에 문제가 있는 경우입니다. 참고로 Unexpected token <가 나왔다면 JSON이 아니라 HTML이 돌아왔다는 신호입니다(뒤의 환경별 오류 메시지 표를 참고하세요).

Unexpected token u in JSON at position 0

JSON.parse에 문자열 "undefined"가 넘어갔습니다. u는 그 첫 글자입니다. 파싱하려던 값이 그 시점에 undefined였다는 뜻으로 거의 확실하게 볼 수 있습니다.

const raw = localStorage.getItem("settings"); // 키가 없으면 null
JSON.parse(undefined); // SyntaxError: Unexpected token u in JSON at position 0

파싱하기 전에 값이 실제로 존재하는지 확인하세요. 본문이 빈 API 응답, 존재하지 않는 스토리지 키, 변수명 오타가 대표적인 원인입니다.

Unexpected token o in JSON at position 1

"[object Object]"라는 문자열을 파싱하려고 할 때 나오는 오류입니다. position 0의 [는 배열의 시작으로 유효하므로, 파서는 position 1의 o에서 실패합니다. JSON 문자열이 아니라 JavaScript 객체 자체를 JSON.parse에 넘기면 암묵적 문자열 변환으로 이 형태가 됩니다.

const data = { name: "Alice" };
JSON.parse(data); // data가 "[object Object]"로 변환되어 Unexpected token o

그 값은 이미 파싱이 끝난 상태입니다. 그대로 사용하거나, 깊은 복사가 목적이라면 stringify / parse 왕복 대신 structuredClone(data)를 사용하세요.

Unexpected end of JSON input

JSON이 완결되기 전에 문자열이 끝났습니다. 흔한 경우는 빈 문자열(JSON.parse(""))과 중간에 끊긴 응답(중단된 네트워크 요청이나 쓰는 중인 파일) 두 가지입니다. 먼저 원본 문자열의 길이를 로그로 출력해 보세요. 길이가 0이라면 문제는 JSON 문법이 아니라 파서에 넘기기 전 단계에 있습니다.

환경별 오류 메시지 차이

같은 문법 오류라도 오류 메시지는 실행 환경에 따라 다릅니다. 검색할 때 단서가 되도록 대표적인 환경의 메시지를 정리합니다.

환경 / 런타임대표적인 오류 메시지알 수 있는 것
브라우저 (Chrome / Firefox)Unexpected token < in JSON at position 0맨 앞의 <는 JSON이 아니라 HTML 오류 페이지가 반환됐다는 신호. 네트워크 탭에서 실제 응답을 확인
Node.jsUnexpected token } in JSON at position 142position 숫자로 위치를 특정할 수 있음
Pythonjson.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)줄 번호와 열 번호를 모두 표시. 빈 응답이나 HTML 응답을 파싱했을 때 자주 발생
Java (Jackson)JsonParseException: Unexpected character ('}' (code 125)): was expecting double-quote to start field name기대한 문자와 실제 문자를 모두 명시

특히 브라우저에서 Unexpected token < in JSON at position 0이 나온다면 API가 JSON이 아니라 HTML 오류 페이지(404나 500 페이지)를 반환하고 있을 가능성이 거의 확실합니다. JSON 문법이 아니라 요청 주소와 상태 코드를 의심해 보세요.

Python에서 자주 보이는 expecting value: line 1 column 1 (char 0)도 같은 유형입니다. 응답 본문이 비어 있거나 JSON이 아닌 내용을 json.loads()에 넘겼을 때 발생하므로, 먼저 응답 내용 자체를 출력해서 확인하는 것이 지름길입니다.

디버깅 절차

오류가 몇 줄짜리 파일이라면 눈으로 고칠 수 있지만, API 응답이나 큰 파일에서는 확인 순서를 정해 두면 더 빨리 해결됩니다.

  1. JSON 포맷터에 붙여넣어 오류의 줄 번호와 내용을 확인한다
  2. 보고된 position보다 조금 앞까지 살펴본다. 실제 실수는 오류 위치보다 앞줄에 있는 경우가 많다(예: 10행의 쉼표 누락이 11행에서 오류로 나타남)
  3. 위에서 다룬 원인 5가지(후행 쉼표·작은따옴표·따옴표 없는 키·주석·BOM)에 해당하지 않는지 확인한다
  4. 코드가 생성한 JSON이라면 직렬화 처리를 확인한다. 문자열 연결로 JSON을 조립하는 부분은 대표적인 버그의 온상이다
  5. API에서 받은 JSON이라면 파싱하기 전에 원본 응답과 인코딩을 확인한다

fetch로 응답을 받을 때는 response.json()을 쓰기 전에 원본 텍스트와 상태 코드를 확인하면, HTML 오류 페이지·빈 응답·끊긴 응답을 한곳에서 구분할 수 있습니다.

const response = await fetch("/api/data");
const raw = await response.text(); // response.json()이 아니라 먼저 원본 텍스트로 받는다

if (!response.ok) {
  console.error(`HTTP ${response.status}:`, raw.slice(0, 200));
} else if (!raw) {
  console.error("응답 본문이 비어 있음"); // Unexpected end of JSON input의 대표 원인
} else {
  try {
    const data = JSON.parse(raw);
  } catch (e) {
    console.error("JSON parse 실패:", e.message, raw.slice(0, 200));
  }
}

FormatArc로 오류 위치 확인하기

원인 5가지를 알고 있어도 수백 줄짜리 JSON 파일에서 문제 위치를 눈으로 찾는 것은 힘든 일입니다. JSON 포맷터를 사용하면 붙여넣기만 해도 오류가 발생한 줄 번호와 내용이 표시됩니다.

사용법은 3단계입니다.

  1. JSON 포맷터를 연다
  2. 왼쪽 에디터에 JSON을 붙여넣는다
  3. 실행 버튼을 누른다

올바른 JSON이라면 정렬된 결과가 오른쪽에 표시됩니다. 문법 오류가 있다면 오류 메시지에 줄 번호가 포함되므로 해당 줄을 수정하고 다시 실행하면 됩니다.

모든 처리가 브라우저 안에서 끝나기 때문에 API 키나 개인정보가 포함된 JSON도 안심하고 사용할 수 있습니다. 데이터가 서버로 전송되는 일은 없습니다.

파싱 오류를 미리 방지하기

오류가 난 뒤에 고치는 것보다 몇 가지 습관으로 파싱 오류의 대부분을 막을 수 있습니다.

  • JSON을 생성할 때는 문자열 연결이 아니라 JSON.stringify()(각 언어의 표준 직렬화 도구)를 사용한다
  • 에디터에서 저장 시 JSON을 검증하는 설정을 켠다(VS Code, IntelliJ, Sublime Text 모두 지원)
  • CI에 검증 단계를 추가한다. python -m json.tool < config.json 같은 간단한 검사로 배포 전에 깨진 파일을 잡을 수 있다
  • 직접 손으로 편집할 때는 실시간으로 검증되는 도구를 사용한다

또한 신뢰할 수 없는 온라인 도구에 업무 데이터를 붙여넣는 것은 피해 주세요. FormatArc는 모든 처리를 브라우저 안에서 끝내며 데이터를 외부로 전송하지 않습니다.

JSON이 맞지 않는 경우

주석을 쓰고 싶다, 여러 줄 문자열을 다루고 싶다, 복잡한 중첩 설정을 관리하고 싶다는 요구로 JSON의 제약과 자주 싸우고 있다면 YAML을 검토할 가치가 있습니다. YAML은 주석을 지원하고 여러 줄 텍스트도 자연스럽게 다룰 수 있어 설정 파일로는 읽기 쉬운 경우가 많습니다.

다만 YAML은 들여쓰기에 민감하고 암묵적 타입 변환 같은 고유한 함정도 있습니다. 두 형식은 필요할 때 YAML JSON 변환기로 서로 변환할 수 있으므로 상황에 맞게 구분해서 쓰면 충분합니다.

정리

JSON 파싱 오류의 대부분은 이 글에서 소개한 5가지 패턴 중 하나에 해당합니다. 오류 메시지의 Unexpected tokenposition을 단서로 원인을 좁힐 수 있지만, 파일이 크다면 도구에 맡기는 것이 효율적입니다.

JSON 포맷터에 붙여넣으면 오류 위치가 줄 번호와 함께 표시되고, 올바른 JSON이라면 그대로 정렬까지 끝납니다. CSV 데이터를 JSON으로 바꾸고 싶다면 CSV JSON 변환기도 함께 활용해 보세요.