跳至正文

JSON과 CSV

在Python中 JSON과 CSV 데이터를 다루는 방법让我们学习. 이 두 가지는 가장 널리 사용되는 데이터 교환 형식입니다.

JSON 다루기 📦

JSON(JavaScript Object Notation)은 데이터를 저장하고 전송하는 경량 형식입니다.

기본 사용법

import json

# Python 객체 → JSON 문자열
data = {
'name': '홍길동',
'age': 30,
'city': '서울',
'hobbies': ['독서', '등산', '요리']
}

json_string = json.dumps(data)
print(json_string)
# {"name": "홍길동", "age": 30, "city": "서울", "hobbies": ["독서", "등산", "요리"]}

# JSON 문자열 → Python 객체
parsed_data = json.loads(json_string)
print(parsed_data['name']) # 홍길동

파일로 저장하고 읽기

import json

# JSON 파일로 저장
data = {
'users': [
{'id': 1, 'name': '김철수', 'email': 'kim@example.com'},
{'id': 2, 'name': '이영희', 'email': 'lee@example.com'},
]
}

with open('users.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)

# JSON 读取文件
with open('users.json', 'r', encoding='utf-8') as f:
loaded_data = json.load(f)
print(loaded_data)

JSON 포맷팅 옵션

import json

data = {'name': '홍길동', 'age': 30, 'skills': ['Python', 'JavaScript']}

# 들여쓰기 없음 (압축)
compact = json.dumps(data)
print(compact)
# {"name": "홍길동", "age": 30, "skills": ["Python", "JavaScript"]}

# 들여쓰기 2칸 (읽기 쉽게)
pretty = json.dumps(data, indent=2, ensure_ascii=False)
print(pretty)
# {
# "name": "홍길동",
# "age": 30,
# "skills": [
# "Python",
# "JavaScript"
# ]
# }

# 키 정렬
sorted_json = json.dumps(data, sort_keys=True, indent=2, ensure_ascii=False)
print(sorted_json)

복잡한 데이터 타입 처리

import json
from datetime import datetime
from decimal import Decimal

class DateTimeEncoder(json.JSONEncoder):
"""datetime을 JSON으로 직렬화"""
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, Decimal):
return float(obj)
return super().default(obj)

# 사용 예제
data = {
'timestamp': datetime.now(),
'amount': Decimal('123.45')
}

json_string = json.dumps(data, cls=DateTimeEncoder, indent=2)
print(json_string)

CSV 다루기 📊

CSV(Comma-Separated Values)는 표 형식 데이터를 저장하는 간단한 형식입니다.

기본 CSV 쓰기

import csv

# CSV 写入文件
data = [
['이름', '나이', '도시'],
['홍길동', 30, '서울'],
['김철수', 25, '부산'],
['이영희', 28, '대구']
]

