알아두면 좋은 내용
JSON(JavaScript Object Notation)이란?
JSON은 값 종류가 6가지(문자열·숫자·불리언·null·객체·배열)뿐인 데이터 교환 포맷으로, 현행 표준은 IETF의 RFC 8259(2017)와 같은 문법을 정의한 ECMA-404입니다. 문법이 작다고 함정까지 없는 것은 아닙니다 — 숫자의 정밀도는 표준이 규정하지 않아 언어(구현체)마다 다르고, JavaScript 값 중 일부는 직렬화할 때 조용히 사라지며, 한글 이스케이프 기본값도 JavaScript와 Python이 서로 반대입니다. 아래에서 오류 메시지별 원인, 이런 함정, 변환 탭의 동작 규칙, 그리고 CLI에서 짝으로 쓰는 jq 치트시트까지 다룹니다.
JSON 파싱 오류 — 메시지별 원인과 고치는 법
정렬·압축·검증 탭은 브라우저 내장 JSON.parse로 검사하고, 오류 메시지 속 position N(0부터 센 문자 위치)이나 line N column N을 찾아 해당 줄과 ^ 표시로 위치를 보여 줍니다. 아래 표의 메시지는 Chrome·Edge·Node.js가 쓰는 V8 엔진에 잘못된 입력을 실제로 넣어 이 페이지를 빌드할 때 받은 문구입니다. Firefox·Safari는 문구가 다르므로 '원인' 열로 대조하세요. 표시되는 위치는 오류가 발견된 곳이라, 마지막 쉼표처럼 원인이 바로 앞 글자에 있는 경우가 많습니다.
| 원인 | 잘못된 입력 | V8 오류 메시지 | 고치는 법 |
|---|---|---|---|
| 객체 마지막 쉼표 | {"a": 1,} | Expected double-quoted property name in JSON at position 8 (line 1 column 9) | 마지막 값 뒤 쉼표 삭제. 쉼표 뒤에 다음 키를 기다렸기 때문에 "큰따옴표 키가 와야 한다"는 메시지가 나온다 |
| 배열 마지막 쉼표 | [1, 2,] | Unexpected token ']', "[1, 2,]" is not valid JSON | 마지막 원소 뒤 쉼표 삭제 |
| 작은따옴표 | {'a': 1} | Expected property name or '}' in JSON at position 1 (line 1 column 2) | 키·문자열 모두 큰따옴표(")로. Python dict를 print한 결과를 복사하면 흔하다 |
| 따옴표 없는 키 | {a: 1} | Expected property name or '}' in JSON at position 1 (line 1 column 2) | "a"처럼 키를 큰따옴표로. JS 객체 리터럴을 그대로 붙여 넣었을 때 흔하다 |
| NaN·undefined 값 | {"a": NaN} | Unexpected token 'N', "{"a": NaN}" is not valid JSON | JSON에는 NaN·Infinity·undefined가 없다. null이나 문자열로 바꾼다 |
| 주석 | {"a": 1} // 메모 | Unexpected non-whitespace character after JSON at position 9 (line 1 column 10) | 주석 삭제 (JSONC·JSON5 파서가 아닌 한 모두 오류) |
| 문자열 안 실제 줄바꿈 | {"a": "줄1↵줄2"} | Bad control character in string literal in JSON at position 9 (line 1 column 10) | \n으로 이스케이프. 탭 등 U+0000~U+001F 제어 문자도 모두 \t·\uXXXX로 |
| 숫자 앞자리 0 | {"n": 007} | Unexpected number in JSON at position 7 (line 1 column 8) | 7로 쓰거나, 우편번호·코드처럼 앞자리 0이 의미 있으면 "007" 문자열로 |
| 허용되지 않는 이스케이프 | {"a": "\x41"} | Bad escaped character in JSON at position 8 (line 1 column 9) | JSON 이스케이프는 \" \\ \/ \b \f \n \r \t \uXXXX뿐 — \x41은 \u0041로 |
| BOM | [BOM]{"a": 1} | Unexpected token '[BOM]', "[BOM]{"a": 1}" is not valid JSON | "UTF-8(BOM 없음)"으로 다시 저장. 메시지의 따옴표 사이 글자가 보이지 않는 BOM이다 |
| 빈 입력 | (빈 문자열) | Unexpected end of JSON input | 본문이 비어 있다 — 204 No Content나 실패한 요청에 response.json()을 불렀을 때 흔하다 |
| HTML 응답 | <!DOCTYPE html><html> | Unexpected token '<', "<!DOCTYPE "... is not valid JSON | API가 JSON 대신 오류·로그인 페이지를 돌려줬다. URL·인증·상태 코드부터 확인 |
↵는 문자열 안의 실제 줄바꿈, [BOM]은 보이지 않는 U+FEFF 문자를 표시한 것입니다.
BOM은 표준이 애매하게 다루는 경우입니다. RFC 8259는 JSON을 만드는 쪽이 BOM을 붙이면 안 된다(MUST NOT)고 하면서, 읽는 쪽은 오류 대신 무시해도 된다(MAY)고 허용합니다. 그래서 jq처럼 BOM을 건너뛰는 도구에서는 멀쩡하던 파일이 JSON.parse에서만 실패할 수 있습니다. 대표적인 경우가 Node.js에서 fs.readFileSync(경로, 'utf8')로 읽은 뒤 JSON.parse할 때입니다 — Node는 BOM을 지우지 않습니다(브라우저의response.json()·TextDecoder는 지움). 메모장·Excel에서 'UTF-8(BOM)'으로 저장한 파일이 스크립트에서만 첫 글자 오류를 낸다면 이것부터 의심하세요.
오류가 나지 않아 더 위험한 경우도 있습니다. RFC 8259는 객체 안의 이름이 고유해야 한다(SHOULD)고 권고만 하므로 {"a": 1, "a": 2}도 문법상 유효하고, JSON.parse는 마지막 값만 남깁니다(결과 {"a":2}). 설정 파일을 병합하다 생긴 중복 키를 찾고 싶다면 YAML ↔ JSON 탭에서 방향을 'YAML → JSON'으로 고정하고 넣어 보세요. YAML은 중복 키를 허용하지 않아 duplicated mapping key 오류와 위치를 알려 줍니다.
JSON.parse가 64비트 ID를 조용히 바꾸는 이유 — 2⁵³ 한계
RFC 8259는 숫자의 범위·정밀도를 구현체 재량에 맡기되, 호환성을 위해 IEEE 754 배정밀도(double)가 정확히 표현하는 범위를 기준으로 삼으라고 안내합니다. JavaScript는 모든 숫자를 double로 다루기 때문에 정수는 2⁵³−1 = 9,007,199,254,740,991(Number.MAX_SAFE_INTEGER)까지만 정확합니다. 트위터(X) ID나 스노우플레이크 방식 주문번호처럼 64비트 정수 ID가 이 한계를 넘으면, JSON.parse는 오류 한 줄 없이 끝자리를 반올림해 버립니다. 본 도구도 브라우저 JSON.parse를 쓰므로, 큰 정수가 든 JSON을 정렬·압축하면 같은 방식으로 바뀐 값이 출력됩니다.
# 실제로 깨지는 예 (브라우저·Node.js 공통, 이 페이지 빌드 때 실행한 결과)
JSON.parse('{"id": 1234567890123456789}').id
// → 1234567890123456800
JSON.parse('9007199254740993')
// → 9007199254740992대처는 세 가지입니다. ① 서버가 ID를 문자열로 직렬화 — 트위터 API v1.1이 숫자 id와 별도로 문자열 id_str을 함께 내려주고, v2부터는 ID를 아예 문자열로만 주는 이유가 바로 이것입니다. ② json-bigint 같은 대체 파서 — 큰 정수를 BigInt로 보존하며 파싱합니다. ③ BigInt를 직접 쓸 때는 반대 방향을 주의하세요 — JSON.stringify는 BigInt를 직렬화하지 못하고 TypeError를 던집니다(아래 표). 참고로 Python의 int는 임의 정밀도라 같은 JSON도 값이 온전히 유지되고, jq 1.7은 큰 정수를 계산 없이 통과시키면(jq .) 자릿수를 그대로 유지합니다. "Python·jq에선 맞는데 JS에서만 ID가 다르다"면 십중팔구 이 문제입니다.
JSON.stringify가 조용히 바꾸거나 지우는 값
JSON을 만드는쪽에서도 데이터가 소리 없이 달라집니다. JavaScript 값 중 JSON에 대응하는 타입이 없는 것은 JSON.stringify가 빼 버리거나 다른 값으로 바꾸기 때문입니다. '보낸 필드가 서버에 없다', '숫자가 null로 왔다' 같은 증상은 대개 여기서 시작합니다. '결과' 열은 이 페이지를 빌드할 때 실제로 실행한 출력입니다.
| JSON.stringify( … ) | 결과 | 무슨 일이 일어났나 |
|---|---|---|
| { a: undefined, b: 1 } | {"b":1} | 값이 undefined인 키는 통째로 빠진다 — 받는 쪽에는 "필드 없음"으로 보인다 |
| [undefined, () => 1] | [null,null] | 배열에서는 자리를 지키려고 null이 된다 |
| { f: () => 1, s: Symbol() } | {} | 함수·Symbol 값도 키째 빠진다 |
| [NaN, Infinity, -0] | [null,null,0] | NaN·±Infinity는 null, −0은 0 |
| new Date(Date.UTC(2026, 8, 26)) | "2026-09-26T00:00:00.000Z" | toJSON()이 불려 UTC 기준 ISO 문자열 — 받는 쪽에서 다시 Date로 바꿔야 한다 |
| new Map([["a", 1]]) | {} | Map·Set은 {} — Object.fromEntries(map)이나 [...set]으로 바꿔서 직렬화 |
| BigInt(10) | TypeError: Do not know how to serialize a BigInt | 오류로 멈춘다 — 문자열로 바꾸거나 BigInt.prototype.toJSON을 정의 |
| undefined | undefined (문자열이 아님) | 최상위가 undefined면 문자열이 아니라 undefined — 그대로 보내면 본문이 비어 받는 쪽이 "Unexpected end of JSON input" |
필드를 빼지 않고 "값 없음"을 명시하고 싶다면 undefined 대신 null을 넣으세요. 날짜는 ISO 8601 문자열로 주고받는 것이 가장 무난하지만, 받는 쪽 JSON.parse는 이 문자열을 Date로 되돌려 주지 않으므로 reviver 함수나 스키마 라이브러리로 직접 변환해야 합니다.
JSON vs JSON5 vs JSONC
설정 파일에서 "JSON인데 주석이 된다"면 대개 JSONC나 JSON5입니다. 주석이나 확장 문법이 들어 있으면 순수 JSON 파서(본 도구 포함)에서 오류가 나므로, API로 보내거나 다른 프로그램에 넘기기 전에 표준 JSON으로 바꿔야 합니다.
| 기능 | JSON (RFC 8259) | JSON5 | JSONC |
|---|---|---|---|
| 주석 (// · /* */) | 불가 | 허용 | 허용 |
| 마지막 쉼표 | 불가 | 허용 | 파서마다 다름 (tsconfig·VS Code 설정 파일은 허용) |
| 작은따옴표 문자열 | 불가 | 허용 | 불가 |
| 따옴표 없는 키 | 불가 | 허용 (식별자 형태) | 불가 |
| 16진수·Infinity·NaN | 불가 | 허용 | 불가 |
| 규격 | IETF RFC 8259 · ECMA-404 | JSON5 명세(spec.json5.org) | 공식 표준 없음 (jsonc.org 초안 · VS Code 구현이 사실상 기준) |
| 주요 사용처 | API·데이터 교환 | 사람이 쓰는 설정 파일 | tsconfig.json·VS Code 설정 |
JSON 정렬(Beautify) vs 압축(Minify) — gzip을 켜면 차이는?
정렬은 들여쓰기·줄바꿈을 넣어 사람이 읽기 좋게, 압축은 의미 없는 공백을 모두 지워 한 줄로 만듭니다. 파싱하면 같은 데이터이고, 정렬 탭의 크기 비교는 입력을 UTF-8 바이트로 셉니다. 가이드 예시 {"name":"Alice","age":30,"address":{"city":"Seoul"}}은 압축 52B, 2칸 정렬 74B, 4칸 정렬 86B입니다. 그런데 웹에서는 응답을 gzip·brotli로 한 번 더 압축해 보내는 경우가 많아, 실제 전송량 차이는 이보다 훨씬 작습니다.
| 데이터 | 정렬(2칸) | 압축 | 압축 효과 | gzip 후 정렬 | gzip 후 압축 | gzip 후 차이 |
|---|---|---|---|---|---|---|
| package.json 예시 | 524B | 401B | −23% | 242B | 225B | −7% |
| K8s Deployment 예시 (YAML → JSON) | 564B | 277B | −51% | 243B | 192B | −21% |
| OpenAPI 3.0 예시 (YAML → JSON) | 1,433B | 691B | −52% | 453B | 362B | −20% |
| 사용자 50건 배열 (id·name·email·active·roles) | 6,792B | 4,391B | −35% | 549B | 497B | −9% |
이 페이지를 빌드할 때 Node.js zlib 기본 수준(6)으로 계산. 앞의 세 줄은 YAML ↔ JSON 탭 예시 데이터(package.json은 그대로, K8s·OpenAPI는 도구와 같은 함수로 YAML → JSON 변환)이고, 사용자 50건 배열은 크기 비교용 예시 데이터입니다.
압축만 하면 크기가 23~52% 줄지만, gzip을 거친 뒤에는 정렬본과의 차이가 7~21%로 좁혀집니다. 들여쓰기 공백은 같은 패턴이 반복돼 gzip이 대부분 압축해 버리기 때문입니다. 그래서 전송량을 줄이려면 ① 서버·CDN의 gzip·brotli 전송 압축이 켜져 있는지부터 확인하고(응답 헤더 Content-Encoding) ② 압축(Minify)은 그 위에 더하는 작은 이득으로 보면 됩니다. 반대로 로그·설정 파일처럼 사람이 열어 볼 파일은 정렬해 두는 편이 diff·리뷰에 유리합니다.
JSON → TypeScript 인터페이스 자동 생성
변환 탭의 TypeScript 인터페이스는 JSON 한 건을 보고 타입을 추론합니다. 아래 두 결과는 이 페이지를 빌드할 때 도구와 같은 함수로 만든 출력 그대로입니다. 첫 번째는 기본형, 두 번째는 추론이 한계에 부딪히는 경우를 일부러 모은 예시입니다.
# 입력
{"name":"Alice","age":30,"address":{"city":"Seoul"}}
# 출력
interface Address {
city: string
}
interface Root {
address: Address
age: number
name: string
}# 입력
{"user-id":7,"response":{"ok":true},"items":[{"id":1},{"id":2,"memo":"x"}],"deletedAt":null,"tags":[]}
# 출력
interface ItemsItem {
id: number
}
interface ResponseData {
ok: boolean
}
interface Root {
deletedAt: null // 샘플 값이 null — 실제 타입 | null 로 바꾸세요
items: ItemsItem[]
response: ResponseData
tags: unknown[]
"user-id": number
}- 배열은 첫 번째 원소만 봅니다 — 두 번째 원소에만 있는
memo가ItemsItem에서 빠진 이유입니다. 원소마다 모양이 다르면memo?: string처럼 직접 보완하세요. - null과 빈 배열은 실제 타입을 알 수 없어 각각
null(주석 표시)·unknown[]으로 둡니다. 응답 여러 건을 보고string | null,string[]처럼 고치세요. - 전역 타입과 겹치는 이름 —
response키가ResponseData가 된 것처럼,Response·Date·Event·Map등 TypeScript·DOM 전역 타입과 같은 이름에는Type(이미 전역에 있으면Data) 접미사를 붙여 충돌을 피합니다. - 식별자로 못 쓰는 키(
user-id, 숫자로 시작하는 키)는"user-id"처럼 따옴표로 감쌉니다. 키 순서는 코드 단위 순(대문자가 소문자보다 앞)입니다. - 같은 구조는 재사용 — 키와 값 타입이 모두 같은 객체만 한 인터페이스로 합칩니다. 키 이름만 같고 타입이 다르면
Address2처럼 번호가 붙은 별도 인터페이스가 됩니다.
생성된 타입은 컴파일 시점의 약속일 뿐 런타임에 응답을 검사하지 않습니다. 외부 API처럼 모양이 바뀔 수 있는 데이터라면 스키마 검증을 따로 두세요.
변환 탭 — YAML·CSV·키 정렬·이스케이프가 하는 일
| 모드 | 하는 일 | 입력 → 출력 (빌드 시 실행) |
|---|---|---|
| YAML | JSON → YAML 한 방향. 들여쓰기 2칸, 긴 줄 자동 줄바꿈 없음. yes·on·날짜형·3.10처럼 YAML에서 타입이 바뀔 문자열은 따옴표로 감쌉니다. | 아래 YAML 값 표 참고 |
| CSV (배열만) | 객체 배열을 표로. 헤더는 모든 객체 키의 합집합(처음 나온 순서), 중첩 값은 JSON 문자열, null은 빈 칸. 쉼표·큰따옴표·줄바꿈이 든 칸은 큰따옴표로 감싸고 안의 "는 ""로(RFC 4180 방식). | [{"name":"kim","tags":["a","b"]},{"name":"lee, j","memo":"say \"hi\""}] → name,tags,memo kim,"[""a"",""b""]", "lee, j",,"say ""hi""" |
| 키 알파벳 정렬 | 모든 깊이의 객체 키를 코드 단위 순으로 정렬. 배열 원소 순서는 데이터이므로 그대로 둡니다. 두 JSON을 diff하기 전 정규화용(jq -S와 거의 같은 효과). 단, "2"·"10"처럼 정수 형태인 키는 JavaScript 객체 규칙상 항상 숫자 순으로 맨 앞에 옵니다(jq -S는 문자 순이라 "10"이 "2"보다 앞). | — |
| 문자열 이스케이프 | 압축한 JSON 전체를 한 번 더 JSON 문자열로 감쌉니다. 코드 속 문자열 리터럴, 환경 변수, 다른 JSON의 필드 값으로 넣을 때. | {"msg":"say \"hi\""} → "{\"msg\":\"say \\\"hi\\\"\"}" |
| 이스케이프 해제 | 입력이 JSON 문자열이면 한 번 풀고, 그 안이 다시 JSON이면 선택한 들여쓰기로 정렬합니다. 로그에 문자열로 찍힌 JSON을 읽을 때. | "{\"a\":1}" → { "a": 1 } |
변환 탭의 YAML은 JSON → YAML 한 방향입니다. YAML을 JSON으로 되돌리거나 YAML 문법을 검증하려면 마지막 YAML ↔ JSON 탭을 쓰세요 — 두 형식의 차이와 변환 때 사라지는 정보는 아래 YAML ↔ JSON 섹션에 정리했습니다. TypeScript 인터페이스는 위 절을 참고하세요.
통계·크기 표시 읽는 법
유효한 JSON을 넣으면 입력칸 아래에 네 가지 수치가 뜹니다. 가이드 예시 {"name":"Alice","age":30,"address":{"city":"Seoul"}}(2칸 정렬 상태로 입력)을 기준으로 계산하면 다음과 같습니다.
| 항목 | 세는 방법 | 예시 값 |
|---|---|---|
| 키 개수 | 모든 객체의 키를 중첩까지 합산. 배열 원소는 키로 세지 않습니다. | 4 |
| 객체 / 배열 | 최상위를 포함한 객체·배열의 개수 | 2 / 0 |
| 최대 깊이 | 최상위 값을 0으로 두고 객체·배열 안으로 들어갈 때마다 +1. YAML ↔ JSON 탭의 '중첩 깊이'와 같은 기준입니다. | 2 |
| 크기 | 입력 텍스트의 UTF-8 바이트. 영문·숫자·공백은 1바이트, 한글 1자는 3바이트입니다. | 74B |
| 압축 절약률 | (원본 − 압축) ÷ 원본. 정렬·압축 탭에만 표시하며, 원본이 이미 한 줄이라 줄어들 게 없으면 표시하지 않습니다. | 29.7% |
'원본 입력'은 붙여 넣은 텍스트 그대로의 크기이고 '정렬'은 선택한 들여쓰기로 다시 만든 크기입니다. 4칸으로 들여쓴 JSON을 붙여 넣고 2칸을 고르면 정렬 결과가 원본보다 작게 나오는 것이 정상입니다. 깊이가 예상보다 크면 트리 뷰어 탭에서 ▾로 접어 가며 어느 가지가 깊은지 찾으면 됩니다.
한글 이스케이프 — JS와 Python의 기본값이 반대입니다
같은 "한"이라도 직렬화 결과는 언어마다 다릅니다. JavaScript의 JSON.stringify는 한글을 그대로 내보내지만, Python의 json.dumps는 기본 옵션 ensure_ascii=True 때문에\ud55c 형태로 이스케이프합니다. 두 표기는 표준상 완전히 동등해서 어떤 파서든 같은 문자열로 읽지만, 파일 크기와 사람이 읽을 수 있는지가 달라집니다.
| 항목 | JS JSON.stringify | Python json.dumps |
|---|---|---|
| '한' 직렬화 결과 | "한" (그대로) | "\ud55c" (이스케이프) |
| 동작 바꾸는 옵션 | 없음 (항상 그대로) | ensure_ascii=False |
| '한' 1자 크기(UTF-8) | 3바이트 | 6바이트 (2배) |
| 파싱 결과 | 동일한 '한' | 동일한 '한' |
실무 팁: Python 로그에서 복사한 \uXXXX 덩어리도 유효한 JSON이므로, 본 도구에 붙여넣고 정렬(Beautify)만 해도 원래 한글로 표시됩니다. 반대로 JSON을 JS 코드 문자열 안에 임베드할 때는 변환 탭의 '문자열 이스케이프'를, \uXXXX를 풀 때는 '이스케이프 해제'를 쓰면 됩니다. 한글 비중이 큰 데이터는 이스케이프 시 글자당 3바이트 → 6바이트로 커지므로, 전송·저장용이라면 Python 쪽에서 ensure_ascii=False로 끄는 편이 이득입니다.
jq 실전 치트시트 — 화면에서 확인, CLI에서 반복
구조 파악은 본 도구의 트리 뷰가 빠르지만, 같은 처리를 스크립트·파이프라인에서 반복할 땐 jq가 표준입니다. 아래는 예시 JSON {"users":[{"name":"kim","age":32},{"name":"lee","age":25},{"name":"park","age":41}]} 기준, jq 1.7 실행 결과로 확인한 자주 쓰는 필터입니다.
| 명령 | 용도 | 출력 |
|---|---|---|
| jq '.users[].name' | 특정 키만 추출 | "kim" "lee" "park" |
| jq -r '.users[0].name' | 따옴표 없이 원시 출력 | kim |
| jq '.users[] | select(.age >= 30)' | 조건으로 필터 | kim·park 객체만 |
| jq '.users | map(.name)' | 배열로 재구성 | ["kim","lee","park"] |
| jq '.users | sort_by(.age)' | 값 기준 정렬 | 나이 오름차순 배열 (lee·kim·park) |
| jq '.users | length' | 개수 세기 | 3 |
| jq 'keys' | 최상위 키 목록 | ["users"] |
| jq -c . | 압축(Minify) | 한 줄 JSON |
| jq -S . | 키 재귀 정렬 | 본 도구 '키 정렬'과 거의 같은 정규화(정수 형태 키 순서만 다름) |
가장 요긴한 조합은 키 정렬 diff입니다.-S가 모든 키를 재귀적으로 알파벳 정렬해 주므로(본 도구의 변환 → 키 정렬과 거의 같은 정규화 — "2"·"10" 같은 정수 형태 키의 순서만 다름), 키 순서만 다른 두 JSON은 diff가 비어 있게 됩니다.
# 두 JSON을 의미 기준으로 비교 (키 순서 차이 무시) diff <(jq -S . a.json) <(jq -S . b.json)
YAML ↔ JSON 탭 — 양방향 변환·검증·예시 12개
마지막 YAML ↔ JSON 탭은 설정 파일용 작업 공간입니다. 하위 탭 세 개로 나뉩니다.
- 변환 — 왼쪽에 YAML/JSON 붙여넣기 → 오른쪽 자동 변환. 형식 자동 감지 + 방향(자동·YAML → JSON·JSON → YAML)·들여쓰기(2·4칸·Tab)·JSON Pretty/Compact·키 알파벳 정렬 옵션. 입력이 바뀌면 200ms 뒤 다시 계산합니다.
- 검증 — 단일 입력 + 형식 자동 감지 + 유효성 + 통계(줄·키·깊이·문자·바이트·실행 시간) + 최상위 5개 항목 구조 미리보기. 유효하면 '변환 탭으로 보내기'로 바로 넘깁니다.
- 예시 12개 — Kubernetes(Pod·Deployment·ConfigMap·Helm values)·Docker Compose·GitHub Actions·GitLab CI·Spring(application.yml·멀티 프로파일)·OpenAPI 3.0·package.json·tsconfig.json. 누르면 변환 탭에 바로 적용됩니다.
이 탭의 입력·옵션은 이 브라우저에 자동 저장되고, 결과는 복사하거나 convert-YYYYMMDD.json/.yaml 파일로 저장할 수 있습니다. YAML을 읽을 때는 YAML 1.2 코어 스키마 규칙(js-yaml의 JSON_SCHEMA + 병합 키 <<)을 쓰므로 yes/no·날짜형 값이 문자열로 남습니다.
자주 쓰는 변환 시나리오
- Spring application.yml → JSON — 외부 시스템 연동·설정 백업·환경별 비교
- K8s 매니페스트 → JSON — jq로 필드를 골라내거나 JSON만 받는 스크립트·API에 넘길 때
- GitHub Actions YAML 검증 — 워크플로 푸시 전 들여쓰기·구문 오류 사전 발견
- OpenAPI YAML → JSON — 일부 SDK 생성 도구는 JSON만 지원
- Helm values.yaml → JSON — 외부 시크릿 매니저(Vault·AWS Secrets Manager) 연동
- Docker Compose 검증 — 멀티 서비스 정의 들여쓰기·키 오타 확인
- 운영 설정 ↔ 백업 — JSON 백업·아카이브 → YAML 편집 후 다시 적용
YAML vs JSON 비교
두 형식은 같은 데이터 구조(객체·배열·문자열·숫자·boolean·null)를 표현하지만 문법이 크게 다릅니다.
| 항목 | YAML | JSON |
|---|---|---|
| 주석 | # 지원 | 미지원 |
| 들여쓰기 | 의미 있음 (구조를 결정) | 무의미 (가독성용) |
| 따옴표 | 선택적 | 문자열 필수 (") |
| 키 표기 | "" 없이 가능 | 반드시 "key" |
| 마지막 쉼표 | 블록 스타일엔 쉼표가 없음 · 플로 스타일 [a, b, ]은 허용 | 금지 (오류) |
| 데이터 타입 | 따옴표 없는 값을 자동 추론 (아래 표의 함정) | 문법으로 명시 |
| 중복 키 | 오류 | 문법상 허용 (JSON.parse는 마지막 값) |
| 멀티 도큐먼트 | --- 구분 | 1 도큐먼트만 |
| 앵커/별칭 | 지원 (& *) | 미지원 |
| 스펙 복잡도 | 높음 | 단순 |
| 주 사용처 | DevOps·CI·설정 | API·웹 데이터 |
일반 룰: 사람이 자주 편집하는 설정 파일(K8s·CI·Spring) → YAML, API/웹 데이터 교환 → JSON.
따옴표 없이 쓰면 값이 바뀌는 YAML — 1.1과 1.2 규칙 비교
YAML은 따옴표 없는 값의 타입을 모양으로 추측합니다. 문제는 그 규칙이 버전마다 다르다는 점입니다. 2009년에 나온 YAML 1.2는 JSON과 맞추려고 규칙을 줄였지만, PyYAML(Ansible 등)처럼 1.1 규칙을 따르는 파서가 여전히 많이 쓰입니다. 같은 파일이 도구마다 다르게 읽히는 이유입니다. '본 도구' 열은 YAML ↔ JSON 탭이 실제로 읽은 값(JSON 표기), 'PyYAML' 열은 PyYAML 6.0의 yaml.safe_load 결과(Python 표기)입니다.
| YAML 값 | 본 도구 (1.2 규칙) | PyYAML (1.1 규칙) | 문자열로 두려면 | 메모 |
|---|---|---|---|---|
| v: yes | "yes" | True | v: "yes" | YAML 1.1은 yes·no·on·off를 boolean으로 정의 |
| v: NO | "NO" | False | v: "NO" | 국가 코드 NO(노르웨이)가 false가 되는 이른바 Norway problem |
| v: on | "on" | True | v: "on" | GitHub Actions의 on: 키도 PyYAML로 읽으면 True 키가 된다 |
| v: 0755 | 755 | 493 | v: "0755" | YAML 1.1은 0으로 시작하면 8진수. 1.2는 8진수를 0o755로만 쓴다 |
| v: 3.10 | 3.1 | 3.1 | v: "3.10" | 1.1·1.2 모두 실수 — 끝의 0이 사라진다(python-version: 3.10 → 3.1) |
| v: 22:22 | "22:22" | 1342 | v: "22:22" | YAML 1.1의 60진수. Compose 포트 매핑을 따옴표로 쓰는 이유 |
| v: 2026-09-26 | "2026-09-26" | datetime.date(2026, 9, 26) | v: "2026-09-26" | 1.1 로더는 날짜 객체로 바꾼다 |
| v: ~ | null | None | v: "~" | ~·null·빈 값은 모두 null |
| v: .inf | null | inf | v: ".inf" | JSON에는 무한대가 없어 JSON으로 옮기면 null |
'문자열로 두려면' 열은 YAML ↔ JSON 탭에서 JSON → YAML로 변환했을 때의 출력입니다. 변환 탭의 YAML 모드는 같은 값을 작은따옴표('yes')로 감쌉니다.
가장 안전한 습관은 숫자·boolean으로 쓸 게 아니면 따옴표로 감싸는 것입니다. 특히 버전 번호(3.10), 파일 권한(0755), 포트 매핑(22:22), 국가·언어 코드(노르웨이 NO·노르웨이어 no), 앞자리 0이 있는 코드(007)는 어느 파서를 쓰든 따옴표가 정답입니다. 본 도구로 JSON → YAML 변환하면 이런 문자열을 자동으로 따옴표로 감싸므로, Ansible(PyYAML)·Spring Boot(SnakeYAML)처럼 1.1 규칙으로 읽는 곳에 넘겨도 값이 바뀌지 않습니다.
YAML → JSON 변환 시 손실되는 정보
- 주석 (#) — JSON은 주석을 지원하지 않으므로 모두 삭제. 역변환 시 복원 불가
- 앵커 (&) / 별칭 (*) — 펼쳐져 데이터 중복으로 변환됨. 참조 관계 손실. 병합 키
<<: *defaults도 값이 합쳐진 형태로 펼쳐짐 - 멀티 도큐먼트 (---) — JSON 배열로 통합 변환 (구분 정보 손실)
- 태그 (!·!!) —
!Ref(CloudFormation)·!!python/object같은 커스텀 태그와, JSON에 대응 타입이 없는!!timestamp·!!binary는 해석하지 않고 변환 오류로 표시.!!str 123처럼 JSON 타입으로 바꾸는 표준 태그는 적용됨 - JSON에 없는 값 —
.inf·.nan은 JSON에 대응 값이 없어null로 바뀜. 날짜처럼 보이는2026-09-26은 문자열 그대로 유지 - 따옴표 없는 값의 해석 — 본 도구는 1.2 규칙이라
0755를 10진수 755로,3.10을 3.1로 읽습니다(위 표). 원래 표기를 살려야 하면 원본 YAML에서 따옴표로 감싸세요
YAML ↔ JSON 탭은 주석·앵커/별칭·멀티 도큐먼트를 변환 결과 아래 경고로 알려 주고, 해석할 수 없는 태그는 변환 오류로 표시합니다. 원본 보존이 필요하면 변환하지 말고 원본 형식 그대로 쓰세요.
흔한 YAML 오류 — 도구가 내는 메시지와 원인
아래 메시지는 YAML ↔ JSON 탭이 쓰는 js-yaml이 각 입력에 실제로 낸 오류의 첫 줄입니다. 괄호 안 숫자는 (줄:열)로, 파서가 더 읽지 못하고 멈춘 곳이라 실제 원인보다 뒤(때로는 입력 끝 다음 줄)를 가리킬 수 있습니다. 마지막 두 줄처럼 오류 없이 값만 바뀌는 경우가 더 위험하니, 변환 결과를 꼭 눈으로 확인하세요.
| 원인 | 잘못된 YAML | 도구 결과 | 고치는 법 |
|---|---|---|---|
| 탭 들여쓰기 | a: ⇥b: 1 | tab characters must not be used in indentation (2:1) | 들여쓰기는 공백만. 에디터에서 탭을 공백으로 바꾸는 설정을 켠다 |
| 콜론 뒤 공백 누락 | a: 1 b:2 | can not read a block mapping entry; a multiline key may not be an implicit key (3:1) | b: 2처럼 띄운다. b:2는 키가 아니라 문자열 한 덩어리라, 파일에 이 줄 하나뿐이면 오류 없이 문자열 "b:2"가 된다 |
| 들여쓰기 불일치 | a: b: 1 c: 2 | bad indentation of a mapping entry (3:5) | 같은 레벨은 같은 칸 수로 |
| 값 안의 ": " | title: 주의: 공백 | bad indentation of a mapping entry (1:10) | 값 전체를 따옴표로 — title: "주의: 공백" |
| 중복 키 | k: v k: w | duplicated mapping key (2:1) | 하나로 합친다. PyYAML 등 일부 파서는 오류 없이 마지막 값으로 덮어써 더 찾기 어렵다 |
| 커스텀 태그 | x: !Ref Foo | unknown tag !<!Ref> (1:12) | CloudFormation이면 Ref: Foo 같은 긴 형식으로. 본 도구는 커스텀 태그를 해석하지 않는다 |
| 정의 안 된 별칭 | x: *foo | unidentified alias "foo" (1:8) | 앵커 &foo를 먼저 정의하거나, 문자열이면 "*foo"처럼 따옴표 |
| 닫히지 않은 따옴표 | key: "unclosed | unexpected end of the stream within a double quoted scalar (2:1) | 따옴표 짝을 맞춘다 |
| 공백 뒤 # | msg: hello #world | 오류 없음 → {"msg":"hello"} | " #" 뒤는 주석이라 값이 잘린다. 값에 #이 들어가면 따옴표 |
| 리스트 들여쓰기 어긋남 | items: - a - b | 오류 없음 → {"items":["a - b"]} | "- "의 칸을 맞춘다. 한 칸 밀린 줄은 앞 항목 값의 연속으로 읽힌다 |
⇥는 탭 문자를 표시한 것입니다.
자주 묻는 질문
JSON에 주석을 쓰면 왜 오류가 나나요?
# 주석은 모두 사라지고, 다시 YAML로 되돌려도 복원되지 않습니다. YAML ↔ JSON 탭은 입력에서 주석을 발견하면 경고로 알려 줍니다.API 응답 JSON에서 TypeScript 타입을 자동 생성할 수 있나요?
null인 필드는 실제 타입을 알 수 없어 null로 두고 주석을 다니 string | null처럼 고쳐 쓰세요. ③ 옵셔널 여부(?)는 JSON 한 건으로 판단할 수 없습니다. 키는 코드 단위 순(대문자가 소문자보다 앞)으로 정렬되고, Response·Date처럼 전역 타입과 겹치는 이름에는 Type·Data 접미사가 붙습니다.JSON Beautify(정렬)와 Minify(압축) 차이는?
JSON 파싱 오류 위치를 어떻게 찾나요?
at position N(0부터 센 문자 위치)이나 line N column N 형식으로 위치가 들어 있습니다. 본 도구는 이를 줄·열로 바꿔 해당 줄의 텍스트와 ^ 표시로 보여 주고, 메시지에 위치 정보가 없으면 위치 표시는 생략합니다. 표시된 위치는 오류가 발견된 곳이라 원인은 그 바로 앞에 있는 경우가 많습니다(예: 마지막 쉼표는 다음 } 위치에서 발견). 메시지별 원인은 가이드의 파싱 오류 표를 참고하세요.두 JSON의 차이점을 비교하는 방법은?
jq -S(키 정렬) + diff 조합이 효율적입니다. 배열은 순서 자체가 데이터라 정렬하지 않으므로, 원소 순서만 다른 배열은 여전히 차이로 나옵니다.Unexpected token '<' … is not valid JSON 오류는 왜 나나요?
JSON.parse나 fetch의 response.json()에 JSON이 아닌 HTML이 들어왔다는 뜻입니다. 첫 글자 <는 대개 <!DOCTYPE html>의 시작으로, API 주소 오타(404 페이지), 서버 오류(500 페이지), 로그인 만료로 인한 로그인 페이지 리다이렉트, 개발 서버의 프록시 설정 누락이 흔한 원인입니다. 개발자 도구 Network 탭에서 그 요청의 상태 코드와 Content-Type(JSON이면 application/json)을 먼저 확인하고, 코드에서는 response.ok를 검사한 뒤에 .json()을 부르세요. 비슷한 Unexpected end of JSON input은 본문이 비어 있을 때(204 No Content, 빈 응답) 납니다.YAML과 JSON 어떤 차이가 있나요?
• YAML: 들여쓰기 기반·주석 지원·앵커/별칭·멀티 도큐먼트 — 사람이 편집하는 설정 파일에 적합 (K8s·CI·Spring)
• JSON: 중괄호·대괄호 기반·문자열 필수 따옴표·주석 미지원 — 기계가 처리하는 API·데이터 교환에 표준
YAML 1.2는 JSON 문서를 그대로 읽을 수 있도록(사실상 상위 집합) 설계됐지만 예외가 있습니다. 대표적으로 중복 키는 JSON.parse가 마지막 값으로 통과시키는 반면 YAML 파서는 오류로 거부합니다. 반대로 YAML 문서 대부분은 JSON이 아닙니다.
YAML 들여쓰기는 탭 vs 공백?
tab characters must not be used in indentation 오류와 줄·열 위치를 보여 줍니다.• 관례: 2 spaces (DevOps·K8s·Docker), 4 spaces (Java/Spring 일부)
• 한 파일 내 통일 필수 — 2/4 혼용은 들여쓰기 오류
에디터 설정: VS Code
"editor.insertSpaces": true, "editor.tabSize": 2 · IntelliJ Settings → Editor → Code Style → YAML → Tabs and IndentsYAML 출력은 들여쓰기 옵션에서 Tab을 골라도 공백 2칸으로 나옵니다(Tab은 JSON 출력에만 적용).
K8s 매니페스트의 ---는 무엇인가요?
[{...pod}, {...service}]), 멀티 도큐먼트를 발견하면 경고로 안내합니다.yes/no가 자동으로 boolean이 되는데 어떻게?
yes·no·on·off(대소문자 변형 포함)를 boolean으로 정의했고, PyYAML처럼 1.1을 따르는 파서가 여전히 널리 쓰입니다. 잘 알려진 예가 국가 코드 NO(노르웨이)가 false가 되는 이른바 Norway problem이고, Docker Compose 문서가 environment의 yes·no·true·false 값을 따옴표로 감싸라고 안내하는 것도 같은 이유입니다.해결: 문자열로 쓸 값은 따옴표로 감싸세요 —
country: "NO". YAML 1.2 코어 스키마에서는 true·false(True·TRUE 같은 대소문자 변형 포함)만 boolean입니다.본 도구는 YAML 1.2 규칙으로 읽어 yes/no를 문자열로 유지하고, JSON → YAML 변환 때는
"yes" 같은 문자열을 따옴표로 감싸 YAML 1.1 로더에서도 문자열로 남게 합니다. 가이드의 YAML 값 표에 두 규칙의 차이를 정리했습니다.앵커(&)와 별칭(*)이 무엇인가요?
defaults: &defaults 아래에 timeout: 30, retries: 3 같은 공통값을 두고, prod: 항목에서 <<: *defaults로 끌어와 재사용합니다. JSON 변환 시에는 앵커가 펼쳐져 모든 곳에 데이터가 복사됩니다(참조 관계 손실). 역변환 시 자동 앵커 생성은 끔(noRefs: true)으로 두어 깔끔한 YAML 출력을 보장합니다.YAML → JSON 변환 결과가 원본보다 길어요
name → "name"), 중괄호·대괄호, 콤마 구분자가 붙고 들여쓰기도 유지되기 때문입니다. YAML ↔ JSON 탭의 YAML 예시 10개를 2칸 Pretty JSON으로 바꾸면 원본의 약 1.3~1.9배가 되고, Compact(한 줄)로 바꾸면 원본 YAML 대비 −22%~+3%로 비슷하거나 더 작아집니다. 반대로 JSON → YAML은 괄호·따옴표가 빠져 대개 짧아집니다(예시 package.json −30%, tsconfig.json −18%). 파일 크기가 중요하면 옵션의 JSON 출력: Compact를 쓰세요.입력한 JSON·YAML이 서버로 전송되나요?
Blob URL로 브라우저 안에서 만듭니다. JSON 탭 입력은 저장하지 않고, YAML ↔ JSON 탭의 입력·옵션만 재방문 편의를 위해 이 브라우저 localStorage에 저장합니다. 공용 PC에서 K8s 시크릿·DB 비밀번호 등을 다뤘다면 개발자 도구 → Application → Local Storage에서 youtil_yaml_json_v1 키를 지우세요.참고 자료
- RFC 8259 JSON 데이터 교환 형식(새 창)rfc-editor.org
- ECMA-404 JSON 문법(새 창)ecma-international.org
- YAML 1.2.2 명세(새 창)yaml.org
- MDN JSON.stringify()(새 창)developer.mozilla.org
- RFC 4180 CSV 형식(새 창)rfc-editor.org
- 계산 기준
- JSON 문법 RFC 8259·ECMA-404 · YAML 1.2.2(코어 스키마) · 브라우저 JSON.parse/stringify · YAML 처리 js-yaml 4 · CSV 따옴표 규칙 RFC 4180
- 오류 제보
- 결과가 다르면 contact@youtil.kr로 입력 조건을 알려 주세요. 평일 기준 1~3일 안에 답변합니다.