JSON 입문: 문법, 데이터 타입, 오류 고치는 법
2026년 11월 업데이트
JSON(JavaScript Object Notation)은 구조화된 데이터를 담는 텍스트 형식입니다. 처음에는 JavaScript 객체 문법의 일부로 시작했지만, 지금은 거의 모든 프로그래밍 언어가 JSON을 읽고 쓰며 웹 API, 많은 설정 파일, 수많은 데이터 내보내기의 기본 형식이 되었습니다. 규칙은 한 페이지에 들어갈 만큼 짧지만, 쉼표 하나가 더 붙거나 따옴표 종류가 틀리는 작은 실수 하나로 문서 전체를 프로그램이 읽지 못하게 됩니다. 이 글에서는 규칙과 데이터 타입, 가장 자주 만나는 오류와 고치는 법을 설명합니다.
JSON은 이렇게 생겼습니다
JSON 문서는 값 하나입니다. 보통 그 값은 중괄호로 감싼 객체나 대괄호로 감싼 배열이고, 그 안에 다른 값들이 들어갑니다.
{"name": "김민아", "age": 31, "languages": ["한국어", "영어"], "manager": null}
규칙은 간단합니다.
- 객체는
{ }안에 이름–값 쌍을 담습니다. 이름(키)은 큰따옴표로 감싼 문자열이고, 그 뒤에 콜론과 값이 옵니다. - 배열은
[ ]안에 값의 목록을 순서대로 담습니다. 한 배열 안에 서로 다른 타입의 값이 섞여도 됩니다. - 항목은 쉼표로 구분하며, 마지막 항목 뒤에는 쉼표를 붙이지 않습니다.
- 문자열에는 큰따옴표만 씁니다. 문자열 안의 큰따옴표는
\", 역슬래시는\\, 줄바꿈은\n으로 씁니다. - 항목 사이의 공백, 탭, 줄바꿈은 무시되므로 같은 데이터를 한 줄로 써도, 여러 줄로 나눠 써도 됩니다.
- 주석, 변수, 함수는 없습니다. JSON은 오직 데이터만 담습니다.
JSON은 서로 같은 내용을 담은 두 표준, ECMA-404와 RFC 8259로 정의되어 있습니다. RFC 8259는 시스템 사이에 주고받는 JSON을 UTF-8로 인코딩하도록 정하고 있어서, 한국어, 日本語, 이모지 같은 문자도 그대로 넣을 수 있습니다.
여섯 가지 데이터 타입
| 타입 | 예 | 규칙 |
|---|---|---|
| 문자열 | "서울", "caf\u00e9" | 큰따옴표만 사용, \u 이스케이프 가능 |
| 숫자 | 42, -3.5, 1.2e6 | 앞자리 0, 앞의 +, 16진수, NaN, Infinity 불가 |
| 불리언 | true, false | 소문자만 |
| 널 | null | “값 없음”을 뜻함, 소문자만 |
| 객체 | {"id": 7} | 키는 문자열이며 중복되지 않아야 함 |
| 배열 | [1, "two", false] | 순서가 있고 타입을 섞을 수 있음 |
빠진 것도 눈여겨보세요. 날짜 타입이 없습니다. 날짜는 보통 "2026-11-05T14:30:00Z" 같은 ISO 8601 문자열이나 유닉스 타임스탬프로 저장하는데, 둘 다 유닉스 시간 가이드에서 설명합니다. 정수 타입도 따로 없고, JavaScript를 비롯한 많은 파서는 모든 숫자를 64비트 부동소수점으로 저장합니다. 그래서 9,007,199,254,740,991보다 큰 정수는 소리 없이 바뀔 수 있습니다. JavaScript는 {"id": 9007199254740993}을 9007199254740992로 읽습니다. 큰 ID를 문자열로 주고받는 경우가 많은 이유입니다.
JavaScript의 JSON.stringify는 JSON으로 나타낼 수 없는 값도 조용히 바꿉니다. 값이 undefined인 속성은 빠지고, NaN과 Infinity는 null이 됩니다.
자주 나는 오류와 고치는 법
JSON 파서는 엄격해서 실수가 하나만 있어도 문서 전체를 거부합니다. 오류 메시지가 위치를 알려 주긴 하지만, 실제 문제보다 조금 뒤를 가리키는 경우가 많습니다. 예를 들어 끝에 남은 쉼표는 그 뒤에 오는 닫는 괄호 위치에서 오류로 보고됩니다. 가장 자주 만나는 오류는 다음과 같습니다.
| 문제 | 잘못된 예 | 고친 예 |
|---|---|---|
| 끝에 남은 쉼표 | {"a": 1, "b": 2,} | {"a": 1, "b": 2} |
| 작은따옴표 | {'name': '민아'} | {"name": "민아"} |
| 따옴표 없는 키 | {name: "민아"} | {"name": "민아"} |
| 주석 | {"port": 8080 // 개발용} | {"port": 8080} |
| 빠진 쉼표 | [1, 2 3] | [1, 2, 3] |
| Python식 값 | {"ok": True, "x": None} | {"ok": true, "x": null} |
| 앞자리 0 | {"code": 007} | {"code": "007"} |
| 이스케이프하지 않은 역슬래시 | {"path": "C:\Users\mina"} | {"path": "C:\\Users\\mina"} |
| 문자열 안에서 줄바꿈 | 따옴표 안에서 Enter를 누름 | "첫 줄\n둘째 줄" |
| JavaScript 전용 값 | {"x": undefined} | {"x": null} |
원인이 뚜렷한 오류도 있습니다. 작은따옴표나 True, None이 보인다면 대개 누군가 Python 딕셔너리를 json.dumps로 변환하지 않고 그냥 출력한 것입니다. 겉보기에는 JSON 같지만 JSON이 아닙니다. 주석과 끝에 남은 쉼표는 JSONC나 JSON5로 쓴 설정 파일에서 오는 경우가 많습니다. VS Code의 settings.json이나 TypeScript의 tsconfig.json처럼 일부 도구가 받아들이는 느슨한 변형이지만, 표준 파서는 둘 다 거부합니다. 순수한 JSON 안에 메모를 남기고 싶다면 "_comment" 같은 일반 필드를 추가하세요.
실수를 가장 빨리 찾는 방법은 문서를 JSON 포매터에 붙여 넣는 것입니다. 입력하는 동안 검사해서 첫 번째 오류의 줄과 열을 보여 주고, 포맷을 누르면 그 위치로 이동합니다.
보기 좋게 정렬하기와 압축하기
항목 사이의 공백은 무시되므로 같은 데이터를 두 가지 스타일로 쓸 수 있습니다. 정렬(pretty-print)한 JSON은 값마다 줄을 바꾸고 안쪽 값을 들여 써서 구조가 한눈에 보입니다. 압축(minify)한 JSON은 필요 없는 공백과 줄바꿈을 모두 없앱니다. {"id": 1042, "name": "박민아", "roles": ["editor", "admin"]}라는 데이터는 압축하면 57바이트, 공백 2칸으로 들여 쓰면 83바이트이지만, 담긴 데이터는 똑같습니다.
| 정렬 | 압축 | |
|---|---|---|
| 알맞은 용도 | 읽기, 디버깅, 설정 파일, 코드 리뷰 | API 응답, 저장, 다른 텍스트 안에 넣기 |
| 크기 | 더 큼. 깊게 중첩될수록 들여쓰기가 쌓임 | 데이터가 허락하는 만큼 작음 |
| 버전 관리 | 바뀐 부분이 줄 단위로 보임 | 한 줄이라 조금만 바뀌어도 전체가 바뀜 |
네트워크에서는 차이가 보기보다 작습니다. 서버가 응답을 gzip이나 Brotli로 압축하는 경우가 많고, 반복되는 공백은 아주 잘 압축되기 때문입니다. 저장하고 보낼 때는 압축한 JSON을 쓰고, 사람이 읽어야 할 때 정렬하는 습관이 실용적입니다.
JSON과 CSV 비교
CSV(쉼표로 구분한 값)는 특히 스프레드시트와 데이터를 주고받을 때 많이 쓰는 또 하나의 형식입니다. 같은 기록 두 개를 각각의 형식으로 써 보면, JSON으로는 [{"name": "민아", "city": "부산", "score": 92}, {"name": "Leo", "city": "Lyon", "score": 87}]이고, CSV로는 머리글 줄 name,city,score 다음에 기록마다 한 줄씩 민아,부산,92와 Leo,Lyon,87이 옵니다.
| JSON | CSV | |
|---|---|---|
| 구조 | 중첩된 객체와 배열 | 행과 열로 된 평평한 표 |
| 타입 | 문자열, 숫자, 불리언, null | 모두 텍스트, 읽는 쪽이 추측 |
| 필드 이름 | 객체마다 반복 | 머리글 행에 한 번 |
| 표 형태 데이터의 크기 | 더 큼 | 더 작음 |
| 여는 프로그램 | 코드 편집기, 브라우저, API | 엑셀, 구글 스프레드시트, 데이터베이스 |
간단한 기준은 이렇습니다. 데이터가 스프레드시트에 자연스럽게 들어가고 사람들이 엑셀로 열어 볼 것이라면 CSV를 쓰세요. 여러 주소를 가진 고객처럼 중첩된 부분이 있거나, 타입이 중요하거나, 프로그램끼리 주고받는 데이터라면 JSON을 쓰세요. CSV의 약점도 조심해야 합니다. 쉼표가 들어간 값은 따옴표로 감싸야 하고, 스프레드시트 앱은 02134 같은 우편번호에서 앞의 0을 지워 버릴 수 있습니다. CSV ⇄ JSON 변환기는 양방향 모두 따옴표를 처리하고, 중첩된 값은 셀 하나에 JSON 텍스트로 넣습니다.
JSON을 만나는 곳
- 웹 API. 대부분의 REST API는
Content-Type: application/json헤더를 붙여 JSON을 주고받습니다. 브라우저 개발자 도구의 네트워크 탭에서 이런 응답을 볼 수 있습니다. - 설정 파일. JavaScript 프로젝트의
package.json, PHP의composer.json, 많은 앱의 설정 파일이 JSON입니다. - 로그인 토큰. JSON 웹 토큰(JWT)은 작은 JSON 객체를 Base64url로 인코딩해 담습니다. 토큰이 거의 항상
eyJ로 시작하는 것도 그 때문인데,{"뒤에 영문자가 오는 부분을 인코딩하면 이렇게 됩니다. JWT 디코더로 토큰을 다시 읽을 수 있는 JSON으로 바꿀 수 있습니다. - 로그와 내보내기. JSON Lines 파일(
.jsonl)은 한 줄에 JSON 객체 하나를 담기 때문에, 아주 큰 파일도 한 줄씩 처리할 수 있습니다. - 데이터베이스와 브라우저. PostgreSQL에는
json과jsonb열 타입이 있고, 웹 앱은 설정을 JSON 문자열로 바꿔 로컬 저장소에 보관하는 경우가 많습니다.
코드에서 JSON 읽고 쓰기
| 언어 | 텍스트 → 데이터 | 데이터 → 텍스트 |
|---|---|---|
| JavaScript | JSON.parse(text) | JSON.stringify(data, null, 2) |
| Python | json.loads(text) | json.dumps(data, indent=2, ensure_ascii=False) |
| PHP | json_decode($text, true) | json_encode($data, JSON_PRETTY_PRINT) |
Python에서 ensure_ascii=False를 주면 “서울” 같은 한글이 \uc11c\uc6b8로 바뀌지 않고 그대로 보입니다. 또 Python의 json 모듈은 기본적으로 NaN과 Infinity를 읽고 쓰는데, 다른 파서는 이를 거부합니다. 결과를 다른 시스템으로 보낼 때는 json.dumps에 allow_nan=False를 넘기세요.
관련 도구
- JSON 포매터 – JSON을 검사하고, 보기 좋게 정렬하거나 압축하고, 오류가 난 줄을 찾습니다.
- CSV ⇄ JSON 변환기 – 스프레드시트 데이터를 JSON으로, JSON을 다시 CSV로 바꿉니다.
- JWT 디코더 – JSON 웹 토큰 안에 든 JSON을 읽습니다.