with open('users.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerows(data)

CSV 읽기

import csv

# CSV 读取文件
with open('users.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)
# ['이름', '나이', '도시']
# ['홍길동', '30', '서울']
# ...

DictReader: 딕셔너리로 읽기

import csv

# 헤더를 키로 사용하여 읽기
with open('users.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
print(f"{row['이름']}{row['나이']}세이고 {row['도시']}에 삽니다.")
# 홍길동은 30세이고 서울에 삽니다.
# 김철수는 25세이고 부산에 삽니다.

DictWriter: 딕셔너리로 쓰기

import csv

# 딕셔너리 데이터를 CSV로 저장
users = [
{'name': '홍길동', 'age': 30, 'city': '서울'},
{'name': '김철수', 'age': 25, 'city': '부산'},
{'name': '이영희', 'age': 28, 'city': '대구'}
]

with open('users_dict.csv', 'w', newline='', encoding='utf-8') as f:
fieldnames = ['name', 'age', 'city']
writer = csv.DictWriter(f, fieldnames=fieldnames)

writer.writeheader() # 헤더 쓰기
writer.writerows(users) # 모든 행 쓰기

CSV 옵션

import csv

# 구분자 변경 (탭, 세미콜론 등)
with open('data.tsv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f, delimiter='\t')
writer.writerow(['이름', '나이'])
writer.writerow(['홍길동', 30])

# 인용 스타일
with open('quoted.csv', 'w', newline='', encoding='utf-8') as f:
# QUOTE_ALL: 모든 필드를 인용
writer = csv.writer(f, quoting=csv.QUOTE_ALL)
writer.writerow(['이름', '나이'])

# QUOTE_MINIMAL: 필요한 경우만 인용 (기본값)
writer = csv.writer(f, quoting=csv.QUOTE_MINIMAL)
writer.writerow(['홍길동', 30])

实践示例 💡

예제 1: API 응답 파싱

import json
import urllib.request

def fetch_and_parse_json(url):
"""JSON API 호출 및 파싱"""
try:
with urllib.request.urlopen(url) as response:
data = response.read()
return json.loads(data)
except Exception as e:
print(f'에러 발생: {e}')
return None

# 사용 예제 (실제 API 사용)
# url = 'https://api.example.com/users'
# users = fetch_and_parse_json(url)

# 로컬 JSON 读取文件 예제
def load_user_data(filename):
"""사용자 데이터 로드"""
try:
with open(filename, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
print(f'파일을 찾을 수 없습니다: {filename}')
return []
except json.JSONDecodeError as e:
print(f'JSON 파싱 에러: {e}')
return []

# users = load_user_data('users.json')
# for user in users:
# print(f"{user['name']}: {user['email']}")

예제 2: 설정 파일 관리

import json
from pathlib import Path

class Config:
"""JSON 설정 파일 관리자"""

def __init__(self, config_file='config.json'):
self.config_file = Path(config_file)
self.data = self.load()

def load(self):
"""설정 파일 로드"""
if self.config_file.exists():
with open(self.config_file, 'r', encoding='utf-8') as f:
return json.load(f)
return self.get_default()

def save(self):
"""설정 파일 저장"""
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.data, f, indent=2, ensure_ascii=False)

def get(self, key, default=None):
"""설정 값 가져오기"""
return self.data.get(key, default)

def set(self, key, value):
"""설정 값 설정하기"""
self.data[key] = value
self.save()

def get_default(self):
"""기본 설정"""
return {
'app_name': 'MyApp',
'version': '1.0.0',
'debug': False,
'max_connections': 100
}

# 사용 예제
config = Config()
print(f"앱 이름: {config.get('app_name')}")

config.set('debug', True)
print(f"디버그 모드: {config.get('debug')}")

예제 3: CSV에서 JSON으로 변환

import csv
import json

def csv_to_json(csv_file, json_file):
"""CSV 파일을 JSON으로 변환"""
data = []

with open(csv_file, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
# 숫자 타입 변환
for key, value in row.items():
if value.isdigit():
row[key] = int(value)
elif value.replace('.', '', 1).isdigit():
row[key] = float(value)
data.append(row)

with open(json_file, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)

print(f'{csv_file}{json_file} 변환 완료')
return data

# 사용 예제
# data = csv_to_json('users.csv', 'users.json')

예제 4: JSON에서 CSV로 변환

import json
import csv

def json_to_csv(json_file, csv_file):
"""JSON 파일을 CSV로 변환"""
with open(json_file, 'r', encoding='utf-8') as f:
data = json.load(f)

if not data:
print('데이터가 비어있습니다')
return

# 첫 번째 항목에서 키 추출
if isinstance(data, list):
fieldnames = list(data[0].keys())
else:
# 단일 객체인 경우
data = [data]
fieldnames = list(data[0].keys())

with open(csv_file, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(data)

print(f'{json_file}{csv_file} 변환 완료')

# 사용 예제
# json_to_csv('users.json', 'users_output.csv')

예제 5: 엑셀 데이터 처리 (CSV 사용)

import csv
from collections import defaultdict

class SalesAnalyzer:
"""판매 데이터 분석기"""

def __init__(self, csv_file):
self.data = self.load_data(csv_file)

def load_data(self, csv_file):
"""CSV 데이터 로드"""
data = []
with open(csv_file, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
# 숫자 변환
row['amount'] = float(row['amount'])
row['quantity'] = int(row['quantity'])
data.append(row)
return data

def total_sales(self):
"""총 매출"""
return sum(item['amount'] for item in self.data)

def sales_by_product(self):
"""제품별 매출"""
sales = defaultdict(float)
for item in self.data:
sales[item['product']] += item['amount']
return dict(sales)

def top_products(self, n=5):
"""상위 N개 제품"""
sales = self.sales_by_product()
sorted_products = sorted(sales.items(), key=lambda x: x[1], reverse=True)
return sorted_products[:n]

def generate_report(self, output_file):
"""리포트 생성"""
report = {
'total_sales': self.total_sales(),
'product_count': len(self.sales_by_product()),
'top_products': [
{'product': name, 'sales': amount}
for name, amount in self.top_products()
]
}

with open(output_file, 'w', encoding='utf-8') as f:
json.dump(report, f, indent=2, ensure_ascii=False)

return report

# 사용 예제 (CSV 파일 생성)
sample_data = [
['product', 'quantity', 'amount'],
['노트북', 5, 5000000],
['마우스', 20, 500000],
['키보드', 15, 1500000],
['노트북', 3, 3000000],
]

with open('sales.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerows(sample_data)

# analyzer = SalesAnalyzer('sales.csv')
# print(f'총 매출: {analyzer.total_sales():,}원')
# print(f'제품별 매출: {analyzer.sales_by_product()}')
# analyzer.generate_report('sales_report.json')

예제 6: 중첩된 JSON 처리

import json

def flatten_json(nested_json, parent_key='', separator='_'):
"""중첩된 JSON을 평탄화"""
items = []

for key, value in nested_json.items():
new_key = f'{parent_key}{separator}{key}' if parent_key else key

if isinstance(value, dict):
items.extend(flatten_json(value, new_key, separator).items())
elif isinstance(value, list):
for i, item in enumerate(value):
if isinstance(item, dict):
items.extend(flatten_json(item, f'{new_key}_{i}', separator).items())
else:
items.append((f'{new_key}_{i}', item))
else:
items.append((new_key, value))

return dict(items)

# 사용 예제
nested_data = {
'name': '홍길동',
'address': {
'city': '서울',
'street': '강남대로',
'zipcode': '12345'
},
'phones': [
{'type': 'home', 'number': '02-1234-5678'},
{'type': 'mobile', 'number': '010-1234-5678'}
]
}

flat = flatten_json(nested_data)
print(json.dumps(flat, indent=2, ensure_ascii=False))

예제 7: CSV 데이터 필터링 및 정렬

import csv

class CSVProcessor:
"""CSV 데이터 처리기"""

def __init__(self, filename):
self.filename = filename
self.data = self.load()

def load(self):
"""CSV 로드"""
with open(self.filename, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
return list(reader)

def filter(self, condition):
"""조건에 맞는 행만 필터링"""
return [row for row in self.data if condition(row)]

def sort(self, key, reverse=False):
"""정렬"""
return sorted(self.data, key=lambda x: x[key], reverse=reverse)

def save(self, data, output_file):
"""결과 저장"""
if not data:
print('저장할 데이터가 없습니다')
return

fieldnames = list(data[0].keys())

with open(output_file, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(data)

print(f'{output_file} 저장 완료')

# 사용 예제 (샘플 CSV 생성)
sample_data = [
['name', 'age', 'city', 'salary'],
['홍길동', '30', '서울', '50000'],
['김철수', '25', '부산', '40000'],
['이영희', '35', '서울', '60000'],
['박민수', '28', '대구', '45000'],
]

with open('employees.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerows(sample_data)

# processor = CSVProcessor('employees.csv')

# 서울에 사는 직원만 필터링
# seoul_employees = processor.filter(lambda row: row['city'] == '서울')
# processor.save(seoul_employees, 'seoul_employees.csv')

# 나이로 정렬
# sorted_by_age = processor.sort('age', reverse=True)
# processor.save(sorted_by_age, 'employees_sorted.csv')

예제 8: JSON 스키마 검증

import json

class JSONValidator:
"""간단한 JSON 스키마 검증기"""

@staticmethod
def validate_user(data):
"""사용자 데이터 검증"""
required_fields = ['name', 'email', 'age']
errors = []

# 필수 필드 확인
for field in required_fields:
if field not in data:
errors.append(f'필수 필드 누락: {field}')

# 타입 확인
if 'name' in data and not isinstance(data['name'], str):
errors.append('name은 문자열이어야 합니다')

if 'age' in data:
try:
age = int(data['age'])
if age < 0 or age > 150:
errors.append('age는 0-150 사이여야 합니다')
except ValueError:
errors.append('age는 숫자여야 합니다')

if 'email' in data and '@' not in data['email']:
errors.append('유효하지 않은 이메일')

return len(errors) == 0, errors

@staticmethod
def validate_batch(data_list):
"""여러 데이터 검증"""
results = []

for i, data in enumerate(data_list):
is_valid, errors = JSONValidator.validate_user(data)
results.append({
'index': i,
'valid': is_valid,
'errors': errors,
'data': data
})

return results

# 사용 예제
users = [
{'name': '홍길동', 'email': 'hong@example.com', 'age': 30},
{'name': '김철수', 'email': 'invalid-email', 'age': 25},
{'email': 'lee@example.com', 'age': 28}, # name 누락
{'name': '박민수', 'email': 'park@example.com', 'age': 200}, # 잘못된 age
]

results = JSONValidator.validate_batch(users)

for result in results:
print(f"\n항목 {result['index']}:")
if result['valid']:
print(' ✓ 유효')
else:
print(' ✗ 유효하지 않음')
for error in result['errors']:
print(f' - {error}')

예제 9: 대용량 JSON 스트리밍

import json

def process_large_json_file(filename):
"""대용량 JSON 파일을 청크 단위로 처리"""
with open(filename, 'r', encoding='utf-8') as f:
# JSON 배열을 스트리밍으로 처리
decoder = json.JSONDecoder()
buffer = ''

for line in f:
buffer += line
try:
# JSON 객체 파싱 시도
obj, index = decoder.raw_decode(buffer)
yield obj

# 파싱된 부분 제거
buffer = buffer[index:].lstrip()
except json.JSONDecodeError:
# 아직 완전한 객체가 아님, 계속 읽기
continue

# 사용 예제
# for item in process_large_json_file('large_data.json'):
# process_item(item)

예제 10: CSV와 JSON 통합 데이터 관리

import csv
import json
from pathlib import Path

class DataManager:
"""CSV와 JSON을 통합 관리하는 클래스"""

def __init__(self, data_dir='data'):
self.data_dir = Path(data_dir)
self.data_dir.mkdir(exist_ok=True)

def save(self, data, filename, format='json'):
"""데이터 저장 (JSON 또는 CSV)"""
filepath = self.data_dir / filename

if format == 'json':
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
elif format == 'csv':
if not data:
return

with open(filepath, 'w', newline='', encoding='utf-8') as f:
if isinstance(data[0], dict):
writer = csv.DictWriter(f, fieldnames=data[0].keys())
writer.writeheader()
writer.writerows(data)
else:
writer = csv.writer(f)
writer.writerows(data)

print(f'{filepath} 저장 완료')

def load(self, filename):
"""데이터 로드 (자동 형식 감지)"""
filepath = self.data_dir / filename

if not filepath.exists():
print(f'파일이 없습니다: {filepath}')
return None

# 확장자로 형식 판단
if filepath.suffix == '.json':
with open(filepath, 'r', encoding='utf-8') as f:
return json.load(f)
elif filepath.suffix == '.csv':
with open(filepath, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
return list(reader)

return None

def convert(self, input_file, output_file):
"""파일 형식 변환"""
data = self.load(input_file)
if data is None:
return

output_format = Path(output_file).suffix[1:] # .json → json
self.save(data, output_file, format=output_format)
print(f'{input_file}{output_file} 변환 완료')

# 사용 예제
manager = DataManager()

# JSON 저장
users = [
{'id': 1, 'name': '홍길동', 'age': 30},
{'id': 2, 'name': '김철수', 'age': 25},
]
manager.save(users, 'users.json', format='json')

# CSV 저장
manager.save(users, 'users.csv', format='csv')

# 로드
loaded_users = manager.load('users.json')
print(loaded_users)

# 변환
# manager.convert('users.json', 'users_converted.csv')

常见问题 ❓

Q1: JSON에서 한글이 \uXXXX로 표시되는 문제는?

import json

data = {'name': '홍길동'}

# 나쁨: \uXXXX로 인코딩
json_str = json.dumps(data)
print(json_str) # {"name": "\ud64d\uae38\ub3d9"}

# 좋음: 한글 그대로 유지
json_str = json.dumps(data, ensure_ascii=False)
print(json_str) # {"name": "홍길동"}

Q2: CSV에서 줄바꿈이 있는 데이터를 처리하는 방법은?

import csv

# 줄바꿈이 포함된 데이터
data = [
['name', 'description'],
['Product A', 'This is\na multi-line\ndescription'],
]

# CSV로 저장 (자동으로 인용 처리)
with open('products.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerows(data)

# 읽을 때도 자동으로 처리
with open('products.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)

Q3: JSON에 주석을 추가할 수 있나요?

# JSON 표준은 주석을 지원하지 않지만,
# 데이터에 _comment 필드를 추가할 수 있습니다

config = {
'_comment': '이것은 설정 파일입니다',
'debug': True,
'max_connections': 100,
'database': {
'_comment': '데이터베이스 설정',
'host': 'localhost',
'port': 5432
}
}

# 또는 JSONC, JSON5 같은 확장 형식 사용

Q4: CSV 첫 줄이 헤더가 아닐 때는?

import csv

# 헤더 직접 지정
with open('no_header.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f, fieldnames=['name', 'age', 'city'])
for row in reader:
print(row)

Q5: JSON 파일이 깨졌는지 확인하는 방법은?

import json

def is_valid_json(filename):
"""JSON 파일 유효성 검사"""
try:
with open(filename, 'r', encoding='utf-8') as f:
json.load(f)
return True
except json.JSONDecodeError as e:
print(f'JSON 에러: {e}')
return False
except FileNotFoundError:
print(f'파일이 없습니다: {filename}')
return False

# 사용
if is_valid_json('data.json'):
print('유효한 JSON 파일입니다')

성능 팁 🚀

JSON 성능

import json

# 좋음: 파일 직접 사용
with open('data.json', 'r') as f:
data = json.load(f) # 파일에서 직접 로드

# 나쁨: 불필요한 중간 단계
with open('data.json', 'r') as f:
content = f.read() # 전체를 문자열로 읽고
data = json.loads(content) # 다시 파싱

CSV 성능

import csv

# 대용량 CSV는 DictReader보다 reader가 빠름
# 하지만 DictReader가 더 편리함

# 빠름 (메모리 효율적)
with open('large.csv', 'r') as f:
reader = csv.reader(f)
for row in reader:
process(row)

# 느림 (모든 데이터를 메모리에)
with open('large.csv', 'r') as f:
reader = csv.reader(f)
all_rows = list(reader) # 메모리에 전체 로드

下一步

  • 정규표현식: JSON/CSV 데이터 검증과 파싱
  • 파일 입출력: 파일 시스템 고급 작업
  • pandas: 대용량 데이터 분석
  • requests: API에서 JSON 데이터 받기