JSON과 파이썬은 같은 모양을 씁니다
JSON은 API, 설정 파일, 그리고 웹에서 데이터를 주고받을 때 사실상의 표준 포맷입니다. 다행히도 파이썬 개발자에게는 반가운 소식이 있는데요, JSON 객체는 파이썬의 dict에, JSON 배열은 list에, JSON 문자열은 str에 그대로 대응됩니다. 이렇게 구조가 딱 맞아떨어지기 때문에 파이썬에서 JSON을 읽고 쓰는 일은 단 두 줄이면 끝납니다.
표준 라이브러리 모듈인 json이 양방향 변환을 모두 처리해 줍니다.
JSON 문자열 파싱하기
json.loads(text)는 "load string"의 줄임말로, JSON 형식의 문자열을 받아 그에 해당하는 파이썬 객체로 변환해 줍니다:
결과는 평범한 dict입니다. JSON 전용 래퍼 객체나 .parse() 같은 메서드 없이, 일반 딕셔너리처럼 키로 바로 접근하면 됩니다.
문자열이 올바른 JSON 형식이 아니라면 json.loads는 json.JSONDecodeError를 던집니다. 에러 메시지에 문제가 발생한 줄과 열 번호가 찍혀 나오기 때문에, 빠진 쉼표나 이스케이프되지 않은 따옴표를 찾는 데 보통 이 정보만으로도 충분합니다.
JSON을 문자열로 직렬화하기
json.dumps(data) — 이름 그대로 "dump string" — 는 반대 방향으로 동작합니다. 파이썬 객체를 받아 JSON 문자열로 돌려주죠:
파이썬의 True, False, None은 JSON에서 각각 true, false, null로 자동 변환됩니다. 숫자와 문자열은 그대로 전달되고, 리스트는 배열로, 딕셔너리는 객체로 바뀝니다.
JSON 예쁘게 출력하기 (Pretty Print)
json.dumps의 기본 출력은 공백 없이 한 줄로 나와서 네트워크로 주고받기엔 좋지만 사람이 읽기엔 불편합니다. 좀 더 읽기 좋게 만들려면 indent=2를 넘겨 주면 됩니다:
알아두면 좋은 dumps 옵션 몇 가지를 더 소개한다:
sort_keys=True— 객체의 키를 알파벳순으로 정렬한다. 출력을 일관되게 유지하고 싶을 때(설정 파일, 테스트 픽스처, diff 비교 등) 유용하다.ensure_ascii=False— 한글이나 é, ü, 中 같은 비 ASCII 문자를\u이스케이프가 아닌 원래 글자 그대로 기록한다. UTF-8 파일을 다룰 때는 대체로 이 옵션이 정답이다.separators=(",", ":")— 출력을 최대한 압축한다.ensure_ascii=False와 함께 쓰면 가장 작은 크기의 UTF-8 JSON을 만들 수 있다.
파일에서 JSON 읽기
json.load(file) — 끝에 s가 없다 — 는 파일 객체에서 바로 읽어 들인다. 보통은 with open과 함께 쓰는 패턴이 가장 흔하다:
import json
with open("config.json") as f:
config = json.load(f)
print(config["version"])
파일 전체를 문자열로 먼저 읽을 필요는 없습니다. json.load가 파일 객체를 스트리밍하면서 읽어주니까요.
JSON을 파일로 저장하기
json.dump(data, file)은 파일로 쓸 때 짝을 이루는 함수입니다:
import json
data = {"created": "2026-01-01", "items": [1, 2, 3]}
with open("state.json", "w") as f:
json.dump(data, f, indent=2)
indent 옵션은 여기서도 그대로 쓸 수 있어요. 저장된 파일을 열어 보면 보기 좋게 정렬된 JSON 문서를 확인할 수 있습니다.
JSON API 응답 파싱하기
대부분의 HTTP 라이브러리는 응답을 바이트나 문자열 형태로 돌려주는데요, 이걸 실제로 다룰 수 있는 데이터로 바꾸려면 json.loads를 호출하면 됩니다:
import json
import urllib.request
with urllib.request.urlopen("https://api.example.com/users/1") as response:
text = response.read().decode("utf-8")
user = json.loads(text)
print(user["name"])
requests 라이브러리(별도 문서에서 다룹니다)는 이 과정을 한 단계 줄여줍니다. 응답 객체에 .json() 메서드가 있어서 내부적으로 json.loads를 대신 호출해 주거든요.
JSON이 표현할 수 있는 것과 없는 것
JSON의 타입 시스템은 Python보다 좁습니다. 양방향 매핑을 정리하면 이렇습니다:
| Python | JSON |
|---|---|
dict | object |
list, tuple | array |
str | string |
int, float | number |
True | true |
False | false |
None | null |
튜플은 왕복 변환 과정에서 리스트로 바뀝니다. 튜플이라는 정보 자체가 사라지는 거죠. 그리고 set, 사용자 정의 클래스, datetime 객체는 기본적으로 직렬화되지 않습니다. 시도하면 TypeError: Object of type X is not JSON serializable 에러를 만나게 됩니다.
datetime과 커스텀 객체 다루기
흔히 쓰는 방법은 두 가지입니다.
먼저 JSON에 안전한 구조로 변환하기. 직렬화 전에 직접 코드에서 변환을 처리한 뒤, 평범한 dict를 넘기는 방식입니다:
default= 함수를 넘겨주는 방법입니다. json.dumps는 직렬화할 수 없는 값을 만날 때마다 이 함수를 호출합니다:
데이터를 다시 읽어올 때는 ISO 문자열을 직접 datetime으로 되돌려야 합니다. JSON은 원래 타입이 무엇이었는지 기억해 주지 않거든요.
딕셔너리를 JSON으로 왕복 변환하기
딕셔너리가 제대로 살아남는지 간단히 확인해 봅시다:
값은 같지만 서로 다른 객체입니다. 보통은 이게 우리가 원하는 동작이죠. 사실 json.dumps + json.loads 조합은 JSON으로 표현 가능한 데이터라면 어떤 구조든 손쉽게 깊은 복사(deep copy)를 뜨는 꼼수로도 자주 쓰입니다.
실전 예제
JSON 설정 파일을 불러와서 값을 수정한 뒤 다시 저장하는 간단한 스크립트를 살펴봅시다:
import json
from pathlib import Path
config_path = Path("settings.json")
# Load (with a default if the file doesn't exist).
if config_path.exists():
config = json.loads(config_path.read_text())
else:
config = {"theme": "dark", "last_opened": None}
# Update.
config["last_opened"] = "2026-01-15"
# Save, pretty-printed.
config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False))
"읽기 → 수정 → 쓰기"로 이어지는 JSON 처리 사이클 전체를 겨우 열두 줄 남짓으로 끝낸 셈입니다.
몇 가지 습관
- 파일을 열 땐 항상
with open(...)을 쓰세요. JSON도 결국 텍스트 파일이니, 파일 다루는 기본 규칙이 그대로 적용됩니다. - 사람이 직접 들여다볼 파일에는
indent=2를 붙이세요 — 설정 파일, 픽스처, 내보낸 데이터 같은 경우죠. 반대로 네트워크로 주고받는 데이터는 용량이 중요하니 빼는 게 낫습니다. - UTF-8로 저장할 땐
ensure_ascii=False를 켜세요. 그래야 한글이나 악센트가 들어간 이름이 깨지지 않고 그대로 보입니다. - 직접 만들지 않은 데이터를 파싱할 땐
try/except json.JSONDecodeError로 감싸세요.
다음 편 예고
JSON은 키-값 형태의 데이터에 잘 맞습니다. 같은 챕터의 다음 도구는 CSV인데요, 스프레드시트에서 내보낸 데이터 대부분이 이 표 형식이고, 파이썬은 이를 위한 기능을 표준 라이브러리로 지원합니다.
자주 묻는 질문
파이썬에서 JSON은 어떻게 파싱하나요?
JSON 문자열이라면 json.loads(text), 파일 객체라면 json.load(file)을 쓰면 됩니다. 둘 다 파이썬 dict(또는 JSON 구조에 따라 list)를 돌려줍니다. 예를 들어 data = json.loads('{"name": "Rosa"}')로 받으면 data['name']은 'Rosa'가 됩니다.
파이썬 딕셔너리를 JSON으로 어떻게 변환하나요?
json.dumps(my_dict)를 호출하면 JSON 문자열이 반환됩니다. 파일로 바로 쓰고 싶다면 json.dump(my_dict, file)을 사용하세요. 보기 좋게 출력하고 싶을 때는 indent 옵션을 줘서 json.dumps(data, indent=2)처럼 쓰면 됩니다.
json.loads와 json.load의 차이는 뭔가요?
끝에 s가 붙은 loads는 문자열을, s가 없는 load는 파일 객체를 받습니다. dumps와 dump도 똑같은 규칙이에요. s를 string의 약자로 기억하면 헷갈릴 일이 없습니다.
날짜나 커스텀 객체는 JSON으로 어떻게 처리하나요?
JSON에는 날짜 타입이 따로 없기 때문에 보통 ISO-8601 형식의 문자열로 저장한 뒤 읽을 때 직접 파싱합니다. 커스텀 클래스의 경우 json.dumps에 default= 함수를 넘겨서 JSON으로 변환 가능한 형태를 반환하게 하거나, 직렬화하기 전에 직접 dict로 변환해 주면 됩니다.