SyntaxError: Bad escaped character in JSON at position N 은 파서가 JSON 문자열 안에서 백슬래시(\)를 만났는데, 그 다음 문자가 JSON이 허용하는 이스케이프 문자가 아니었다는 뜻입니다. RFC 8259 section 7에 따르면, JSON 문자열은 큰따옴표, 백슬래시, 슬래시, 제어 이스케이프 b, f, n, r, t, 또는 소문자 u 뒤에 정확히 네 자리 16진수가 오는 유니코드 이스케이프만 이스케이프할 수 있습니다.
실제 디버깅에서 이 에러는 대개 복사된 값 하나에서 비롯됩니다: Windows 경로(C:\Users\Ada), JavaScript 또는 셸 이스케이프(\x1b), 정규식 패턴(\d+), Python 스타일의 유니코드 이스케이프(\U0001F600), 혹은 로그에서 절반만 언이스케이프된 문자열입니다. 해결책은 "백슬래시를 지우는 것"이 아닙니다. 최종 문자열 값이 무엇이어야 하는지 먼저 결정한 뒤, 그 값을 표현하는 JSON 텍스트를 작성하는 것입니다.
이 가이드는 JavaScript JSON.parse()의 문구를 기준으로 하지만, 같은 규칙이 Python json.loads(), Go encoding/json, Ruby JSON.parse, PHP json_decode, jq, Postgres jsonb, 그리고 대부분의 엄격한 JSON 파서에도 적용됩니다.
어떤 문자열 에러인가요?
- Bad escaped character:
\뒤에 JSON이 허용하지 않는 문자가 옴 —— 예:\x,\d,\',\Users.- Bad control character: 원시 탭, 개행, NUL 바이트, 또는 ANSI ESC 바이트가 문자열 안에 그대로 나타남.
- Unterminated string:
"로 열린 문자열이 닫히지 않음.
30초 만에 고치기
- 보고된
position,line, 또는column위치로 이동합니다. - 그 위치 바로 앞에 백슬래시가 있는지 확인합니다.
- 백슬래시 다음 문자를 확인합니다.
- 백슬래시가 데이터의 일부라면
\\로 씁니다. - 이스케이프가 다른 언어의 것이라면(
\x,\d,\U) JSON 문법으로 변환합니다. - 백슬래시가 인용된 로그 줄에서 복사되어 온 것뿐이라면, 정규식으로 제거하지 말고 한 층씩 파싱합니다.
예:
{"path":"C:\Users\Ada\file.json"}
^
JSON 백슬래시 뒤에 U는 유효하지 않음
올바른 JSON 텍스트:
{
"path": "C:\\Users\\Ada\\file.json"
}
파싱 후 실제 애플리케이션 값은 여전히:
C:\Users\Ada\file.json
두 배로 늘어난 백슬래시는 JSON 텍스트 안에만 존재합니다.
에러는 어떻게 보이나
엔진마다 문구가 조금씩 다릅니다:
// V8: Chrome, Node.js, Edge
SyntaxError: Bad escaped character in JSON at position 12
// Firefox
SyntaxError: JSON.parse: bad escaped character at line 1 column 13 of the JSON data
// Safari
SyntaxError: JSON Parse error: Invalid escape character \x
V8의 position은 보통 백슬래시가 아니라 백슬래시 다음 문자를 가리킵니다. 아래의 잘못된 JSON에서 보고되는 문자는 \Users의 U입니다:
{"path":"C:\Users\Ada\file.json"}
^^
\U 가 잘못된 이스케이프
따라서 메시지가 position 12라고 말하면, 위치 12 앞뒤 좁은 구간을 살펴보세요. 잘못된 문자도 유용하지만, 그 앞의 백슬래시가 버그를 설명합니다.
JSON이 허용하는 이스케이프 전부
JSON 문자열 안의 백슬래시는 다음 이스케이프만 도입할 수 있습니다:
| JSON 이스케이프 | 파싱된 문자 | 비고 |
|---|---|---|
\" |
" |
JSON 문자열 안의 큰따옴표에 필수 |
\\ |
\ |
리터럴 백슬래시에 필수 |
\/ |
/ |
선택 사항; / 는 이스케이프 없이도 유효 |
\b |
백스페이스 | U+0008 |
\f |
폼피드 | U+000C; Windows 경로에서 \file이 위험한 이유 |
\n |
라인 피드 | U+000A |
\r |
캐리지 리턴 | U+000D |
\t |
탭 | U+0009 |
\uXXXX |
유니코드 코드 유닛 | 소문자 u 뒤에 정확히 네 자리 16진수 |
그 외의 것은 모두 유효하지 않은 JSON입니다: \x, \', \d, \s, \w, \0, \v, \e, \U, \u{1F600}, \N{...}, \cA, 그리고 \u12 같은 짧은 유니코드 이스케이프.
빠른 수정 표
최종 값이 무엇이 되어야 하는지 이미 알고 있다면 이 표를 사용하세요.
| 잘못된 JSON 텍스트 | 실패 이유 | 유효한 JSON 텍스트 |
|---|---|---|
{ "path": "C:\Users\Ada\file.json" } |
\U 와 \A 가 무효; \f 는 유효하지만 경로 구분자가 아니라 폼피드가 됨. |
{ "path": "C:\\Users\\Ada\\file.json" } |
{ "path": "C:/Users/Ada/file.json" } |
실패하지 않음. 슬래시는 이스케이프가 필요 없음. | 소비자가 슬래시를 받아들이면 그대로 유지. |
{ "color": "\x1b[32mOK\x1b[0m" } |
JSON에는 \xNN 이스케이프가 없음. |
{ "color": "\u001b[32mOK\u001b[0m" } |
{ "name": "O\'Brien" } |
JSON 문자열에서 아포스트로피는 이스케이프가 필요 없음. | { "name": "O'Brien" } |
{ "pattern": "^\d{4}-\d{2}-\d{2}$" } |
\d 는 정규식 이스케이프이지 JSON 이스케이프가 아님. |
{ "pattern": "^\\d{4}-\\d{2}-\\d{2}$" } |
{ "char": "\u12" } |
\u 다음에는 정확히 4자리 16진수가 와야 함. |
{ "char": "\u0012" } |
{ "emoji": "\u{1F600}" } |
JavaScript는 소스 문자열에서 지원하지만 JSON은 지원하지 않음. | { "emoji": "😀" } 또는 { "emoji": "\uD83D\uDE00" } |
한 가지 까다로운 세부 사항은 별도의 경고가 필요합니다: \f 는 유효한 JSON 이스케이프입니다. Windows 경로에 \file이 포함되어 있으면, 파서는 이를 폼피드 문자 뒤에 ile이 오는 것으로 변환할 수 있습니다. 파싱은 성공하지만 경로 값은 손상됩니다. 그래서 경로 문자열을 무턱대고 "복구"하는 것은 위험합니다.
원인 1: JSON에 복사된 Windows 경로
Windows 경로는 사람이 백슬래시를 경로 구분자로 읽기 때문에 무해해 보입니다:
{ "downloadDir": "C:\Users\Ada\Downloads" }
JSON은 백슬래시를 이스케이프 시퀀스의 시작으로 읽습니다. \U 를 보고, 대문자 U 가 JSON 이스케이프가 아니라서 중단합니다.
JSON에서는 백슬래시를 두 배로 씁니다:
{
"downloadDir": "C:\\Users\\Ada\\Downloads"
}
수신 프로그램이 슬래시를 받아들이면 슬래시를 사용해도 됩니다:
{
"downloadDir": "C:/Users/Ada/Downloads"
}
설정 파일에는 슬래시가 실수를 줄이는 경우가 많습니다. 정확한 Windows 전용 값이라면, 두 배 백슬래시가 이식성 있는 JSON 표현입니다.
원인 2: JavaScript 소스 문자열과 JSON 텍스트의 혼동
웹의 많은 예제가 여기서 사람들을 헷갈리게 합니다. 두 개의 계층이 있습니다:
- JavaScript 소스 문자열 문법
- 그 JavaScript 문자열 안의 JSON 텍스트 문법
이 JavaScript 소스 코드는 유효합니다:
const raw = '{"path":"C:\\Users\\Ada"}';
JSON.parse(raw);
하지만 파서에 도달하는 JSON 텍스트는:
{"path":"C:\\Users\\Ada"}
JavaScript가 먼저 백슬래시를 소비하지 않도록 JavaScript에서 잘못된 JSON 샘플을 테스트하고 싶다면 String.raw 를 사용하세요:
const broken = String.raw`{"path":"C:\Users\Ada"}`;
JSON.parse(broken);
JSON.parse() 가 진짜 잘못된 JSON 텍스트를 받기 때문에 Bad escaped character 를 던집니다.
스택 트레이스를 읽을 때 이 사고 모델을 사용하세요: JSON이 .json 파일, HTTP 본문, localStorage 값, 또는 데이터베이스 문자열에서 왔다면 JSON 텍스트를 고치세요. JSON이 JavaScript 소스 문자열 안에 있다면 JavaScript용 한 층, JSON용 또 한 층의 이스케이프가 필요할 수 있습니다.
원인 3: 다른 언어의 이스케이프를 가져옴
JSON은 \n 과 \t 를 받아들이지만, 프로그래밍 언어에서는 흔한 많은 이스케이프를 받아들이지 않습니다:
{ "code": "\x1b[0m", "name": "O\'Brien" }
유효한 JSON:
{
"code": "\u001b[0m",
"name": "O'Brien"
}
흔한 가짜 친구들:
| 이스케이프 | 유효한 곳 | JSON 수정 |
|---|---|---|
\x1b |
JavaScript, Python, 다수의 셸 | \u001b |
\' |
JavaScript/Python 작은따옴표 문자열 | 백슬래시 없이 ' 사용 |
\0 |
JavaScript/Python NUL 축약 | \u0000 |
\v |
JavaScript 수직 탭 | \u000b |
\U0001F600 |
Python 유니코드 이스케이프 | 리터럴 UTF-8 이모지 또는 서로게이트 쌍 |
\u{1F600} |
JavaScript 유니코드 코드포인트 이스케이프 | 리터럴 UTF-8 이모지 또는 서로게이트 쌍 |
생산자가 여러분의 코드라면, 모든 경우를 손으로 변환하지 마세요. 일반 객체를 만들고 언어의 JSON 직렬화기가 유효한 JSON을 쓰도록 하세요.
원인 4: JSON 설정에 저장된 정규식 패턴
정규식은 자체 이스케이프 언어를 갖고 있습니다. JSON 문자열은 별도의 이스케이프 언어를 갖고 있습니다. 정규식의 백슬래시는 정규식 엔진에 도달하기 전에 JSON 파싱에서 살아남아야 합니다.
잘못된 JSON 설정:
{ "datePattern": "^\d{4}-\d{2}-\d{2}$" }
유효한 JSON 설정:
{
"datePattern": "^\\d{4}-\\d{2}-\\d{2}$"
}
JSON 파싱 후, 애플리케이션은 다음 문자열을 봅니다:
^\d{4}-\d{2}-\d{2}$
그다음에야 정규 표현식이 됩니다:
const config = JSON.parse('{"datePattern":"^\\\\d{4}-\\\\d{2}-\\\\d{2}$"}');
const re = new RegExp(config.datePattern);
동일한 규칙이 \s, \w, \b, 이름 있는 그룹, 룩비하인드 예제, 그리고 치환 문자열에도 적용됩니다. 백슬래시가 이후의 파서를 위한 것이라면 JSON에서는 두 배로 쓰세요.
원인 5: 잘못된 형식의 유니코드 이스케이프
JSON의 유니코드 이스케이프는 고정 너비입니다:
{ "char": "\u12" }
유효한 JSON:
{
"char": "\u0012"
}
u 는 소문자여야 하고 정확히 네 자리 16진수(0-9, a-f, A-F)가 뒤따라야 합니다.
다음은 JSON의 유니코드 이스케이프가 아닙니다:
"\u{2028}" // JavaScript 소스 스타일, JSON 아님
"\U00002028" // Python 스타일, JSON 아님
"\u20G0" // G 는 16진수가 아님
기본 다국어 평면(BMP) 밖의 문자, 예를 들어 많은 이모지와 일부 수학 기호는 UTF-8 JSON에 리터럴로 저장할 수 있습니다:
{
"emoji": "😀"
}
이스케이프될 때는 UTF-16 서로게이트 쌍으로 표현됩니다:
{
"emoji": "\uD83D\uDE00"
}
\uD83D 처럼 짝이 되는 하위 서로게이트가 없는 고립된 서로게이트는 피하세요. 어떤 파서는 이를 코드 유닛으로 받아들이지만, 잘 형성된 유니코드를 요구하는 하류 시스템은 거부할 수 있습니다.
원인 6: 손으로 만든 JSON 문자열
이것이 이 버그의 프로덕션 버전입니다:
// 안전하지 않음: userInput 에 백슬래시, 큰따옴표, 개행이 있을 수 있음.
const payload = '{"message":"' + userInput + '"}';
userInput 이 C:\Users\Ada 라면, 방출된 텍스트는 유효하지 않은 JSON입니다. " 를 포함하면 JSON이 다른 방식으로 깨질 수 있습니다. 원시 개행이 포함되면 대신 'bad control character' 를 받을 수도 있습니다.
직렬화기를 사용하세요:
const payload = JSON.stringify({
message: userInput,
path: 'C:\\Users\\Ada\\file.json',
code: '\x1b[32mOK\x1b[0m',
});
JSON.stringify() 는 JSON 고유의 이스케이프를 처리합니다. 결과는 유효한 JSON 텍스트입니다:
{
"message": "...",
"path": "C:\\Users\\Ada\\file.json",
"code": "\u001b[32mOK\u001b[0m"
}
동일한 원리가 다른 언어에도 적용됩니다:
import json
payload = json.dumps({
"path": r"C:\Users\Ada\file.json",
"pattern": r"^\d+$",
})
body, err := json.Marshal(map[string]string{
"path": `C:\Users\Ada\file.json`,
"pattern": `^\d+$`,
})
생산자를 고치는 중이라면 이것이 진짜 해결책입니다. 하류에서 잘못된 JSON을 패치하는 것은 잘못된 텍스트가 만들어진 곳을 감출 뿐입니다.
잘못된 이스케이프를 찾는 방법
붙여넣은 JSON에 대해서는, 다음의 작은 헬퍼가 V8의 position 주변 영역을 보기 쉽게 만들어 줍니다:
function showJsonParseContext(raw) {
try {
JSON.parse(raw);
console.log('Valid JSON');
} catch (error) {
const message = String(error.message);
const match = message.match(/position (\d+)/);
if (!match) {
console.log(message);
return;
}
const pos = Number(match[1]);
const start = Math.max(0, pos - 24);
const end = Math.min(raw.length, pos + 24);
const excerpt = raw.slice(start, end);
console.log(message);
console.log(JSON.stringify(excerpt));
console.log(' '.repeat(pos - start) + '^');
}
}
const raw = String.raw`{"path":"C:\Users\Ada\file.json"}`;
showJsonParseContext(raw);
JSON.stringify(excerpt) 는 의도적입니다. 백슬래시와 제어 문자를 눈에 보이는 이스케이프로 표시하는데, 이는 버그가 보이지 않는 공백이거나 과도한 이스케이프일 때 정확히 필요한 것입니다.
Firefox 스타일의 line 과 column 에러의 경우, 먼저 해당 줄로 이동한 다음 그 줄의 문자열 리터럴을 검사하세요. 정확한 열이 백슬래시 뒤에 떨어지면 그 앞의 문자도 함께 읽으세요.
복구 도구를 쓸까, 페이로드를 거부할까?
다음의 경우 복구 도구를 사용하세요:
- 붙여넣은 스니펫을 정리하는 경우.
- 로그 줄을 디버깅하는 경우.
- LLM 출력을 검토하는 경우.
- 복구된 값을 시각적으로 확인할 수 있는 경우.
- 값이 금전 이동, 권한, 삭제, 되돌릴 수 없는 상태 변경을 유발하지 않는 경우.
다음의 경우 페이로드를 거부하고 생산자를 고치세요:
- JSON이 API 계약에서 온 경우.
- 값이 청구, 권한, 보안, 또는 데이터 삭제에 영향을 주는 경우.
- 파서가 여러 가능한 의미 사이에서 추측해야 했던 경우.
- 경로, 정규식, 또는 이스케이프 시퀀스가 유효할 수 있지만 의미상 잘못될 수 있는 경우.
예를 들어 C:\Users\Ada\file.json 을 복구하는 것은 단순한 문법 작업이 아닙니다. \file 의 \f 는 유효한 이스케이프라서, 도구가 백슬래시를 보존하는 대신 폼피드 문자를 파싱할 수 있습니다. 사람 또는 생산자 코드가 의도한 경로를 결정해야 합니다.
이 사이트의 JSON Fix 도구는 브라우저 로컬 디버깅 보조 도구로 가장 잘 사용됩니다: 텍스트를 붙여 넣고, 출력을 검사한 다음, 복구된 JSON을 검증하세요. 잘못된 형식의 프로덕션 페이로드를 위한 조용한 수집 계층으로 삼아서는 안 됩니다.
JSON을 안전하게 언이스케이프하는 방법
때로 백슬래시가 잘못된 것이 아니라, JSON이 이중 인코딩된 경우가 있습니다. 로그에서 이런 것을 볼 수 있습니다:
{\"name\":\"Ada\",\"path\":\"C:\\\\Users\\\\Ada\"}
일괄 replace(/\\/g, '') 를 실행하지 마세요. 진짜 이스케이프까지 파괴합니다.
한 번에 유효한 JSON 한 층씩 파싱하세요:
// 바깥쪽 값은 JSON 텍스트를 담고 있는 JSON 문자열이다.
const wrapped = '"{\\"name\\":\\"Ada\\",\\"path\\":\\"C:\\\\\\\\Users\\\\\\\\Ada\\"}"';
const once = JSON.parse(wrapped);
// once 는: {"name":"Ada","path":"C:\\Users\\Ada"}
const data = JSON.parse(once);
// data 는: { name: "Ada", path: "C:\\Users\\Ada" }
첫 번째 파싱이 Bad escaped character 로 실패하면, 입력은 단순히 인코딩된 것이 아닙니다. 유효하지 않은 JSON 텍스트이며 표적화된 복구가 필요합니다.
예방 체크리스트
- 절대 사용자 문자열을 JSON에 연결하지 마세요.
JSON.stringify(),json.dumps(),json.Marshal(), 또는 플랫폼의 JSON 직렬화기를 사용하세요.- 정규식 패턴은 JSON에 백슬래시를 두 배로 저장하세요.
- 소비자가 받아들인다면 경로에는 슬래시를 선호하세요.
- 로그, 셸, 문서에서 복사한 예제는 따옴표를 붙이고 테스트하세요.
- 생성된
.json파일을 CI에서 실제 파서로 검증하세요. - 전체 페이로드를 로그로 남기지 말고, 파서 위치 주변의 안전한 미리보기를 로그로 남기세요.
- 자동 복구는 개발자 워크플로로 취급하지, 프로덕션 계약으로 취급하지 마세요.
자주 묻는 질문
'Bad escaped character in JSON' 이 무슨 뜻인가요?
JSON 문자열 안의 백슬래시 뒤에 JSON이 \ 다음에 허용하지 않는 문자가 옵니다. 유효한 이스케이프는 ", \, /, b, f, n, r, t, 그리고 uXXXX 입니다.
JSON에서 Windows 경로를 어떻게 고치나요?
모든 경로 백슬래시를 \\ 로 쓰세요 —— 예: C:\\Users\\Ada\\file.json. 수신 프로그램이 슬래시를 받아들이면 C:/Users/Ada/file.json 이 유효한 JSON이고 읽기도 더 쉽습니다.
정규식이 JavaScript에서는 되는데 JSON에서는 왜 실패하나요?
JSON 파서는 정규식 엔진보다 먼저 문자열을 봅니다. \d 같은 정규식 이스케이프는 JSON에서 \\d 로 써야, 파싱된 문자열이 여전히 \d 를 포함합니다.
\x1b 는 유효한 JSON인가요?
아니요. \xNN 은 JavaScript, Python, 셸 예제에서 흔하지만 JSON은 지원하지 않습니다. ANSI ESC 문자에는 \u001b 를 사용하거나, 로그를 직렬화하기 전에 ANSI 색상 코드를 제거하세요.
이건 'Bad control character' 와 같은 건가요?
아니요. 'Bad escaped character' 는 백슬래시 다음 문자가 무효라는 뜻입니다. 'Bad control character' 는 원시 제어 바이트 —— 리터럴 개행, 탭, NUL, 또는 ESC 바이트 —— 가 JSON 문자열 안에 나타난다는 뜻입니다.
JSON 복구 도구가 잘못된 이스케이프를 자동으로 고칠 수 있나요?
의도한 값이 명백한 붙여넣은 스니펫에서는 때때로 가능합니다. API 페이로드, 보안 민감 데이터, 결제, 권한, 삭제, 또는 \f, \n, \t 가 유효할 수는 있지만 의도되지 않았을 값에 대해서는 자동 복구를 조용히 수행하지 마세요.
JSON을 어떻게 언이스케이프하나요?
JSON.parse() 로 한 번에 한 층씩 파싱하세요. 이중 인코딩된 값은 첫 파싱 후 일반 JSON 문자열이 되고, 두 번째 파싱 후 실제 객체나 배열이 됩니다. 정규식 백슬래시 제거는 유효한 이스케이프를 손상시키므로 피하세요.
소스 코드에서 이 에러를 어떻게 예방하나요?
네이티브 값을 만들고 JSON.stringify() 또는 해당 언어의 직렬화기로 직렬화하세요. 문자열 연결로 JSON을 조립하지 마세요.
지금 고치기
- JSON Fix —— 브라우저에서 유효하지 않은 이스케이프를 찾아 수정합니다.
- JSON Stringify —— JSON 문자열 리터럴을 이스케이프하고 언이스케이프합니다.
- Escape JSON as a String Literal —— 중첩되고 이중 인코딩된 JSON을 다룹니다.
- Bad Control Character in JSON —— 원시 제어 바이트와 이스케이프된 텍스트.
- Unterminated String in JSON —— JSON 문자열이 닫히지 않는 경우.
- JSON.parse "Unexpected Token" 에러를 고치는 방법 —— 더 넓은 JSON 파서 에러 가이드.
출처
- RFC 8259 section 7 —— JSON 문자열 문법과 이스케이프 전체 목록.
- MDN JSON.parse —— JavaScript 파서 동작과
SyntaxError처리. - MDN JSON.stringify —— JavaScript 값에서 안전한 JSON 생성.
- MDN JSON.parse bad parsing errors —— 흔한 브라우저 JSON 파싱 에러 메시지.
- MDN String and UTF-16 —— UTF-16 코드 유닛, 서로게이트 쌍, 잘 형성된 문자열.
최종 검토: 2026년 7월.