C.W.K.
Stream
Lesson 03 of 06 · published

JSON — load / dump / 진짜 함정

~20 min · json, serialization, encoding

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

99% JSON 커버하는 4 함수

json.dumps(obj) 가 문자열로 직렬화. json.loads(s) 가 문자열 파싱. json.dump(obj, file) 가 파일에 쓰기. json.load(file) 가 파일에서 읽기. 두 페어가 문자열 다루는지 파일 다루는지만 차이. 짜는 모든 JSON 처리 코드가 이 넷 중 하나.

직렬화 가능한 거 + 안 되는 거

JSON 이 native 처리 — dict, list, str, int, float, bool, None. tuple 은 list 됨 (조용히 — round-trip 에 tuple-ness 잃음). set, datetime, Path, dataclass, 커스텀 클래스 — 다 도움 없이 X. json.dumps({1, 2, 3})TypeError. 해결 — 직렬화 전 변환 또는 커스텀 default.

default 키워드 — 미지원 타입 처리

json.dumps(obj, default=fn)json 이 native 처리 못 하는 모든 값에 fn 호출. *처리할 수 있는 거* (dict, list, str) 반환하면 사용. 표준 패턴 — default=str 가 date 와 Path 같은 거에 문자열 표현 fallback, round-tripping 비대칭 비용.

알아둘 4 추가 인자

indent=2 가 pretty-print. sort_keys=True 가 키 알파벳 순 — diff 가능 출력에 유용. ensure_ascii=False 가 non-ASCII 글자를 escape 안 하고 그대로 (사람 읽기 좋음, 옛 환경엔 나쁨). separators=(',', ':') 가 컴팩트 출력 (추가 공백 X).

War Story: JSON 의 숫자 타입은 double-precision float. 1.1 round-trip 살아남지만 0.1 + 0.2 직렬화하고 다시 파싱하면 여전히 0.30000000000000004. JSON 이 정확한 십진 수학 할 거라 기대 X. 통화엔 Decimal + 문자열로 직렬화.

Code

4 함수·python
import json

obj = {"name": "pippa", "age": 4, "vessels": ["claude", "codex", "gemini", "ollama"]}

# 문자열로
s = json.dumps(obj)
print(s)

# 문자열 파싱
back = json.loads(s)
print(back)

# 파일 to/from
with open("/tmp/pippa.json", "w", encoding="utf-8") as f:
    json.dump(obj, f)

with open("/tmp/pippa.json", encoding="utf-8") as f:
    loaded = json.load(f)
print(loaded == obj)         # True
직렬화 *불가* — 처리법·python
import json
from datetime import datetime, date
from pathlib import Path
from decimal import Decimal

obj = {
    "set": {1, 2, 3},                 # set
    "date": date(2026, 5, 2),         # date
    "path": Path("/tmp/x"),           # Path
    "money": Decimal("1.99"),         # Decimal
}

# 이거 raise
try:
    json.dumps(obj)
except TypeError as e:
    print("raised:", e)

# 해결 1 — dump 전 수동 변환
cleaned = {
    "set": list(obj["set"]),
    "date": obj["date"].isoformat(),
    "path": str(obj["path"]),
    "money": str(obj["money"]),
}
print(json.dumps(cleaned))

# 해결 2 — default=str (round-trip 비대칭)
print(json.dumps(obj, default=str))
커스텀 default — 타입별 적절한 처리·python
import json
from datetime import datetime, date
from pathlib import Path

def serialize(obj):
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    if isinstance(obj, Path):
        return str(obj)
    if isinstance(obj, set):
        return sorted(obj)               # set 을 정렬된 list
    raise TypeError(f"직렬화 불가: {type(obj).__name__}")

obj = {
    "created": datetime.now(),
    "path": Path("/tmp/x"),
    "tags": {"urgent", "review"},
}
print(json.dumps(obj, default=serialize, indent=2, ensure_ascii=False))
Pretty-print + 안정 출력·python
import json

obj = {"b": 2, "a": 1, "items": [3, 1, 2], "name": "피파"}

# 디폴트 — 컴팩트
print(json.dumps(obj))

# Pretty
print(json.dumps(obj, indent=2))

# 안정 — sort + non-ascii passthrough
print(json.dumps(obj, indent=2, sort_keys=True, ensure_ascii=False))

# 컴팩트 (추가 공백 X)
print(json.dumps(obj, separators=(",", ":")))
Streaming JSON — 큰 파일은 streaming 파서·python
# json 은 streaming X — 파일 통째 메모리에 읽음
# 1GB JSON 파일엔 ijson 같은 streaming 파서 필요

# 파일이 객체 list (newline-delimited JSON / JSONL) 면 줄 단위 stream-처리:
import json

lines = ['{"id": 1}', '{"id": 2}', '{"id": 3}']

for line in lines:
    obj = json.loads(line)            # 한 번에 한 레코드
    print(obj)

# JSONL = cwkPippa 가 세션 로그에 쓰는 거 — 줄당 하나의 JSON 객체.
# Append-only 쉬움, stream-read 쉬움.

External links

Exercise

함수 save_record(record, path)datetime, Path, set 포함 가능한 dict 받고 JSON 파일로 쓰기. 그 셋 처리하는 커스텀 default 함수. 그 다음 load_record(path) 가 레코드 다시 읽기. 단순 타입 round-trip 완벽 작동 확인. set / datetime 은 완전 round-trip 원하면 수동 재구성 필요 — 한계로 받아들이고 문서화.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.