Alert
์ด ๊ธ์ Claude Code์ ๋์์ ๋ฐ์ ์์ฑ๋์์ต๋๋ค
TL;DR
- ์ง๋ ฌํ(Serialization)๋ ๋ฉ๋ชจ๋ฆฌ์ ์๋ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๊ฑฐ๋ ์ ์กํ ์ ์๋ ํํ๋ก ๋ณํํ๋ ๊ฒ
- JSON์ ์ฌ๋์ด ์ฝ์ ์ ์๊ณ ์ธ์ด ๊ฐ ํธํ์ด ๋๋ ํ ์คํธ ํฌ๋งท, pickle์ Python ์ ์ฉ ๋ฐ์ด๋๋ฆฌ ํฌ๋งท
- ์ธ๋ถ ์์คํ ๊ณผ ํต์ ํ๋ฉด JSON, Python ๋ด๋ถ ์ ์ฅ์ด๋ฉด pickle, ๋๊ท๋ชจ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ์ด๋ฉด Avro/Protobuf
Sources
1. ์ง๋ ฌํ๋
์ ํ์ํ๊ฐ
Python์์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๋ฉด ๋ฉ๋ชจ๋ฆฌ์ ์กด์ฌํ๋ค. ๊ทธ๋ฐ๋ฐ ์ด ๊ฐ์ฒด๋ฅผ ํ์ผ์ ์ ์ฅํ๊ฑฐ๋, ๋คํธ์ํฌ๋ก ๋ณด๋ด๊ฑฐ๋, ๋ค๋ฅธ ํ๋ก์ธ์ค์ ์ ๋ฌํ๋ ค๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋ฐ๊นฅ์ผ๋ก ๊บผ๋ด์ผ ํ๋ค. ๋ฌธ์ ๋ ๋ฉ๋ชจ๋ฆฌ ์ ๊ฐ์ฒด๋ ํฌ์ธํฐ, ์ฐธ์กฐ, ๋ด๋ถ ๊ตฌ์กฐ๊ฐ ๋ค์์ฌ ์์ด์ ๊ทธ ์ํ ๊ทธ๋๋ก๋ ์ ์ฅํ๊ฑฐ๋ ์ ์กํ ์ ์๋ค๋ ๊ฒ์ด๋ค.
์ง๋ ฌํ(Serialization) ๋ ์ด ๋ฉ๋ชจ๋ฆฌ ์ ๊ฐ์ฒด๋ฅผ ์ฐ์๋ ๋ฐ์ดํธ ๋๋ ๋ฌธ์์ด๋ก ๋ณํํ๋ ๊ณผ์ ์ด๋ค. ๋ฐ๋๋ก ๋ฐ์ดํธ/๋ฌธ์์ด์์ ๊ฐ์ฒด๋ฅผ ๋ณต์ํ๋ ๊ฒ์ ์ญ์ง๋ ฌํ(Deserialization) ๋ผ๊ณ ํ๋ค.
๋ฉ๋ชจ๋ฆฌ ์ ๊ฐ์ฒด โ [์ง๋ ฌํ] โ ๋ฐ์ดํธ/๋ฌธ์์ด โ [์ญ์ง๋ ฌํ] โ ๋ฉ๋ชจ๋ฆฌ ์ ๊ฐ์ฒด
์ง๋ ฌํ์ ๋ค๋ฅธ ์ด๋ฆ๋ค
๊ฐ์ ๊ฐ๋ ์ ๋ฌธ๋งฅ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ๋ถ๋ฅธ๋ค.
- Serialization / Deserialization โ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ํํ
- Marshalling / Unmarshalling โ RPC, ๋คํธ์ํฌ ํต์ ์์ ์ฃผ๋ก ์ฌ์ฉ
- Pickling / Unpickling โ Python ๊ณ ์ ํํ
- Encoding / Decoding โ JSON ๋ฑ ํ ์คํธ ํฌ๋งท์์ ์ฃผ๋ก ์ฌ์ฉ
์ง๋ ฌํ๊ฐ ์์ผ๋ฉด ์ด๋ป๊ฒ ๋๋
user = {"name": "alice", "age": 30, "scores": [95, 87, 92]}
# ํ์ผ์ ์ ์ฅํ๊ณ ์ถ๋ค๋ฉด?
with open("user.txt", "w") as f:
f.write(str(user)) # "{'name': 'alice', 'age': 30, 'scores': [95, 87, 92]}"
# ๋ค์ ์ฝ์ผ๋ฉด?
with open("user.txt", "r") as f:
data = f.read()
print(type(data)) # <class 'str'> โ dict๊ฐ ์๋๋ผ ๋ฌธ์์ด์ด๋ค
# eval(data)๋ก ๋ณต์ํ ์๋ ์์ง๋ง ๋ณด์์ ์ ๋ ํ๋ฉด ์ ๋๋คstr()๋ก ๋ณํํ๋ฉด ํํ๋ง ๋น์ทํ ๋ฟ ํ์
์ ๋ณด๊ฐ ์ฌ๋ผ์ง๋ค. ์ง๋ ฌํ๋ ์ด ๋ฌธ์ ๋ฅผ ํ์
๊ณผ ๊ตฌ์กฐ๋ฅผ ๋ณด์กดํ๋ฉด์ ํด๊ฒฐํ๋ค.
2. ์ง๋ ฌํ๊ฐ ์ฐ์ด๋ ๊ณณ
์ง๋ ฌํ๋ ๋ฐ์ดํฐ๊ฐ ๋ฉ๋ชจ๋ฆฌ ๋ฐ๊นฅ์ผ๋ก ๋๊ฐ๋ ๊ฑฐ์ ๋ชจ๋ ๊ณณ์์ ์ฐ์ธ๋ค.
| ์ํฉ | ์ง๋ ฌํ ๋์ | ์ฃผ๋ก ์ฐ๋ ํฌ๋งท |
|---|---|---|
| REST API ์์ฒญ/์๋ต | dict, ๋ฆฌ์คํธ โ HTTP body | JSON |
| ํ์ผ ์ ์ฅ (์ค์ , ์ํ) | ๊ฐ์ฒด โ ํ์ผ | JSON, YAML, pickle |
| ๋ฉ์์ง ํ (Kafka, RabbitMQ) | ๋ฉ์์ง ๊ฐ์ฒด โ ๋ฐ์ดํธ | JSON, Avro, Protobuf |
| ์บ์ (Redis, Memcached) | ๊ฐ์ฒด โ ๋ฐ์ดํธ | pickle, JSON, msgpack |
| DB ์ ์ฅ (BLOB, JSON ์ปฌ๋ผ) | ๊ฐ์ฒด โ ๋ฐ์ดํธ/๋ฌธ์์ด | JSON, pickle |
| RPC (gRPC, Thrift) | ํจ์ ์ธ์/๋ฐํ๊ฐ โ ๋ฐ์ดํธ | Protobuf, Thrift |
| ํ๋ก์ธ์ค ๊ฐ ํต์ (IPC) | ๊ฐ์ฒด โ ๋ฐ์ดํธ | pickle |
| ML ๋ชจ๋ธ ์ ์ฅ | ํ์ต๋ ๋ชจ๋ธ โ ํ์ผ | pickle, joblib, ONNX |
DE ๊ด์ ์์์ ์ง๋ ฌํ
๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ์์๋ ์ง๋ ฌํ ํฌ๋งท ์ ํ์ด ์ฑ๋ฅ๊ณผ ํธํ์ฑ์ ์ง๊ฒฐ๋๋ค. Kafka์ JSON์ผ๋ก ๋ฉ์์ง๋ฅผ ๋ฃ์ผ๋ฉด ์ฌ๋์ด ์ฝ๊ธฐ ์ฝ์ง๋ง ๋๋ฆฌ๊ณ ํฌ๋ค. Avro๋ Protobuf๋ฅผ ์ฐ๋ฉด ์คํค๋ง๊ฐ ๊ฐ์ ๋๊ณ ๋ฐ์ด๋๋ฆฌ๋ผ ๋น ๋ฅด์ง๋ง ๋๋ฒ๊น ์ด ์ด๋ ต๋ค.
3. ์ง๋ ฌํ ํฌ๋งท ๋น๊ต
ํ ์คํธ ํฌ๋งท
| ํฌ๋งท | ํน์ง | ์ฅ์ | ๋จ์ |
|---|---|---|---|
| JSON | key-value ๊ตฌ์กฐ, ์น ํ์ค | ์ฌ๋์ด ์ฝ๊ธฐ ์ฌ์, ์ธ์ด ๊ฐ ํธํ | ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ ๋ถ๊ฐ, ๋๋ฆผ |
| XML | ํ๊ทธ ๊ธฐ๋ฐ, ์ํฐํ๋ผ์ด์ฆ | ์คํค๋ง ๊ฒ์ฆ(XSD), ๋ค์์คํ์ด์ค | ์ฅํฉํจ, ํ์ฑ ๋๋ฆผ |
| YAML | ๋ค์ฌ์ฐ๊ธฐ ๊ธฐ๋ฐ | ๊ฐ๋ ์ฑ ์ต๊ณ , ์ค์ ํ์ผ์ ์ ํฉ | ํ์ฑ ๋๋ฆผ, ์คํ์ด ๋ณต์ก |
| CSV | ํ/์ด ๊ตฌ์กฐ | ๋จ์, ์คํ๋ ๋์ํธ ํธํ | ์ค์ฒฉ ๊ตฌ์กฐ ๋ถ๊ฐ, ํ์ ์ ๋ณด ์์ |
๋ฐ์ด๋๋ฆฌ ํฌ๋งท
| ํฌ๋งท | ํน์ง | ์ฅ์ | ๋จ์ |
|---|---|---|---|
| pickle | Python ์ ์ฉ | Python ๊ฐ์ฒด ๊ฑฐ์ ์ ๋ถ ์ง์ | Python์์๋ง ์ฌ์ฉ ๊ฐ๋ฅ, ๋ณด์ ์ทจ์ฝ |
| Protobuf | Google, ์คํค๋ง ํ์ (.proto) | ๋น ๋ฆ, ์ธ์ด ๊ฐ ํธํ, ํ์ ์์ | ์คํค๋ง ์ ์ ํ์, ์ฌ๋์ด ์ฝ๊ธฐ ๋ถ๊ฐ |
| Avro | Apache, ์คํค๋ง ๋ด์ฅ | ์คํค๋ง ์งํ ์ง์, Hadoop ์ํ๊ณ | Java ์ค์ฌ ์ํ๊ณ |
| MessagePack | JSON๊ณผ ์ ์ฌํ ๊ตฌ์กฐ | JSON๋ณด๋ค ๋น ๋ฅด๊ณ ์์ | ์คํค๋ง ์์ |
์ ํ ๊ธฐ์ค
- ์ฌ๋์ด ์ฝ์ด์ผ ํ๋ค โ JSON, YAML
- Python ๋ด๋ถ์์๋ง ์ด๋ค โ pickle
- ์ธ์ด ๊ฐ ํธํ + ์ฑ๋ฅ โ Protobuf, MessagePack
- ๋๊ท๋ชจ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ โ Avro, Protobuf
4. Python json ๋ชจ๋
JSON์ ๊ฐ์ฅ ๋๋ฆฌ ์ฐ์ด๋ ํ
์คํธ ์ง๋ ฌํ ํฌ๋งท์ด๋ค. Python์ json ๋ชจ๋์ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํฌํจ๋์ด ์๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
import json
# ์ง๋ ฌํ (Python โ JSON ๋ฌธ์์ด)
data = {"name": "alice", "age": 30, "scores": [95, 87, 92]}
json_str = json.dumps(data)
print(json_str) # {"name": "alice", "age": 30, "scores": [95, 87, 92]}
# ์ญ์ง๋ ฌํ (JSON ๋ฌธ์์ด โ Python)
restored = json.loads(json_str)
print(restored["name"]) # alice
print(type(restored)) # <class 'dict'>ํ์ผ ์ ์ถ๋ ฅ
# ํ์ผ์ ์ ์ฅ
with open("data.json", "w") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
# ํ์ผ์์ ์ฝ๊ธฐ
with open("data.json", "r") as f:
loaded = json.load(f)dump/load๋ ํ์ผ ๊ฐ์ฒด๋ฅผ ๋ฐ๊ณ , dumps/loads๋ ๋ฌธ์์ด์ ๋ค๋ฃฌ๋ค. s๋ string์ ์ฝ์๋ค.
Python โ JSON ํ์ ๋ณํ ๊ท์น
| Python | JSON | ๋น๊ณ |
|---|---|---|
dict | object | |
list, tuple | array | tuple์ list๋ก ๋ณํ๋จ (๋ณต์ ์ ๊ตฌ๋ถ ๋ถ๊ฐ) |
str | string | |
int, float | number | |
True / False | true / false | |
None | null |
์ด ์ธ์ ํ์
(datetime, set, ์ปค์คํ
ํด๋์ค ๋ฑ)์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง๋ ฌํํ ์ ์๋ค.
from datetime import datetime
json.dumps({"now": datetime.now()})
# TypeError: Object of type datetime is not JSON serializable์ปค์คํ ๊ฐ์ฒด ์ง๋ ฌํ โ default ํ๋ผ๋ฏธํฐ
default ํ๋ผ๋ฏธํฐ์ ๋ณํ ํจ์๋ฅผ ๋๊ธฐ๋ฉด ๊ธฐ๋ณธ ์ง์ํ์ง ์๋ ํ์
๋ ์ง๋ ฌํํ ์ ์๋ค.
from datetime import datetime, date
def json_default(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
if isinstance(obj, set):
return list(obj)
raise TypeError(f"์ง๋ ฌํ ๋ถ๊ฐ: {type(obj)}")
data = {
"created": datetime(2026, 4, 12, 15, 30),
"tags": {"python", "serialization"},
}
json_str = json.dumps(data, default=json_default, ensure_ascii=False)
print(json_str)
# {"created": "2026-04-12T15:30:00", "tags": ["python", "serialization"]}์ปค์คํ ์ญ์ง๋ ฌํ โ object_hook
JSON์ ์ฝ์ ๋ dict๋ฅผ ์ํ๋ ๊ฐ์ฒด๋ก ์๋ ๋ณํํ ์ ์๋ค.
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
def as_user(dct):
if "name" in dct and "age" in dct:
return User(**dct)
return dct
json_str = '{"name": "alice", "age": 30}'
user = json.loads(json_str, object_hook=as_user)
print(user) # User(name='alice', age=30)
print(type(user)) # <class 'User'>pretty-print ์ต์
data = {"users": [{"name": "alice", "age": 30}, {"name": "bob", "age": 25}]}
# ๊ธฐ๋ณธ โ ํ ์ค
json.dumps(data)
# indent๋ก ๋ณด๊ธฐ ์ข๊ฒ
print(json.dumps(data, indent=2, ensure_ascii=False))
# {
# "users": [
# {
# "name": "alice",
# "age": 30
# },
# ...
# ]
# }
# separators๋ก ๊ณต๋ฐฑ ์ ๊ฑฐ (์ ์ก ์ ํฌ๊ธฐ ์ ์ฝ)
json.dumps(data, separators=(",", ":"))
# {"users":[{"name":"alice","age":30},{"name":"bob","age":25}]}5. Python pickle ๋ชจ๋
pickle์ Python ์ ์ฉ ๋ฐ์ด๋๋ฆฌ ์ง๋ ฌํ ํฌ๋งท์ด๋ค. JSON๊ณผ ๋ฌ๋ฆฌ Python ๊ฐ์ฒด๋ฅผ ๊ฑฐ์ ๊ทธ๋๋ก ์ ์ฅํ๊ณ ๋ณต์ํ ์ ์๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
import pickle
data = {"name": "alice", "scores": [95, 87, 92], "active": True}
# ์ง๋ ฌํ (Python โ ๋ฐ์ดํธ)
pickled = pickle.dumps(data)
print(type(pickled)) # <class 'bytes'>
# ์ญ์ง๋ ฌํ (๋ฐ์ดํธ โ Python)
restored = pickle.loads(pickled)
print(restored) # {'name': 'alice', 'scores': [95, 87, 92], 'active': True}ํ์ผ ์ ์ถ๋ ฅ
# ํ์ผ์ ์ ์ฅ (๋ฐ์ด๋๋ฆฌ ๋ชจ๋)
with open("data.pkl", "wb") as f:
pickle.dump(data, f)
# ํ์ผ์์ ์ฝ๊ธฐ
with open("data.pkl", "rb") as f:
loaded = pickle.load(f)pickle์ด JSON๋ณด๋ค ๊ฐ๋ ฅํ ์
pickle์ Python ๊ฐ์ฒด์ ํ์ ๊ณผ ๊ตฌ์กฐ๋ฅผ ๊ทธ๋๋ก ๋ณด์กดํ๋ค.
from datetime import datetime
from collections import defaultdict
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
data = {
"user": User("alice", 30),
"created": datetime(2026, 4, 12),
"counter": defaultdict(int, {"a": 1, "b": 2}),
"numbers": {1, 2, 3},
}
# pickle์ ์ด ๋ชจ๋ ๊ฒ์ ๊ทธ๋๋ก ์ง๋ ฌํ/์ญ์ง๋ ฌํํ ์ ์๋ค
pickled = pickle.dumps(data)
restored = pickle.loads(pickled)
print(type(restored["user"])) # <class 'User'> โ dataclass ๊ทธ๋๋ก
print(type(restored["created"])) # <class 'datetime.datetime'>
print(type(restored["counter"])) # <class 'collections.defaultdict'>
print(type(restored["numbers"])) # <class 'set'>JSON์ด์๋ค๋ฉด User๋ dict๋ก, datetime์ ์๋ฌ๋ก, set์ list๋ก ๋ณํ๋์ ๊ฒ์ด๋ค.
์ง๋ ฌํ ๊ฐ๋ฅํ/๋ถ๊ฐ๋ฅํ ๊ฒ
# โ
์ง๋ ฌํ ๊ฐ๋ฅ
pickle.dumps(42) # ์ซ์
pickle.dumps("hello") # ๋ฌธ์์ด
pickle.dumps([1, 2, 3]) # ๋ฆฌ์คํธ
pickle.dumps({"key": "value"}) # ๋์
๋๋ฆฌ
pickle.dumps(User("alice", 30)) # ํด๋์ค ์ธ์คํด์ค
pickle.dumps(len) # ๋ด์ฅ ํจ์
# โ ์ง๋ ฌํ ๋ถ๊ฐ๋ฅ
pickle.dumps(lambda x: x + 1) # TypeError โ ๋๋ค
pickle.dumps(open("test.txt")) # TypeError โ ํ์ผ ๊ฐ์ฒด
# โ ๏ธ ์ฃผ์: ํจ์/ํด๋์ค๋ "์ด๋ฆ"์ผ๋ก ์ ์ฅ๋๋ค
# ์ญ์ง๋ ฌํํ ๋ ํด๋น ๋ชจ๋์์ import ๊ฐ๋ฅํด์ผ ํ๋ค๋ณด์ ์ฃผ์์ฌํญ
pickle์ ์ ๋ขฐํ ์ ์๋ ๋ฐ์ดํฐ์ ์ฌ์ฉํ๋ฉด ์ ๋๋ค
pickle์ ์ญ์ง๋ ฌํ ๊ณผ์ ์์ ์์ ์ฝ๋๋ฅผ ์คํํ ์ ์๋ค. ์ ์์ ์ผ๋ก ์กฐ์๋ pickle ๋ฐ์ดํฐ๋ฅผ
loads()ํ๋ฉด ์์คํ ๋ช ๋ น์ด ์คํ๋ ์ ์๋ค.# ์ด๋ฐ ๊ณต๊ฒฉ์ด ๊ฐ๋ฅํ๋ค (์คํํ์ง ๋ง ๊ฒ) import pickle malicious = b"cos\nsystem\n(S'rm -rf /'\ntR." pickle.loads(malicious) # os.system('rm -rf /') ์คํ
- ์ธ๋ถ์์ ๋ฐ์ ๋ฐ์ดํฐ๋ ์ ๋ pickle๋ก ์ญ์ง๋ ฌํํ์ง ์๋๋ค
- API ํต์ , ์ฌ์ฉ์ ์ ๋ ฅ ๋ฑ์๋ JSON์ ์ฌ์ฉํ๋ค
- pickle์ ๋ด๊ฐ ๋ง๋ ๋ฐ์ดํฐ๋ฅผ ๋ด๊ฐ ์ฐ๋ ๊ฒฝ์ฐ์๋ง ์์ ํ๋ค
ํ๋กํ ์ฝ ๋ฒ์
pickle์๋ ์ฌ๋ฌ ํ๋กํ ์ฝ ๋ฒ์ ์ด ์๋ค. ๋ฒ์ ์ด ๋์์๋ก ํจ์จ์ ์ด๋ค.
# ์ต์ ํ๋กํ ์ฝ ์ฌ์ฉ (๊ถ์ฅ)
pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
# ๊ธฐ๋ณธ ํ๋กํ ์ฝ ํ์ธ
print(pickle.DEFAULT_PROTOCOL) # 5 (Python 3.14 ๊ธฐ์ค)| ๋ฒ์ | Python | ํน์ง |
|---|---|---|
| 0 | 2.x | ํ ์คํธ ๋ชจ๋, ๋๋ฒ๊น ์ฉ |
| 2 | 2.3+ | new-style ํด๋์ค ์ง์ |
| 4 | 3.4+ | ๋์ฉ๋ ๊ฐ์ฒด ์ง์ |
| 5 | 3.8+ | out-of-band ๋ฒํผ, ํ์ฌ ๊ธฐ๋ณธ๊ฐ |
6. ๋ฐ์ด๋๋ฆฌ ์ง๋ ฌํ โ Avro, Protobuf
JSON์ด๋ pickle๊ณผ ๋ฌ๋ฆฌ, Avro์ Protobuf๋ ์คํค๋ง๋ฅผ ๋จผ์ ์ ์ํ๊ณ ๊ทธ ์คํค๋ง์ ๋ง์ถฐ ์ง๋ ฌํํ๋ค. ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ์์ ๋ฉ์์ง ํฌ๋งท์ ๊ฐ์ ํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ค.
Avro
Apache Avro๋ Hadoop ์ํ๊ณ์์ ํ์ํ ์ง๋ ฌํ ํฌ๋งท์ด๋ค. ์คํค๋ง๊ฐ ๋ฐ์ดํฐ ํ์ผ์ ๋ด์ฅ๋์ด์ ๋ณ๋ ์ฝ๋ ์์ฑ ์์ด ์ฝ๊ณ ์ธ ์ ์๋ค.
pip install avro์คํค๋ง๋ฅผ JSON์ผ๋ก ์ ์ํ๋ค. (user.avsc)
{
"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "age", "type": "int"},
{"name": "email", "type": ["string", "null"]}
]
}import avro.schema
from avro.datafile import DataFileWriter, DataFileReader
from avro.io import DatumWriter, DatumReader
# ์คํค๋ง ๋ก๋
schema = avro.schema.parse(open("user.avsc", "rb").read())
# ์ง๋ ฌํ โ Avro ํ์ผ์ ์ฐ๊ธฐ
writer = DataFileWriter(open("users.avro", "wb"), DatumWriter(), schema)
writer.append({"name": "alice", "age": 30, "email": "alice@example.com"})
writer.append({"name": "bob", "age": 25, "email": None})
writer.close()
# ์ญ์ง๋ ฌํ โ Avro ํ์ผ์์ ์ฝ๊ธฐ
# DatumReader()์ ์คํค๋ง๋ฅผ ๋๊ธฐ์ง ์์๋ ๋๋ค โ ํ์ผ ํค๋์ ์คํค๋ง๊ฐ ๋ค์ด์๊ธฐ ๋๋ฌธ
reader = DataFileReader(open("users.avro", "rb"), DatumReader())
for user in reader:
print(user)
# {'name': 'alice', 'age': 30, 'email': 'alice@example.com'}
# {'name': 'bob', 'age': 25, 'email': None}
reader.close()Avro๋ ์คํค๋ง๋ฅผ ํ์ผ์ ๋ด์ฅํ๋ค
Avro ํ์ผ(
.avro)์ ๊ตฌ์กฐ๋ ์ด๋ ๊ฒ ์๊ฒผ๋ค:[ํ์ผ ํค๋: ์คํค๋ง(JSON) + ๋ฉํ๋ฐ์ดํฐ] [๋ฐ์ดํฐ ๋ธ๋ก 1] [๋ฐ์ดํฐ ๋ธ๋ก 2] ...์ธ ๋ ์คํค๋ง๋ฅผ ์ง์ ํ๋ฉด, ๊ทธ ์คํค๋ง๊ฐ ํ์ผ ํค๋์ ํจ๊ป ์ ์ฅ๋๋ค. ๊ทธ๋์ ์ฝ์ ๋๋ ํ์ผ๋ง ์์ผ๋ฉด ์คํค๋ง๋ฅผ ๊บผ๋ด์ ์ญ์ง๋ ฌํํ ์ ์๋ค. ๋ณ๋์ ์คํค๋ง ํ์ผ(
.avsc)์ด๋ ์์ฑ๋ ์ฝ๋ ์์ด๋ ๋๊ตฌ๋.avroํ์ผ๋ง์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ์ ์๋ค๋ ๋ป์ด๋ค.Protobuf๋ ๋ฐ๋๋ค. ๋ฐ์ด๋๋ฆฌ์ ์คํค๋ง๊ฐ ์์ด์, ์ฝ๋ ์ชฝ๋
.proto๋ก ์์ฑํ ์ฝ๋(user_pb2.py)๋ฅผ ๊ฐ๊ณ ์์ด์ผ ์ญ์ง๋ ฌํํ ์ ์๋ค.
Avro์ ๊ธฐํ ํน์ง
- ์คํค๋ง ์งํ(evolution)๋ฅผ ์ง์ํ๋ค โ ํ๋ ์ถ๊ฐ/์ญ์ ์ ํธํ์ฑ ์ ์ง ๊ฐ๋ฅ
- Kafka + Schema Registry ์กฐํฉ์์ ๋ฉ์์ง ํฌ๋งท์ผ๋ก ๋ง์ด ์ฌ์ฉ๋๋ค
Protobuf (Protocol Buffers)
Google์ด ๋ง๋ ์ง๋ ฌํ ํฌ๋งท์ด๋ค. .proto ํ์ผ๋ก ์คํค๋ง๋ฅผ ์ ์ํ๊ณ , ์ปดํ์ผ๋ฌ(protoc)๋ก Python ์ฝ๋๋ฅผ ์์ฑํด์ ์ฌ์ฉํ๋ค.
pip install protobuf
# protoc ์ปดํ์ผ๋ฌ๋ ๋ณ๋ ์ค์น ํ์ (https://protobuf.dev)์คํค๋ง๋ฅผ .proto ํ์ผ๋ก ์ ์ํ๋ค. (user.proto)
syntax = "proto3";
message User {
string name = 1;
int32 age = 2;
string email = 3;
}Python ์ฝ๋ ์์ฑ:
protoc --python_out=. user.proto
# user_pb2.py ํ์ผ์ด ์์ฑ๋๋คimport user_pb2
# ์ง๋ ฌํ
user = user_pb2.User()
user.name = "alice"
user.age = 30
user.email = "alice@example.com"
binary = user.SerializeToString() # bytes
print(len(binary)) # ~30 bytes (JSON์ด๋ฉด ~60 bytes)
# ์ญ์ง๋ ฌํ
restored = user_pb2.User()
restored.ParseFromString(binary)
print(restored.name) # alice
print(restored.age) # 30JSON ๋ณํ๋ ๊ฐ๋ฅํ๋ค:
from google.protobuf import json_format
# Protobuf โ JSON
json_str = json_format.MessageToJson(user)
print(json_str) # {"name": "alice", "age": 30, "email": "alice@example.com"}
# JSON โ Protobuf
user2 = user_pb2.User()
json_format.Parse(json_str, user2)Protobuf์ ํน์ง
.protoโprotocโ Python ์ฝ๋ ์์ฑ ๊ณผ์ ์ด ํ์ํ๋ค (Avro์์ ํต์ฌ ์ฐจ์ด)- ๋ฐ์ด๋๋ฆฌ ํฌ๊ธฐ๊ฐ JSON ๋๋น ์ ๋ฐ ์ดํ๋ก ์๊ณ , ์ง๋ ฌํ/์ญ์ง๋ ฌํ ์๋๊ฐ ๋น ๋ฅด๋ค
- gRPC์ ๊ธฐ๋ณธ ์ง๋ ฌํ ํฌ๋งท์ด๋ค
Avro vs Protobuf
| ๊ธฐ์ค | Avro | Protobuf |
|---|---|---|
| ์คํค๋ง ์์น | ๋ฐ์ดํฐ ํ์ผ์ ๋ด์ฅ | .proto ํ์ผ ๋ณ๋ ๊ด๋ฆฌ |
| ์ฝ๋ ์์ฑ | ๋ถํ์ (๋์ ์ฒ๋ฆฌ) | ํ์ (protoc ์ปดํ์ผ) |
| ์คํค๋ง ์งํ | ๊ธฐ๋ณธ ์ง์ (reader/writer schema ๋ถ๋ฆฌ) | ์ง์ (ํ๋ ๋ฒํธ ๊ธฐ๋ฐ) |
| ์ฃผ ์ฌ์ฉ์ฒ | Kafka, Hadoop, ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ | gRPC, ๋ง์ดํฌ๋ก์๋น์ค ํต์ |
| ์ํ๊ณ | Java/Hadoop ์ค์ฌ | ์ธ์ด ์ค๋ฆฝ์ , Google ์ํ๊ณ |
7. dataclass + ์ง๋ ฌํ
dataclass์ ์ง๋ ฌํ๋ ์์ฃผ ํจ๊ป ์ฐ์ธ๋ค. ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ JSON์ผ๋ก ๋ณํํ๊ฑฐ๋, API ์๋ต์ dataclass๋ก ๋งคํํ๋ ํจํด์ด ์ผ๋ฐ์ ์ด๋ค.
dataclass โ JSON (asdict ํ์ฉ)
from dataclasses import dataclass, asdict
import json
@dataclass
class Address:
city: str
zipcode: str
@dataclass
class User:
name: str
age: int
address: Address
user = User("alice", 30, Address("์์ธ", "06000"))
# asdict()๋ก dict ๋ณํ ํ json.dumps()
json_str = json.dumps(asdict(user), ensure_ascii=False, indent=2)
print(json_str)
# {
# "name": "alice",
# "age": 30,
# "address": {
# "city": "์์ธ",
# "zipcode": "06000"
# }
# }์ค์ฒฉ๋ dataclass๋ asdict()๊ฐ ์ฌ๊ท์ ์ผ๋ก dict ๋ณํํด์ฃผ๋ฏ๋ก ๊ทธ๋๋ก json.dumps()์ ๋ฃ์ผ๋ฉด ๋๋ค.
JSON โ dataclass
์ญ๋ฐฉํฅ์ ์๋ ๋ณํ์ด ์์ด์ ์ง์ ๋งคํํด์ผ ํ๋ค.
# ๋ฐฉ๋ฒ 1: ๋จ์ํ ๊ฒฝ์ฐ โ dict unpacking
json_str = '{"name": "alice", "age": 30}'
data = json.loads(json_str)
user = User(**data, address=Address("์์ธ", "06000"))# ๋ฐฉ๋ฒ 2: ์ค์ฒฉ ๊ตฌ์กฐ โ object_hook ํ์ฉ
def from_json(json_str: str) -> User:
data = json.loads(json_str)
data["address"] = Address(**data["address"])
return User(**data)
json_str = '{"name": "alice", "age": 30, "address": {"city": "์์ธ", "zipcode": "06000"}}'
user = from_json(json_str)
print(user) # User(name='alice', age=30, address=Address(city='์์ธ', zipcode='06000'))datetime์ด ํฌํจ๋ dataclass
from dataclasses import dataclass, field, asdict
from datetime import datetime
import json
@dataclass
class Event:
title: str
start: datetime
end: datetime
attendees: list[str] = field(default_factory=list)
def json_default(obj):
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(f"์ง๋ ฌํ ๋ถ๊ฐ: {type(obj)}")
event = Event(
title="์คํ๋ฆฐํธ ๋ฆฌ๋ทฐ",
start=datetime(2026, 4, 12, 14, 0),
end=datetime(2026, 4, 12, 15, 0),
attendees=["alice", "bob"],
)
json_str = json.dumps(asdict(event), default=json_default, ensure_ascii=False, indent=2)
print(json_str)
# {
# "title": "์คํ๋ฆฐํธ ๋ฆฌ๋ทฐ",
# "start": "2026-04-12T14:00:00",
# "end": "2026-04-12T15:00:00",
# "attendees": ["alice", "bob"]
# }dataclass + pickle
pickle์ dataclass๋ฅผ ๋ณ๋ ๋ณํ ์์ด ๋ฐ๋ก ์ง๋ ฌํํ ์ ์๋ค.
import pickle
pickled = pickle.dumps(user)
restored = pickle.loads(pickled)
print(restored) # User(name='alice', age=30, address=Address(city='์์ธ', zipcode='06000'))
print(type(restored)) # <class 'User'>
print(restored == user) # TrueJSON๊ณผ ๋ฌ๋ฆฌ ํ์ ์ด ๊ทธ๋๋ก ๋ณด์กด๋๋ฏ๋ก ๋ณํ ์ฝ๋๊ฐ ํ์ ์๋ค. ๋ค๋ง pickle์ ๋ณด์ ์ฃผ์์ฌํญ์ ๊ทธ๋๋ก ์ ์ฉ๋๋ค.
8. ์ธ์ ๋ญ ์ธ๊น
| ๊ธฐ์ค | JSON | pickle | Protobuf / Avro |
|---|---|---|---|
| ์ฌ๋์ด ์ฝ์ ์ ์๋๊ฐ | O | X | X |
| ์ธ์ด ๊ฐ ํธํ | O | X (Python ์ ์ฉ) | O |
| Python ๊ฐ์ฒด ๊ทธ๋๋ก ๋ณด์กด | X (ํ์ ์์ค) | O | X (์คํค๋ง ๋ณํ ํ์) |
| ๋ณด์ | ์์ | ์ํ (์์ ์ฝ๋ ์คํ) | ์์ |
| ์๋ | ๋ณดํต | ๋น ๋ฆ | ๋งค์ฐ ๋น ๋ฆ |
| ํฌ๊ธฐ | ํผ (ํ ์คํธ) | ์ค๊ฐ | ์์ (๋ฐ์ด๋๋ฆฌ) |
| ์คํค๋ง ๊ฐ์ | X | X | O |
ํ๋จ ํ๋ฆ
์ธ๋ถ ์์คํ
๊ณผ ํต์ ํ๋๊ฐ?
โโโ Yes โ JSON (REST API, ์น)
โ Protobuf (gRPC, ๊ณ ์ฑ๋ฅ)
โ Avro (Kafka, ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ)
โโโ No โ Python ๋ด๋ถ์์๋ง ์ฐ๋๊ฐ?
โโโ Yes โ pickle (๋ชจ๋ธ ์ ์ฅ, ์บ์, IPC)
โโโ ์ค์ ํ์ผ โ YAML, JSON
์ค๋ฌด ์กฐํฉ ์์
- ์น API โ ์์ฒญ/์๋ต์ JSON, ๋ด๋ถ ์บ์๋ pickle
- ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ โ Kafka ๋ฉ์์ง๋ Avro, ์ค๊ฐ ๊ฒฐ๊ณผ ์บ์๋ pickle
- ML ์๋น โ ๋ชจ๋ธ ์ ์ฅ์ pickle/joblib, API ์๋ต์ JSON