이번 편의 목표: csv 모듈로 헤더가 있는 표를 저장하고 숫자로 변환합니다.

지출을 한 줄짜리 문장으로 저장하면 사람이 읽기는 쉽지만 합계를 다시 구하기 불편합니다. 날짜·항목·금액을 각각의 열로 저장하면 프로그램과 스프레드시트가 함께 다루기 쉬워집니다. CSV는 이런 표를 텍스트로 표현하는 형식입니다.

구분자는 직접 붙이지 않는다

Python
import csv
from pathlib import Path

path = Path("practice_output") / "expenses_demo.csv"
path.parent.mkdir(parents=True, exist_ok=True)
rows = [
    {"date": "2026-10-01", "item": "커피, 차", "amount": 4500},
    {"date": "2026-10-02", "item": "점심", "amount": 8000},
]

with path.open("w", encoding="utf-8-sig", newline="") as file:
    writer = csv.DictWriter(file, fieldnames=["date", "item", "amount"])
    writer.writeheader()
    writer.writerows(rows)

loaded = []
with path.open("r", encoding="utf-8-sig", newline="") as file:
    reader = csv.DictReader(file)
    for row in reader:
        row["amount"] = int(row["amount"])
        loaded.append(row)

print(loaded[0]["item"])
print(sum(row["amount"] for row in loaded))

출력은 `커피, 차`와 `12500`입니다. 항목 안에 쉼표가 있어도 `csv` 모듈이 따옴표 규칙을 처리합니다. 문자열을 직접 쉼표로 연결하거나 읽을 때 `split(",")`로 나누면 이 경우 열이 틀어질 수 있습니다.

`DictWriter`는 지정한 열 이름에 맞춰 딕셔너리를 기록합니다. `writeheader()`가 첫 줄에 열 이름을 쓰고, `writerows()`가 여러 행을 씁니다. `newline=""`은 CSV 모듈이 줄바꿈을 처리하도록 파일을 여는 권장 방식입니다.

텍스트 파일에서는 숫자도 문자로 읽힌다

`DictReader`가 읽은 금액은 문자열입니다. 그대로 더하면 숫자 합계를 구할 수 없으므로 `int()`로 변환합니다. 마지막 `sum(row["amount"] for row in loaded)`는 값을 차례로 계산해 `sum()`에 넘기는 제너레이터 표현식입니다. 여기서는 12편의 리스트 컴프리헨션에서 대괄호를 생략하고 합계에 필요한 값만 공급하는 형태로 이해하면 됩니다.

`utf-8-sig`는 UTF-8에 BOM을 포함해 쓰고 읽을 때 제거하는 인코딩입니다. 일부 스프레드시트 프로그램이 UTF-8 한글을 인식하는 데 도움이 됩니다. 다만 쉼표가 열로 자동 분리되는지는 프로그램의 가져오기 옵션과 지역 설정에도 영향을 받습니다. 필요하면 데이터 가져오기에서 UTF-8과 쉼표 구분을 지정합니다.

파일이 있다고 믿기 전에 형식을 확인한다

외부에서 받은 CSV에는 헤더 누락, 빈 금액, `4,500` 같은 표시용 숫자, 잘못된 날짜가 있을 수 있습니다. 이번 예제는 우리가 만든 정상 자료를 다시 읽습니다. 마지막 프로젝트에서는 헤더·금액·날짜를 확인하고 잘못된 행에서 오류를 알리도록 확장합니다.

위 코드는 다시 실행하면 데모 파일을 덮어씁니다. 실제 지출 파일에 적용하기 전에 출력 경로를 분리해야 합니다. 또한 CSV에서 따옴표로 감쌌다고 스프레드시트 수식 실행 문제가 사라지는 것은 아닙니다. 외부 입력을 배포용 CSV로 내보낼 때는 수식처럼 해석될 값의 처리 정책도 필요합니다.

직접 해 보기

읽은 `loaded`에서 금액이 5000원 이상인 건수와 총액을 구하세요. 파일을 다시 읽을 필요 없이 이미 읽은 자료를 사용합니다.

해설

Python
# 독립 실행할 수 있도록 같은 형태의 읽기 완료 자료를 준비합니다.
loaded = [
    {"item": "커피, 차", "amount": 4500},
    {"item": "점심", "amount": 8000},
]
selected = [row for row in loaded if row["amount"] >= 5000]
print(len(selected))
print(sum(row["amount"] for row in selected))

결과는 `1`, `8000`입니다. 파일 읽기와 자료 계산을 분리하면 같은 자료로 여러 보고서를 만들 수 있습니다.