본문 바로가기
C.W.K.
Stream
Lesson 03 of 06 · published

JSON — 문자열과 파일을 오가는 네 함수

~20 min · json, serialization, encoding

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

dumps·loads와 dump·load

끝의 s가 있는 함수는 문자열을, 없는 함수는 파일 객체를 다뤄. JSON이 바로 표현하는 값은 객체·배열·문자열·숫자·참거짓·null이며 Python에서는 dict·list·str·숫자·bool·None에 대응해.

모든 Python 타입이 돌아오는 건 아니야

튜플은 배열로 바뀌어 튜플이라는 정보가 사라지고, set·datetime·Path·사용자 클래스는 직접 바꿔야 해. default 함수로 표현 가능한 값으로 변환할 수 있지만 다시 읽을 때의 복원 계약도 따로 정해야 해.

사람용과 기계용 출력을 구분해

indent, sort_keys, ensure_ascii, separators로 읽기 쉬움과 크기·안정성을 조절해. JSON은 부동소수점의 오차를 고치지 않으므로 통화 Decimal은 문자열처럼 명시적인 형태로 저장해.

Code

JSON을 다루는 네 함수·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))
읽기 쉽고 안정적인 JSON 출력·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))

# 컴팩트 (추가 공백 없음)
print(json.dumps(obj, separators=(",", ":")))
큰 자료에는 줄 단위 또는 전용 파서·python
# json은 streaming 방식이 아님 — 파일 통째 메모리에 읽음
# 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

datetime, Path, set이 들어올 수 있는 딕셔너리를 JSON 파일에 쓰는 save_record와 다시 읽는 load_record를 만들어. 세 타입을 처리하는 default 함수를 쓰고, 단순 타입은 왕복 보존되지만 세 타입의 자동 복원은 별도 계약이라는 한계를 문서화해.

Progress

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

댓글 0

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

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