💰
실습 프로젝트 (Practice) · ★★★
JSON 영구 저장 + 카테고리
Lv3 · 가계부
수입/지출 기록을 JSON 으로 영구 저장. 카테고리별 합계, 잔액, 막대그래프 시각화까지 포함합니다.
JSON예외집계
소요 시간
⏱ 3~5시간
난이도
📊 중급 종합
선수 조건
🎯 중급 5-6주차 (예외, JSON I/O)
결과물
영구 저장 패턴과 집계
이 강의에서 배우는 것
- 1JSON 으로 영구 저장
- 2카테고리별 집계와 잔액 계산
- 3잘못된 입력에 대한 친절한 예외 처리
프로젝트 개요
- ledger.json 에 기록 영구 저장
- 명령: add / list / summary / del <번호> / quit
- summary: 카테고리별 합계 + 막대그래프
도전 과제
- 월별 집계 (2026-05 필터)
- 한 줄 명령 (add 지출 5000 식비 점심)
채점 체크리스트
- JSON 손상 안전 처리
- 예외 처리
- 막대그래프 출력
💻 예제 (examples)
실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.
solution.py— 전체 동작 코드
CODE
"""가계부 — JSON 영구 저장"""
import json
from pathlib import Path
from datetime import date
DB = Path("ledger.json")
def load():
if not DB.exists():
return []
try:
return json.loads(DB.read_text(encoding="utf-8"))
except json.JSONDecodeError:
print("⚠️ ledger.json 손상 — 빈 장부로 시작")
return []
def save(records):
DB.write_text(
json.dumps(records, ensure_ascii=False, indent=2),
encoding="utf-8"
)
def add(records):
t = input("수입/지출: ").strip()
if t not in ("수입", "지출"):
print("'수입' 또는 '지출'"); return
try:
amount = int(input("금액: "))
if amount < 0:
raise ValueError
except ValueError:
print("양의 정수 입력"); return
category = input("카테고리: ").strip()
memo = input("메모: ").strip()
records.append({
"type": t, "amount": amount, "category": category,
"memo": memo, "date": date.today().isoformat(),
})
save(records)
print("추가됨")
def show(records):
if not records:
print("(비어있음)"); return
for i, r in enumerate(records, 1):
sign = "+" if r["type"] == "수입" else "-"
print(f"{i:>3}. {r['date']} {sign}{r['amount']:>10,}원 [{r['category']:<6}] {r['memo']}")
def summary(records):
income = sum(r["amount"] for r in records if r["type"] == "수입")
expense = sum(r["amount"] for r in records if r["type"] == "지출")
print(f"수입: {income:>12,}원")
print(f"지출: {expense:>12,}원")
print(f"잔액: {income - expense:>12,}원")
by_cat = {}
for r in records:
if r["type"] == "지출":
by_cat[r["category"]] = by_cat.get(r["category"], 0) + r["amount"]
if by_cat:
max_amt = max(by_cat.values())
print("\n[지출 카테고리별]")
for cat, amt in sorted(by_cat.items(), key=lambda x: -x[1]):
bar = "#" * int(40 * amt / max_amt)
print(f" {cat:<8} {amt:>10,} {bar}")
def main():
records = load()
print(f"불러옴: {len(records)}건")
while True:
cmd = input("\n명령(add/list/summary/del <n>/quit): ").strip()
if not cmd: continue
if cmd == "quit": break
elif cmd == "add": add(records)
elif cmd == "list": show(records)
elif cmd == "summary": summary(records)
elif cmd.startswith("del "):
try:
idx = int(cmd.split()[1]) - 1
if 0 <= idx < len(records):
del records[idx]; save(records); print("삭제")
except (ValueError, IndexError):
print("올바른 번호 입력")
if __name__ == "__main__":
main()▶ 실행 결과
불러옴: 0건
명령: add
수입/지출: 지출
금액: 5000
카테고리: 식비
메모: 점심
추가됨
명령: summary
수입: 0원
지출: 5,000원
잔액: -5,000원
[지출 카테고리별]
식비 5,000 ########################################📝 과제 (exercises)
직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.
과제 1
프로젝트 구현
목표: 위 사양대로 직접 작성한다.
요구사항
- JSON 영구 저장
- summary 막대그래프
- 예외 처리
💡 힌트
json.dumps(..., ensure_ascii=False, indent=2)
입출력 예시
잔액: -5,000원채점
- · 저장 안전
- · 집계 정확
- · 예외 안전
▶정답 코드 펼치기 / 접기
SOLUTION
# 위 solution.py 와 동일▶ 실행 결과
(예제 참고)