← 파이썬 강의 목록으로
📖
실습 프로젝트 (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 와 동일
▶ 실행 결과
(예제 참고)
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