📖
실습 프로젝트 (Practice) · ★★★
OOP + JSON
Lv3 · 도서 관리 시스템
Book/Member/Library 클래스로 도서 등록·대출·반납·검색을 구현. dataclass 와 JSON 직렬화까지 통합합니다.
OOPdataclassJSON
소요 시간
⏱ 5~8시간
난이도
📊 중급 종합
선수 조건
🎯 중급 6-8주차 (파일 I/O, OOP)
결과물
OOP 도메인 모델링과 영속화
이 강의에서 배우는 것
- 1Book / Member / Library 도메인 분리
- 2asdict 로 dataclass → JSON
- 3대출·반납 시 양쪽 상태 일관성
도메인
- Book(title, author, isbn, is_borrowed, borrower)
- Member(member_id, name, borrowed: list[str])
- Library: 두 컬렉션 + load/save
명령
- book add / book list
- member add
- borrow <isbn> <member_id>
- return <isbn>
- search <키워드>
도전 과제
- 대출 기간/연체 일수 (datetime)
- 회원별 대출 이력
채점 체크리스트
- 3개 클래스 분리
- JSON 영구 저장
- 대출 일관성
💻 예제 (examples)
실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.
solution.py— 전체 동작 코드 (OOP 버전)
CODE
"""도서 관리 — OOP + JSON"""
import json
from pathlib import Path
from dataclasses import dataclass, asdict, field
from typing import Optional
DB = Path("library.json")
@dataclass
class Book:
title: str
author: str
isbn: str
is_borrowed: bool = False
borrower: Optional[str] = None
@dataclass
class Member:
member_id: str
name: str
borrowed: list[str] = field(default_factory=list)
class Library:
def __init__(self):
self.books: list[Book] = []
self.members: list[Member] = []
self.load()
def load(self):
if not DB.exists(): return
data = json.loads(DB.read_text(encoding="utf-8"))
self.books = [Book(**b) for b in data.get("books", [])]
self.members = [Member(**m) for m in data.get("members", [])]
def save(self):
DB.write_text(json.dumps({
"books": [asdict(b) for b in self.books],
"members": [asdict(m) for m in self.members],
}, ensure_ascii=False, indent=2), encoding="utf-8")
def add_book(self, title, author, isbn):
self.books.append(Book(title, author, isbn))
self.save()
def add_member(self, mid, name):
self.members.append(Member(mid, name))
self.save()
def borrow(self, isbn, member_id):
book = next((b for b in self.books if b.isbn == isbn and not b.is_borrowed), None)
member = next((m for m in self.members if m.member_id == member_id), None)
if not book: raise ValueError(f"대출 불가: {isbn}")
if not member: raise ValueError(f"회원 없음: {member_id}")
book.is_borrowed = True
book.borrower = member_id
member.borrowed.append(isbn)
self.save()
def return_book(self, isbn):
book = next((b for b in self.books if b.isbn == isbn and b.is_borrowed), None)
if not book: raise ValueError(f"반납 불가: {isbn}")
member = next((m for m in self.members if m.member_id == book.borrower), None)
if member: member.borrowed.remove(isbn)
book.is_borrowed = False
book.borrower = None
self.save()
def search(self, keyword):
return [b for b in self.books if keyword.lower() in b.title.lower() or keyword.lower() in b.author.lower()]
def main():
lib = Library()
while True:
raw = input("\n> ").strip()
if not raw: continue
if raw == "quit": break
try:
parts = raw.split()
cmd = parts[0]
if cmd == "book" and parts[1] == "add":
t = input("제목: "); a = input("저자: "); i = input("ISBN: ")
lib.add_book(t, a, i); print("등록됨")
elif cmd == "book" and parts[1] == "list":
for b in lib.books:
status = f"대출중({b.borrower})" if b.is_borrowed else "보유"
print(f" [{status}] {b.title} / {b.author} / {b.isbn}")
elif cmd == "member" and parts[1] == "add":
m = input("ID: "); n = input("이름: ")
lib.add_member(m, n); print("등록됨")
elif cmd == "borrow":
lib.borrow(parts[1], parts[2]); print("대출 완료")
elif cmd == "return":
lib.return_book(parts[1]); print("반납 완료")
elif cmd == "search":
results = lib.search(parts[1])
for b in results:
print(f" {b.title} / {b.author}")
except (IndexError, ValueError) as e:
print(f"에러: {e}")
if __name__ == "__main__":
main()▶ 실행 결과
> book add
제목: 파이썬
저자: 홍길동
ISBN: 978-0-1
등록됨
> member add
ID: M001
이름: Alice
등록됨
> borrow 978-0-1 M001
대출 완료
> book list
[대출중(M001)] 파이썬 / 홍길동 / 978-0-1📝 과제 (exercises)
직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.
과제 1
프로젝트 구현
목표: 위 사양대로 직접 작성한다.
요구사항
- 3개 dataclass/class
- JSON load/save
- borrow/return 일관성
💡 힌트
next((b for b in ... if ...), None)
입출력 예시
대출 완료채점
- · OOP 분리
- · 예외 처리
▶정답 코드 펼치기 / 접기
SOLUTION
# 위 solution.py 와 동일▶ 실행 결과
(예제 참고)