JSON 파싱 오류 해결 - Unexpected token 단계별 점검
핵심 요약: JSON.parse 오류를 행과 문자 위치로 바꾼 뒤 후행 쉼표, 작은따옴표, 주석, 제어 문자, BOM, 잘림이라는 여섯 가지 원인을 차례로 배제합니다.

핵심부터 말하면, 파싱 오류가 나면 보고된 행과 열을 읽고 첫 번째 불법 문자를 찾은 다음, 닫히지 않은 따옴표인지, 빠진 쉼표인지, 후행 쉼표인지, BOM인지, 잘린 데이터인지 판단하세요. 문서 전체를 처음부터 다시 읽지 마세요.
오류 메시지는 "무엇이 틀렸는지"를 알려 주지 않습니다. "파서가 더 진행할 수 없게 된 지점"만 알려 줍니다. 그 두 위치는 대개 같은 곳이 아닙니다.
1. 먼저 오류를 좌표로 바꾸기
같은 깨진 JSON이라도 언어마다 전혀 다른 오류가 나오지만, 모두 "몇 번째 행, 몇 번째 문자"로 바꿀 수 있습니다:
| 환경 | 대표 오류 | 읽는 법 |
|---|---|---|
| 브라우저 / Node | Unexpected token } in JSON at position 42 |
position은 0부터 시작하는 문자 오프셋이며 행 번호가 아님 |
| Node 20+ | Unexpected non-whitespace character after JSON at position 42 |
앞부분은 유효하고 42 이후에 여분의 내용이 있다는 뜻 |
| Java Jackson | Unexpected character ('}' (code 125)): was expecting ... at [Source: (String)..."..." ; line: 3, column: 1] |
행과 열을 바로 알려 주며 가장 활용하기 좋음 |
| Python | json.decoder.JSONDecodeError: Expecting ',' delimiter: line 3 column 1 (char 42) |
char는 오프셋이고, 실제로 중요한 것은 행과 열 |
| Go | invalid character '}' looking for beginning of object key string |
문자만 알려 주므로 스스로 역추적해 위치를 찾아야 함 |
위치를 얻었다면 JSON을 JSON 온라인 파서에 붙여 넣으세요. 도구가 오류 위치에 커서를 두고 행과 열 번호를 표시합니다. 이 단계만으로 문자를 눈으로 훑는 시간의 90%가 줄어듭니다.
2. 빈도 높은 여섯 가지 오류
1. 후행 쉼표
{
"name": "json",
"version": 1,
}
} 앞에 쉼표가 하나 더 있습니다. JSON 명세는 후행 쉼표를 허용하지 않지만 JavaScript 객체 리터럴은 허용하기 때문에, 코드에서 복사해 온 설정에 가장 자주 붙어 있습니다. 해결: 마지막 쉼표를 지웁니다. 이 데이터가 원래 JS 파일에 속한다면 억지로 JSON으로 바꾸지 말고 JSON5 변환을 쓰는 편이 낫습니다.
2. 작은따옴표와 따옴표 없는 키
{ 'name': 'json' }
{ name: "json" }
JSON 문자열은 큰따옴표만 쓸 수 있고, 키도 큰따옴표로 감싼 문자열이어야 합니다. 이 두 형태는 JSON5 / JavaScript의 느슨한 문법이지 JSON이 아닙니다. 문자열 안의 큰따옴표는 \", 역슬래시는 \\로 써야 합니다.
3. 주석
{
// Timeout in milliseconds
"timeout": 3000
}
RFC 8259의 문법에는 주석이 설 자리가 없습니다. 주석을 지우거나, .jsonc / .json5로 저장하고 주석을 지원하는 파서를 쓰세요. 여러 "주석 방안"의 장단점은 JSON에 주석을 쓰는 방법을 참고하세요.
4. 문자열 안의 실제 줄 바꿈과 제어 문자
{"sql": "SELECT *
FROM t"}
JSON 문자열에는 실제 줄 바꿈, 탭, U+0000–U+001F 제어 문자를 넣을 수 없고 \n, \t, \u0009로 써야 합니다. 여러 줄 텍스트를 JSON에 넣을 때는 먼저 에디터에서 한 번 이스케이프하거나 JSON 압축 및 이스케이프로 일괄 처리하세요.
5. BOM 헤더
Windows 메모장이 저장한 UTF-8에는 EF BB BF 세 바이트가 붙습니다. JSON.parse는 BOM을 만나면 Unexpected token을 보고하고, 눈으로는 전혀 보이지 않습니다. 확인 방법: 파일 헤더를 16진수로 검사하거나, 내용을 온라인 도구에 복사해 첫 문자가 로 표시되는지 봅니다. 해결: "BOM 없는 UTF-8"으로 저장합니다.
6. 잘린 내용
앞 4096자만 출력한 로그, 게이트웨이에서 잘린 API 응답, 중단된 다운로드가 여기에 속합니다. 이 부류의 특징은 "오류가 맨 끝에 있다"는 것과 괄호 개수가 맞지 않는다는 것입니다. 먼저 괄호를 세세요. {와 [의 닫는 괄호 수가 맞지 않으면 문법 오류가 아니라 잘림입니다. 그럴 때는 고칠 것이 없습니다. 완전한 데이터를 다시 가져와야 합니다.
3. 중국어 텍스트와 이스케이프에 관한 두 가지 거짓 경보
중국어 텍스트가 \u4e2d\u6587 형태로 바뀜: 이것은 유효한 JSON 이스케이프이지 깨진 텍스트가 아닙니다. 파싱하면 값이 원래 중국어로 복원되며, \u 형태는 원시 문자열을 직접 볼 때만 나타납니다. 사람에게 보여 줄 때는 포매팅 도구로 출력하면 됩니다. 중국어 텍스트가 URL이나 로그에서 살아남아야 할 때만 이스케이프 전략을 고민하면 됩니다.
중국어 텍스트가 涓枃 같은 깨진 문자로 바뀜: 이것은 인코딩 문제이지 JSON 문제가 아닙니다. UTF-8 바이트를 GBK로 디코딩할 때 생기며, "중국어 한 글자를 세 글자가 대신하는" 규칙적인 패턴을 보입니다. 응답 헤더의 Content-Type: application/json; charset=utf-8을 확인하고, 파일을 읽을 때 encoding='utf-8'이 명시되어 있는지 확인하세요.
4. 5분 수리 워크플로
- 도구에 붙여 넣기: 먼저 온라인 파서로 첫 번째 오류를 찾고, 한 번에 하나만 고칩니다.
- 고칠 때마다 검증: 변경 후 매번 재파싱합니다. 여러 곳을 한꺼번에 고치면 새 오류가 기존 오류를 가릴 수 있습니다.
- 한 번 압축하기: 압축으로 공백을 제거하고 다시 파싱합니다. 이렇게 하면 "구조 오류"와 "공백이나 줄 바꿈 문제"를 구분할 수 있습니다.
- 구조 비교: 변경 후 갑자기 오류가 나면 이전 버전과 JSON diff를 떠 보세요. 차이는 대개 쉼표 하나입니다.
- 필드 수준 검증: 문법은 통과하는데 비즈니스 계층이 거부한다면(필드 누락, 타입 불일치) 이는 JSON Schema 검증의 영역이지 문법 문제가 아닙니다.
5. "똑같아 보이는" JSON 조각 중 하나만 파싱되는 이유
가장 흔한 원인은 보이지 않는 문자입니다. 전각 쉼표 ,, 전각 따옴표 “”, 줄 바꿈 없는 공백 U+00A0, 너비 0 공백 U+200B 등입니다. 화면에서는 반각 문자와 거의 구별되지 않지만, 어느 것도 JSON 문법의 일부가 아닙니다.
확인 방법은 하나뿐입니다. 문제 문자 주변의 내용을 복사해 코드 포인트를 바이트 단위로 검사하세요. 브라우저 콘솔에서 다음을 실행하면 비ASCII이면서 CJK 범위를 벗어난 모든 문자를 나열합니다:
[...text].filter((ch) => {
const code = ch.codePointAt(0);
return code > 127 && !(code >= 0x4e00 && code <= 0x9fff);
});
출력에 0x201c(왼쪽 큰따옴표), 0xff0c(전각 쉼표), 0x00a0(줄 바꿈 없는 공백)이 보이면 그 문자가 범인입니다.
6. 수리보다 예방이 싸다
- 설정 파일은 커밋 전에 파싱 검사를 실행하세요. CI에 한 줄이면 됩니다:
node -e "JSON.parse(require('fs').readFileSync('config.json','utf8'))"; - API 응답은 직렬화 라이브러리로 생성하고 문자열을 직접 이어 붙이지 마세요. 손으로 만든 문자열은 후행 쉼표와 누락된 따옴표의 유일한 근원입니다.
- JSON을 로그로 남길 때는 템플릿에 보간하지 말고 먼저
JSON.stringify를 호출하세요. 절반만 작성된 페이로드가 완전한 메시지로 오인되지 않습니다.
FAQ
JSON 문법 오류 위치를 빠르게 찾으려면?
파서가 보고한 position 또는 행/열을 사용하세요. position은 행 번호가 아니라 0부터 시작하는 문자 오프셋이고, Java Jackson과 Python은 행과 열을 직접 알려 줍니다. JSON을 JSON 온라인 파서에 붙여 넣으면 도구가 오류 위치에 커서를 두고 행과 열을 표시합니다. 오류가 맨 앞에 있다면 파일 헤더의 BOM과 보이지 않는 문자를 확인하세요.
코드에서 복사한 설정에 후행 쉼표가 자주 붙는 이유는?
JSON 명세는 후행 쉼표를 금지하지만 JavaScript 객체 리터럴은 허용하므로, JS 코드에서 복사해 온 설정에 쉽게 따라붙습니다. 해결은 마지막 쉼표를 지우는 것입니다. 데이터가 원래 JS 파일에 속한다면 JSON으로 억지로 바꾸기보다 JSON5 변환이 더 잘 맞습니다.
주석이 들어 있는 설정 파일을 일반 JSON으로 파싱할 수 있나?
안 됩니다. RFC 8259의 문법에는 주석이 설 자리가 없어 직접 파싱하면 오류가 납니다. 주석을 지우거나 .jsonc / .json5로 저장하고 주석을 지원하는 파서를 쓰세요. 주석 방안의 장단점은 JSON에 주석을 쓰는 방법을 참고하세요.