# 파이썬 표준 라이브러리로 데이터 저장 & 직렬화: `json`, `pickle`, `csv` 데이터를 파일에 저장하거나 네트워크로 전달하려면 **직렬화(Serialization)** 가 필요합니다. 파이썬은 이를 위해 꽤 성격이 다른 세 가지 표준 도구를 제공합니다: **`json`**, **`pickle`**, **`csv`**. ![데이터 배송 포맷 업체들](/media/editor_temp/6/82b2d285-16b0-473a-b88a-cd9a6f6582f2.png) * **`json`**: 사람이 읽을 수 있는 텍스트 포맷, 언어 간 교환에 강함 * **`pickle`**: 파이썬 객체를 그대로 저장하는 바이너리 포맷, 강력하지만 위험 요소가 있음 * **`csv`**: 표(테이블) 데이터를 가장 단순하게 담는 텍스트 포맷, 범용성이 높음 이 글에서는 “어떤 데이터를 어디에 쓰려는지”에 따라 세 모듈을 고르는 감각을 정리해 봅니다. --- ## 1. JSON {#sec-c0452ee3f8b9} ### 1.1 개요 {#sec-573894aef250} JSON(JavaScript Object Notation)은 **텍스트 기반**이며 **언어 독립적**입니다. 설정 파일, API 응답/요청, 로그 등 “다른 시스템과 주고받기 좋은 형태”로 널리 쓰입니다. 파이썬에서는 `json` 모듈로 바로 다룹니다. ### 1.2 핵심 API(자주 쓰는 것만) {#sec-7388e1478aff} * `json.dump(obj, fp, ...)` / `json.load(fp, ...)` : 파일로 저장/읽기 * `json.dumps(obj, ...)` / `json.loads(s, ...)` : 문자열로 변환/복원 옵션 중에서 기억해두면 좋은 것: * `ensure_ascii=False` : ASCII 범위를 벗어나는 국제 문자(예: 라틴 확장, 키릴, CJK 등)를 이스케이프하지 않고 원문 그대로 저장/출력 * `indent=2` : 보기 좋게 예쁘게 출력 * `default=...` : JSON이 모르는 타입을 변환하는 “우회로” 제공 ### 1.3 사용 예시(기본) {#sec-57d0c0653337} ```python import json data = { "name": "Alice", "age": 30, "skills": ["Python", "Data Science"] } with open("data.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) with open("data.json", "r", encoding="utf-8") as f: loaded = json.load(f) print(loaded) ``` ### 1.4 JSON의 “타입 제한”을 간편하게 해결하는 법 {#sec-a43d0fe8a199} JSON은 기본적으로 `dict`, `list`, `str`, `int/float`, `bool`, `None`만 자연스럽습니다. 예를 들어 `datetime`은 그대로는 저장되지 않습니다. 이때 `default`를 쓰면 깔끔합니다. `default`에는 **변환 함수(callable)** 를 넘깁니다. `json`이 직렬화하지 못하는 객체를 만나면 그 객체가 함수의 인자로 들어오고, 함수는 `str`/`dict`/`list`처럼 **JSON이 이해할 수 있는 타입**으로 바꿔 반환하면 됩니다. 변환할 수 없는 타입은 `TypeError`를 던져 “여기서는 지원하지 않는다”를 명확히 알려주는 패턴이 흔합니다. ```python import json from datetime import datetime, timezone payload = {"created_at": datetime.now(timezone.utc)} def to_jsonable(obj): if isinstance(obj, datetime): return obj.isoformat() raise TypeError(f"Not JSON serializable: {type(obj)!r}") s = json.dumps(payload, default=to_jsonable, ensure_ascii=False) print(s) ``` ### 1.5 장단점 {#sec-667d6fad184b} **장점** * 언어 독립적이라 교환/공유에 강함 * 텍스트라 디버깅이 쉽고 버전 관리(Git)에도 친화적 * `pickle`처럼 로딩만으로 코드가 실행되는 구조가 아님 **단점** * 타입 표현력이 제한적(특히 `datetime`, `set`, `Decimal`, 바이너리 데이터 등) * 큰 데이터에서는 용량/속도 면에서 손해를 볼 수 있음 --- ## 2. Pickle {#sec-7417b1955ccc} ### 2.1 개요 {#sec-1889607ea8cb} `pickle`은 **파이썬 객체를 ‘파이썬 그대로’ 보관**하기 위한 직렬화 방식입니다. 결과물은 사람이 읽는 텍스트가 아니라 **바이너리 데이터**(bytes)이며, 다시 불러오면 거의 동일한 형태의 객체로 복원됩니다. `pickle`이 특히 유용한 경우는 이런 때입니다. * **복잡한 파이썬 객체를 그대로 저장**하고 싶을 때 예: 사용자 정의 클래스 인스턴스, 중첩된 자료구조, 학습된 모델/설정 객체(파이썬 객체 형태), 계산 결과 캐시 등 * **다른 언어와 교환할 필요가 없을 때** `pickle`은 파이썬 전용 포맷이라 “공유/교환” 목적이라면 보통 `json`이 더 적합합니다. 반대로, 다음 상황에는 피하는 편이 좋습니다. * **신뢰할 수 없는 출처의 데이터를 열어야 할 때** (`pickle.load()`는 위험할 수 있음) * **다른 언어/시스템과 데이터를 주고받아야 할 때** (호환성 없음) --- ### 2.2 핵심 API(가장 많이 쓰는 4개) {#sec-a0a97fe87a25} * `pickle.dump(obj, file, protocol=...)` : 객체 → 파일(바이너리)로 저장 * `pickle.load(file)` : 파일에서 읽어 → 객체로 복원 * `pickle.dumps(obj, protocol=...)` : 객체 → bytes로 변환 * `pickle.loads(data)` : bytes → 객체로 복원 아래처럼 “파일로 저장/불러오기”가 가장 흔합니다. ```python import pickle data = {"a": [1, 2, 3], "b": ("x", "y")} with open("data.pkl", "wb") as f: pickle.dump(data, f) with open("data.pkl", "rb") as f: loaded = pickle.load(f) print(loaded) ``` --- ### 2.3 `protocol`이 뭐고, 왜 신경 써야 하나? {#sec-1f8e5565848c} `protocol`은 **pickle이 데이터를 저장하는 “포맷 버전”** 입니다. 프로토콜 버전에 따라 파일 크기/속도/지원 기능이 달라질 수 있습니다. * `protocol`을 **지정하지 않으면**: 파이썬이 “현재 환경에서 가장 적절한 기본값”을 사용합니다. * 보통은 **기본값 그대로** 두어도 충분합니다. * 굳이 건드려야 하는 경우는 대개 두 가지입니다: 1. **아주 오래된 파이썬 버전과 호환**해야 해서 낮은 프로토콜을 강제해야 할 때 2. 파일 크기/속도 최적화를 위해 **명시적으로 최신 프로토콜**을 고정하고 싶을 때 자주 쓰는 패턴은 이렇게 “최신 프로토콜”[^최신 프로토콜]을 지정하는 것입니다. [^최신 프로토콜]: `pickle.HIGHEST_PROTOCOL` **현재 파이썬이 지원하는 가장 높은 프로토콜 번호**를 의미합니다. ```python import pickle with open("data.pkl", "wb") as f: pickle.dump({"x": 1}, f, protocol=pickle.HIGHEST_PROTOCOL) ``` 요약하면: **처음에는 `protocol`을 신경 쓰지 말고**, 필요해졌을 때 `pickle.HIGHEST_PROTOCOL`을 떠올리면 됩니다. ### 2.4 pickle의 진짜 주의점(중요) {#sec-007b70a5e9ec} `pickle.load()` / `pickle.loads()`는 **데이터 안에 들어있는 “복원 로직”을 실행**할 수 있습니다. 즉, **신뢰할 수 없는 pickle을 로딩하는 순간**, 의도치 않은 동작이 발생할 수 있습니다. * 안전한 쪽 선택: “모르는 출처의 `.pkl` 파일은 열지 않는다” * 공유/교환 목적이라면: `json` 같은 텍스트 기반 포맷이 기본 선택 ### 2.5 장단점 {#sec-bbd5a24bec31} **장점** * 파이썬 객체를 폭넓게 저장(거의 무엇이든) * 보통 `json`보다 빠르고 편합니다(특히 복잡한 객체) **단점** * 보안 위험(신뢰할 수 없는 데이터는 절대 로딩 금지) * 파이썬 전용(다른 언어와 호환 불가) * 클래스 정의가 바뀌면 과거 pickle이 깨질 수 있음(버전 호환 이슈) --- ## 3. CSV {#sec-99e590b24e50} ### 3.1 개요 {#sec-1265cc44a461} CSV(Comma-Separated Values)는 표 형태 데이터를 담는 가장 단순한 포맷입니다. 스프레드시트, 데이터 내보내기/가져오기, 간단한 로그 덤프 등에서 자주 등장합니다. ### 3.2 핵심 API(자주 쓰는 것만) {#sec-5b2e4c37cce5} * `csv.reader`, `csv.writer` : 리스트 기반 * `csv.DictReader`, `csv.DictWriter` : 딕셔너리 기반(대부분 이쪽이 편함) ### 3.3 사용 예시(DictWriter/DictReader) {#sec-524921270351} ```python import csv data = [ {"name": "Alice", "age": 30, "city": "Seoul"}, {"name": "Bob", "age": 25, "city": "Busan"}, ] with open("people.csv", "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=["name", "age", "city"]) writer.writeheader() writer.writerows(data) with open("people.csv", "r", encoding="utf-8") as f: reader = csv.DictReader(f) loaded = list(reader) print(loaded) ``` ### 3.4 CSV 주의점 포인트 3가지 {#sec-3db1b7ed8444} 1. **`newline=""`를 꼭 주기** * 특히 Windows에서 줄바꿈이 두 번 들어가는 문제를 피하기 위해 권장됩니다. 2. **타입은 기본적으로 전부 문자열** * 읽어오면 `"30"`처럼 문자열로 들어옵니다. 필요한 컬럼은 직접 변환해야 합니다. 3. **구분자/따옴표/개행이 데이터에 포함될 수 있음** * CSV는 단순해 보이지만 “값에 쉼표가 들어가는 순간”부터 규칙이 생깁니다. 그래서 `csv` 모듈을 쓰는 것이 안전합니다(직접 `split(",")` 하면 곧 망가집니다). ### 3.5 장단점 {#sec-6a8ca4891a8e} **장점** * 매우 가볍고 범용적(대부분의 도구가 읽고 씀) * 표 데이터에는 과할 정도로 단순해서 다루기 쉬움 **단점** * 중첩 구조(리스트 안의 리스트, 딕셔너리 중첩 등)는 표현이 어려움 * 타입 정보가 없어 변환 비용이 따름 --- ## 4. 비교 & 선택 가이드 {#sec-6304ec188cc0} | 항목 | JSON | Pickle | CSV | | ------ | --------- | ------------ | -------- | | 언어 호환 | 매우 좋음 | 파이썬 전용 | 매우 좋음 | | 가독성 | 높음 | 낮음(바이너리) | 높음 | | 타입 표현력 | 제한적 | 매우 높음 | 제한적 | | 안전성 | 비교적 안전 | **주의 필요** | 비교적 안전 | | 데이터 형태 | 트리 구조(중첩) | “파이썬 객체 그대로” | 테이블(행/열) | 고르는 기준을 한 문장으로 줄이면: * **다른 시스템과 주고받는다** → `json` * **파이썬 객체를 통째로 저장한다** → `pickle` (단, 로딩 출처는 엄격히) * **행/열 형태로 내보내고 읽는다** → `csv` --- ## 5. 같은 데이터를 3가지로 저장/복원하기 {#sec-21f8c2a6d6c9} 아래는 동일한 데이터를 `json`, `pickle`, `csv`로 각각 저장하고 다시 읽어오는 예시입니다. ```python import json, pickle, csv data = [ {"id": 1, "name": "Alice", "score": 95.5}, {"id": 2, "name": "Bob", "score": 88.0}, ] # 1) JSON with open("data.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) with open("data.json", "r", encoding="utf-8") as f: json_loaded = json.load(f) # 2) Pickle with open("data.pkl", "wb") as f: pickle.dump(data, f) with open("data.pkl", "rb") as f: pickle_loaded = pickle.load(f) # 3) CSV with open("data.csv", "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=["id", "name", "score"]) writer.writeheader() writer.writerows(data) with open("data.csv", "r", encoding="utf-8") as f: reader = csv.DictReader(f) csv_loaded = [ {"id": int(row["id"]), "name": row["name"], "score": float(row["score"])} for row in reader ] print(json_loaded) print(pickle_loaded) print(csv_loaded) ``` 포인트: * CSV는 읽자마자 숫자 변환을 해줘야 “의도한 타입”으로 돌아옵니다. * pickle은 편하지만, 로딩 데이터의 출처를 신중히 관리해야 합니다. --- ## 6. 마무리 {#sec-f88db3766ed6} 파이썬 표준 라이브러리만으로도 데이터 저장과 직렬화는 놀랄 만큼 넓은 범위를 커버합니다. * **사람도 읽고, 다른 언어도 읽어야 한다** → `json` * **파이썬 객체를 그대로 보관하고 싶다** → `pickle` * **표 형태로 간단히 내보내고 싶다** → `csv` 이 세 모듈 중에서 저장하고자 하는 **데이터 모양과 목적이 무엇인지**로 고르면 선택이 빠르고 깔끔해집니다.