[오늘의 문제] 바이브 코딩 미션

shlim55·2025년 11월 9일

코딩테스트

목록 보기
171/223

미션: 도서 관리 시스템 구축
*개요
최종 목표: 도서관의 책 대여 및 반납을 관리하는 시스템을 구축하고,
대여 통계를 파일로 저장하는 프로그램을 완성합니다.
핵심 요구사항:
➢ 책 정보를 담는 클래스 구조 설계
➢ 대여/반납 기능 구현
➢ 사용자 입력 처리 및 예외 처리
➢ 대여 기록을 파일로 저장하고 불러오기
➢ 통계 정보 출력 기능
제공 데이터:
도서 목록:
1. 파이썬 완전정복 (저자: 김코딩, ISBN: 9781234567890, 재고: 3권)
2. 데이터 과학 입문 (저자: 이데이터, ISBN: 9781234567891, 재고: 2권)
3. 웹 개발의 모든 것 (저자: 박웹개, ISBN: 9781234567892, 재고: 5권)
4. AI 프로그래밍 (저자: 최인공, ISBN: 9781234567893, 재고: 1권)
5. 알고리즘 기초 (저자: 정알고, ISBN: 9781234567894, 재고: 4권)
(필요에 따라 자유롭게 설정)
미션 1: 데이터 구조 설계하기
도서 정보를 효율적으로 관리하기 위한 데이터 구조를 설계하세요.
요구사항:
➢ 책의 제목, 저자, ISBN, 재고 수량을 저장할 수 있어야 함
➢ Book 클래스를 설계
➢ 최소 5권의 책 정보를 저장하는 리스트 생성
➢ 책 정보를 출력하는 함수 작성
미션 2: 대여 및 반납 기능 구현하기
도서 대여와 반납 기능을 구현하세요.
요구사항:
➢ 책 제목으로 대여할 수 있는 borrow_book() 함수
➢ 재고가 0이면 대여 불가 처리
➢ 대여 시 재고 감소, 반납 시 재고 증가
➢ return_book() 함수 구현
➢ 예외 처리: 존재하지 않는 책 제목 입력 시
힌트:
➢ 리스트에서 특정 책을 찾는 방법 고려
➢ 조건문으로 재고 확인
➢ try-except로 예외 상황 처리
미션 3: 대여 기록 관리하기
대여 기록을 저장하고 관리하는 기능을 추가하세요.
요구사항:
➢ 대여 기록을 저장하는 리스트 또는 딕셔너리 생성
➢ 대여 시 기록 추가 (책 제목, 대여 날짜/시간)
➢ 대여 기록을 조회하는 함수
➢ datetime 모듈 활용
힌트:
➢ datetime 모듈의 datetime.now() 사용
➢ 딕셔너리로 {'title': '책제목', 'date': '날짜'} 형태 저장
➢ 리스트에 여러 대여 기록 딕셔너리 추가
미션 4: 파일 저장 및 불러오기
대여 기록과 현재 재고 상태를 파일로 저장하고 불러오는 기능을 구현하세요.
요구사항:
➢ 대여 기록을 텍스트 파일로 저장
➢ 현재 책 재고 상태를 파일로 저장
➢ 프로그램 시작 시 파일에서 데이터 불러오기
➢ 파일이 없을 때 예외 처리
힌트:
➢ open() 함수와 with 문 사용
➢ FileNotFoundError 예외 처리
➢ 문자열 포맷팅으로 데이터 정리
미션 5: 메뉴 시스템 및 통계 기능
사용자가 쉽게 사용할 수 있는 메뉴 시스템과 통계 기능을 구현하세요.
요구사항:
➢ 반복문을 사용한 메뉴 시스템
➢ 도서 목록 보기, 2. 대여하기, 3. 반납하기, 4. 대여 기록, 5. 통계 보기, 6.
종료
➢ 통계: 총 대여 횟수, 가장 많이 대여된 책
➢ 잘못된 입력에 대한 예외 처리
힌트:
➢ while True 반복문과 break 사용
➢ 딕셔너리로 책별 대여 횟수 카운트
➢ max() 함수로 최대값 찾기
추가 도전 과제!

  • 회원 관리 기능: 대여자 정보를 저장하고 관리
    회원 정보에 대한 클래스 Member 정의
    회원 등록, 조회, 목록 기능
    회원 기반 대여 및 반납 기능 구현
    회원 정보 기반 대여 기록 관리하기
  • 연체 확인: 대여일로부터 7일 이상 지난 책 찾기
  • 검색 기능: 저자나 제목으로 책 검색

내가 작성한 프롬프트

넌 파이썬을 가르치는 선생님이야 도서관의 책 대여 및 반납을 관리하는 시스템을 구축을 해야 해.

요구사항
	1. 클래스: 책 정보를 담는 클래스 구현
	2. 기능: 대여 반납기능구현
	3. 사용자 입력 처리 input, 예외처리 
	4. 대여기록을 파일로 저장 및 불러오기
	5. 통계 정보 출력 기능

제공 데이터: 

도서 목록: 
1. 파이썬 완전정복 (저자: 김코딩, ISBN: 9781234567890, 재고: 3권) 
2. 데이터 과학 입문 (저자: 이데이터, ISBN: 9781234567891, 재고: 2권) 
3. 웹 개발의 모든 것 (저자: 박웹개, ISBN: 9781234567892, 재고: 5권) 
4. AI 프로그래밍 (저자: 최인공, ISBN: 9781234567893, 재고: 1권) 
5. 알고리즘 기초 (저자: 정알고, ISBN: 9781234567894, 재고: 4권)

으로 각 딕셔너리를 리스트에 데이터 저장해줘

각 단계 별로 진행해야 해
	1. 데이터 구조 설계하기 
	요구사항:
	   1) 책 제목, 저자, ISBN, 재고수량을 딕셔너리 형태로 책정보 저장
		2) Book클래스 설계
		3) 리스트는 최소 5권의 책 정보를 저장 
		4) 책 정보 출력함수 생성

	2. 대여 및 반납 기능 구현하기 
	요구사항:
		1) 책 제목으로 대여하는 함수 borrow_book생성
		2) 재고가 0일시 대출 불가 if문으로 충족할시 print("대출 불가합니다.")
		3) 대여시 재고감소, 반납시 재고 증가하게끔 구현 
		4) 반납 함수 return_book
		5) 예외 처리: 존재하지 않는 책 제목 입력시 예외 발생

	3. 대여 기록 관리하기 
	요구사항:
		1) 대여기록을 저장하는 리스트 생성 리스트안에 딕셔너리 저장하는 형태[{},{},{}]
		2) 대여 시 리스트에 대출 정보 추가 (책, 제목, 대여 날짜/시간)
		3) 대여 기록을 조회하는 select_borrow함수 추가
		4) Datetime모듈 활용  
	
	4. 파일 저장 및 불러오기
	대여 기록과 현재 재고 상태를 파일로 저장및 출력기능 구현
	요구사항:
		1) 대여 기록을 텍스트 파일로 저장 책, 제목, 대여 날짜.시간
		2) 현재 책 재고 상태를 파일 저장 파이썬 완전정복 (저자: 김코딩, ISBN: 9781234567890, 재고: 3권) 
		3) 프로그램 시작 시 파일에서 데이터 불러오기 open(), with문사용
		4) 파일 없을시 예외처리 
	
	5. 메뉴 시스템 및 통계 기능
	사용자가 쉽게 사용할 수 있는 메뉴 시스템과 통계 기능을 구현하세요.
	요구사항:
			1) While True문을 활용한 도서 메뉴선택
			2) 도서 목록 보기, 2. 대여하기, 3.반납하기, 4.대여기록, 5.통계 보기, 6. 종료
			3) 통계: 총 대여 횟수, 가장 많이 대여된 책 max함수로 추출 (제목만)
			4) 숫자 입력 잘못시 예외처리

제미나이가 만든 결과물

import datetime
from collections import Counter

class Book:
    """책 정보를 담는 클래스"""
    def __init__(self, title, author, isbn, stock):
        self.title = title
        self.author = author
        self.isbn = isbn
        # 재고 수량은 정수형으로 저장
        self.stock = int(stock)

    def __str__(self):
        """책 정보를 보기 좋게 출력하는 메서드"""
        return f"제목: {self.title}, 저자: {self.author}, ISBN: {self.isbn}, 재고: {self.stock}권"

# 초기 도서 목록 데이터 (딕셔너리 리스트)
initial_books_data = [
    {"title": "파이썬 완전정복", "author": "김코딩", "isbn": "9781234567890", "stock": 3},
    {"title": "데이터 과학 입문", "author": "이데이터", "isbn": "9781234567891", "stock": 2},
    {"title": "웹 개발의 모든 것", "author": "박웹개", "isbn": "9781234567892", "stock": 5},
    {"title": "AI 프로그래밍", "author": "최인공", "isbn": "9781234567893", "stock": 1},
    {"title": "알고리즘 기초", "author": "정알고", "isbn": "9781234567894", "stock": 4},
]

# Book 객체를 저장할 리스트
books = []

def display_books():
    """현재 도서 목록 정보를 출력하는 함수"""
    if not books:
        print("도서 목록이 비어 있습니다.")
        return

    print("\n📚 **현재 도서 목록** 📚")
    print("-" * 30)
    for book in books:
        # Book 클래스의 __str__ 메서드를 활용
        print(book)
    print("-" * 30)

# 초기 Book 객체 생성 및 리스트에 저장
# 이 과정은 4단계에서 파일 불러오기로 대체될 수 있지만, 초기 구동을 위해 여기서 실행합니다.
for data in initial_books_data:
    book = Book(data["title"], data["author"], data["isbn"], data["stock"])
    books.append(book)

# display_books()

def borrow_book(title):
    """
    책 제목으로 책을 대여하는 함수.
    대여 성공 시 해당 Book 객체를 반환, 실패 시 None 반환.
    """
    try:
        # 책 제목으로 목록에서 책을 찾습니다.
        book_to_borrow = next(book for book in books if book.title == title)
    except StopIteration:
        # 존재하지 않는 책 제목 입력 시 예외 처리
        raise ValueError(f"'{title}' 책은 도서 목록에 존재하지 않습니다.")

    # 재고 확인
    if book_to_borrow.stock > 0:
        book_to_borrow.stock -= 1
        print(f"✅ '{title}' 책이 대여되었습니다. 남은 재고: {book_to_borrow.stock}권")
        return book_to_borrow
    else:
        # 재고가 0일 경우 대출 불가
        print(f"❌ '{title}' 책은 현재 **대출 불가**합니다. (재고: 0)")
        return None

### **반납 함수 (`return_book`)**

def return_book(title):
    """책 제목으로 책을 반납하고 재고를 증가시키는 함수"""
    try:
        # 책 제목으로 목록에서 책을 찾습니다.
        book_to_return = next(book for book in books if book.title == title)
    except StopIteration:
        # 존재하지 않는 책 제목 입력 시 예외 처리
        raise ValueError(f"'{title}' 책은 도서 목록에 존재하지 않습니다.")

    # 재고 증가
    book_to_return.stock += 1
    print(f"✅ '{title}' 책이 반납되었습니다. 현재 재고: {book_to_return.stock}권")
    return book_to_return



# 대여 기록을 저장할 리스트. [{}, {}, ...] 형태
borrow_records = []

def record_borrow(book):
    """대여 성공 시 대여 기록 리스트에 정보를 추가하는 함수"""
    # 현재 날짜와 시간을 'YYYY-MM-DD HH:MM:SS' 형식의 문자열로 저장
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    record = {
        "title": book.title,
        "isbn": book.isbn,
        "borrow_time": now
    }
    borrow_records.append(record)
    print(f"⭐ 대여 기록이 저장되었습니다. (대여 시각: {now})")

def select_borrow():
    """대여 기록을 조회하고 출력하는 함수"""
    if not borrow_records:
        print("\n📝 **현재 대여 기록이 없습니다.**")
        return

    print("\n📝 **전체 도서 대여 기록** 📝")
    print("-" * 50)
    for record in borrow_records:
        print(f"책 제목: {record['title']}, ISBN: {record['isbn']}, 대여 시각: {record['borrow_time']}")
    print("-" * 50)

BORROW_RECORDS_FILE = "borrow_records.txt"
BOOK_STOCK_FILE = "book_stock.txt"    

def save_data():
    """대여 기록과 현재 재고 상태를 파일로 저장하는 함수"""
    print("\n💾 **데이터 저장 중...**")

    # 1. 대여 기록 저장 (title, isbn, borrow_time)
    try:
        with open(BORROW_RECORDS_FILE, 'w', encoding='utf-8') as f:
            for record in borrow_records:
                # 콤마(,)로 구분하여 한 줄에 저장
                line = f"{record['title']},{record['isbn']},{record['borrow_time']}\n"
                f.write(line)
        print(f"✅ 대여 기록이 '{BORROW_RECORDS_FILE}'에 저장되었습니다.")
    except Exception as e:
        print(f"❌ 대여 기록 저장 중 오류 발생: {e}")

    # 2. 현재 책 재고 상태 저장 (title, author, isbn, stock)
    try:
        with open(BOOK_STOCK_FILE, 'w', encoding='utf-8') as f:
            for book in books:
                # 콤마(,)로 구분하여 한 줄에 저장
                line = f"{book.title},{book.author},{book.isbn},{book.stock}\n"
                f.write(line)
        print(f"✅ 책 재고 상태가 '{BOOK_STOCK_FILE}'에 저장되었습니다.")
    except Exception as e:
        print(f"❌ 책 재고 저장 중 오류 발생: {e}")


def load_data():
    """파일에서 대여 기록과 책 재고 상태를 불러오는 함수"""
    print("\n📥 **데이터 불러오는 중...**")
    global books
    global borrow_records

    # 1. 책 재고 상태 불러오기
    try:
        books.clear() # 기존 목록 초기화
        with open(BOOK_STOCK_FILE, 'r', encoding='utf-8') as f:
            for line in f:
                # 각 줄을 콤마(,)로 분리
                title, author, isbn, stock = line.strip().split(',')
                # Book 객체를 생성하여 books 리스트에 추가
                books.append(Book(title, author, isbn, int(stock)))
        print(f"✅ 책 재고 상태를 '{BOOK_STOCK_FILE}'에서 불러왔습니다.")
    except FileNotFoundError:
        # 파일이 없을 경우 예외 처리
        print(f"⚠️ '{BOOK_STOCK_FILE}' 파일이 없어 초기 데이터를 사용합니다.")
        # 초기 데이터를 다시 로드 (4단계 이전에 이미 로드되어 있으므로 생략 가능하나, 안전을 위해 구조화)
    except Exception as e:
        print(f"❌ 책 재고 불러오기 중 오류 발생: {e}")


    # 2. 대여 기록 불러오기
    try:
        borrow_records.clear() # 기존 기록 초기화
        with open(BORROW_RECORDS_FILE, 'r', encoding='utf-8') as f:
            for line in f:
                # 각 줄을 콤마(,)로 분리
                title, isbn, borrow_time = line.strip().split(',')
                # 딕셔너리 형태로 borrow_records 리스트에 추가
                borrow_records.append({
                    "title": title,
                    "isbn": isbn,
                    "borrow_time": borrow_time
                })
        print(f"✅ 대여 기록을 '{BORROW_RECORDS_FILE}'에서 불러왔습니다.")
    except FileNotFoundError:
        # 파일이 없을 경우 예외 처리
        print(f"⚠️ '{BORROW_RECORDS_FILE}' 파일이 없어 새로운 대여 기록을 시작합니다.")
    except Exception as e:
        print(f"❌ 대여 기록 불러오기 중 오류 발생: {e}")

# 프로그램 시작 시 데이터 불러오기 (초기 데이터 로드 후에 실행하여 기존 데이터를 덮어씀)
# load_data()



def show_statistics():
    """총 대여 횟수 및 가장 많이 대여된 책을 출력하는 함수"""
    print("\n📊 **도서관 통계 정보** 📊")
    print("-" * 30)

    # 1. 총 대여 횟수
    total_borrows = len(borrow_records)
    print(f"**총 대여 횟수:** {total_borrows}회")

    # 2. 가장 많이 대여된 책 (제목만)
    if borrow_records:
        # 대여 기록에서 모든 책 제목을 추출
        titles = [record['title'] for record in borrow_records]
        # Counter를 사용하여 각 책 제목의 등장 횟수(대여 횟수)를 계산
        borrow_counts = Counter(titles)

        # 가장 많이 대여된 책의 제목과 횟수를 추출
        most_borrowed = borrow_counts.most_common(1) # 가장 흔한 항목 1개를 리스트로 반환
        
        # most_borrowed는 [('책 제목', 횟수)] 형태
        most_title = most_borrowed[0][0]
        most_count = most_borrowed[0][1]

        print(f"**가장 많이 대여된 책:** '{most_title}' ({most_count}회)")
    else:
        print("아직 대여 기록이 없어 통계를 계산할 수 없습니다.")
    
    print("-" * 30)        

def main():
    """메인 메뉴 시스템을 구동하는 함수"""
    
    # 4단계: 프로그램 시작 시 데이터 불러오기
    # 초기 데이터를 Book 객체로 변환하는 과정은 load_data() 내에서 처리됩니다.
    load_data() 
    
    # 만약 파일이 없어 초기 데이터를 사용했다면, books 리스트가 비어있을 수 있으므로 다시 채웁니다.
    # (실습의 일관성을 위해 1단계에서 이미 채웠다면 이 부분은 건너뛰어도 됩니다.)
    if not books:
        for data in initial_books_data:
            books.append(Book(data["title"], data["author"], data["isbn"], data["stock"]))


    while True:
        print("\n*** 📖 도서 관리 시스템 메뉴 📖 ***")
        print("1. 도서 목록 보기")
        print("2. 대여하기")
        print("3. 반납하기")
        print("4. 대여 기록 조회")
        print("5. 통계 보기")
        print("6. 종료")
        print("-" * 30)

        choice = input("메뉴를 선택하세요 (1-6): ").strip()

        try:
            if choice == '1':
                display_books()
            
            elif choice == '2':
                # 대여
                title = input("대여할 책 제목을 입력하세요: ").strip()
                borrowed_book = borrow_book(title)
                if borrowed_book:
                    record_borrow(borrowed_book) # 대여 성공 시 기록 추가

            elif choice == '3':
                # 반납
                title = input("반납할 책 제목을 입력하세요: ").strip()
                return_book(title)
            
            elif choice == '4':
                # 대여 기록 조회
                select_borrow()
            
            elif choice == '5':
                # 통계 보기
                show_statistics()
            
            elif choice == '6':
                # 종료 시 파일 저장
                print("\n프로그램을 종료합니다. 현재 데이터를 저장합니다.")
                save_data()
                break
            
            else:
                # 숫자 입력 잘못시 예외처리 (여기서는 범위 외의 문자를 포함하여 처리)
                print("⚠️ **오류:** 1부터 6 사이의 유효한 메뉴 번호를 입력해 주세요.")
        
        except ValueError as e:
            # 존재하지 않는 책 제목 입력 등 사용자 입력 관련 예외 처리
            print(f"❌ **사용자 입력 오류:** {e}")
        except Exception as e:
            # 기타 예상치 못한 오류 처리
            print(f"❌ **시스템 오류 발생:** {e}")

# 프로그램 실행
if __name__ == "__main__":
    main()import datetime
from collections import Counter

class Book:
    """책 정보를 담는 클래스"""
    def __init__(self, title, author, isbn, stock):
        self.title = title
        self.author = author
        self.isbn = isbn
        # 재고 수량은 정수형으로 저장
        self.stock = int(stock)

    def __str__(self):
        """책 정보를 보기 좋게 출력하는 메서드"""
        return f"제목: {self.title}, 저자: {self.author}, ISBN: {self.isbn}, 재고: {self.stock}권"

# 초기 도서 목록 데이터 (딕셔너리 리스트)
initial_books_data = [
    {"title": "파이썬 완전정복", "author": "김코딩", "isbn": "9781234567890", "stock": 3},
    {"title": "데이터 과학 입문", "author": "이데이터", "isbn": "9781234567891", "stock": 2},
    {"title": "웹 개발의 모든 것", "author": "박웹개", "isbn": "9781234567892", "stock": 5},
    {"title": "AI 프로그래밍", "author": "최인공", "isbn": "9781234567893", "stock": 1},
    {"title": "알고리즘 기초", "author": "정알고", "isbn": "9781234567894", "stock": 4},
]

# Book 객체를 저장할 리스트
books = []

def display_books():
    """현재 도서 목록 정보를 출력하는 함수"""
    if not books:
        print("도서 목록이 비어 있습니다.")
        return

    print("\n📚 **현재 도서 목록** 📚")
    print("-" * 30)
    for book in books:
        # Book 클래스의 __str__ 메서드를 활용
        print(book)
    print("-" * 30)

# 초기 Book 객체 생성 및 리스트에 저장
# 이 과정은 4단계에서 파일 불러오기로 대체될 수 있지만, 초기 구동을 위해 여기서 실행합니다.
for data in initial_books_data:
    book = Book(data["title"], data["author"], data["isbn"], data["stock"])
    books.append(book)

# display_books()

def borrow_book(title):
    """
    책 제목으로 책을 대여하는 함수.
    대여 성공 시 해당 Book 객체를 반환, 실패 시 None 반환.
    """
    try:
        # 책 제목으로 목록에서 책을 찾습니다.
        book_to_borrow = next(book for book in books if book.title == title)
    except StopIteration:
        # 존재하지 않는 책 제목 입력 시 예외 처리
        raise ValueError(f"'{title}' 책은 도서 목록에 존재하지 않습니다.")

    # 재고 확인
    if book_to_borrow.stock > 0:
        book_to_borrow.stock -= 1
        print(f"✅ '{title}' 책이 대여되었습니다. 남은 재고: {book_to_borrow.stock}권")
        return book_to_borrow
    else:
        # 재고가 0일 경우 대출 불가
        print(f"❌ '{title}' 책은 현재 **대출 불가**합니다. (재고: 0)")
        return None

### **반납 함수 (`return_book`)**

def return_book(title):
    """책 제목으로 책을 반납하고 재고를 증가시키는 함수"""
    try:
        # 책 제목으로 목록에서 책을 찾습니다.
        book_to_return = next(book for book in books if book.title == title)
    except StopIteration:
        # 존재하지 않는 책 제목 입력 시 예외 처리
        raise ValueError(f"'{title}' 책은 도서 목록에 존재하지 않습니다.")

    # 재고 증가
    book_to_return.stock += 1
    print(f"✅ '{title}' 책이 반납되었습니다. 현재 재고: {book_to_return.stock}권")
    return book_to_return



# 대여 기록을 저장할 리스트. [{}, {}, ...] 형태
borrow_records = []

def record_borrow(book):
    """대여 성공 시 대여 기록 리스트에 정보를 추가하는 함수"""
    # 현재 날짜와 시간을 'YYYY-MM-DD HH:MM:SS' 형식의 문자열로 저장
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    record = {
        "title": book.title,
        "isbn": book.isbn,
        "borrow_time": now
    }
    borrow_records.append(record)
    print(f"⭐ 대여 기록이 저장되었습니다. (대여 시각: {now})")

def select_borrow():
    """대여 기록을 조회하고 출력하는 함수"""
    if not borrow_records:
        print("\n📝 **현재 대여 기록이 없습니다.**")
        return

    print("\n📝 **전체 도서 대여 기록** 📝")
    print("-" * 50)
    for record in borrow_records:
        print(f"책 제목: {record['title']}, ISBN: {record['isbn']}, 대여 시각: {record['borrow_time']}")
    print("-" * 50)

BORROW_RECORDS_FILE = "borrow_records.txt"
BOOK_STOCK_FILE = "book_stock.txt"    

def save_data():
    """대여 기록과 현재 재고 상태를 파일로 저장하는 함수"""
    print("\n💾 **데이터 저장 중...**")

    # 1. 대여 기록 저장 (title, isbn, borrow_time)
    try:
        with open(BORROW_RECORDS_FILE, 'w', encoding='utf-8') as f:
            for record in borrow_records:
                # 콤마(,)로 구분하여 한 줄에 저장
                line = f"{record['title']},{record['isbn']},{record['borrow_time']}\n"
                f.write(line)
        print(f"✅ 대여 기록이 '{BORROW_RECORDS_FILE}'에 저장되었습니다.")
    except Exception as e:
        print(f"❌ 대여 기록 저장 중 오류 발생: {e}")

    # 2. 현재 책 재고 상태 저장 (title, author, isbn, stock)
    try:
        with open(BOOK_STOCK_FILE, 'w', encoding='utf-8') as f:
            for book in books:
                # 콤마(,)로 구분하여 한 줄에 저장
                line = f"{book.title},{book.author},{book.isbn},{book.stock}\n"
                f.write(line)
        print(f"✅ 책 재고 상태가 '{BOOK_STOCK_FILE}'에 저장되었습니다.")
    except Exception as e:
        print(f"❌ 책 재고 저장 중 오류 발생: {e}")


def load_data():
    """파일에서 대여 기록과 책 재고 상태를 불러오는 함수"""
    print("\n📥 **데이터 불러오는 중...**")
    global books
    global borrow_records

    # 1. 책 재고 상태 불러오기
    try:
        books.clear() # 기존 목록 초기화
        with open(BOOK_STOCK_FILE, 'r', encoding='utf-8') as f:
            for line in f:
                # 각 줄을 콤마(,)로 분리
                title, author, isbn, stock = line.strip().split(',')
                # Book 객체를 생성하여 books 리스트에 추가
                books.append(Book(title, author, isbn, int(stock)))
        print(f"✅ 책 재고 상태를 '{BOOK_STOCK_FILE}'에서 불러왔습니다.")
    except FileNotFoundError:
        # 파일이 없을 경우 예외 처리
        print(f"⚠️ '{BOOK_STOCK_FILE}' 파일이 없어 초기 데이터를 사용합니다.")
        # 초기 데이터를 다시 로드 (4단계 이전에 이미 로드되어 있으므로 생략 가능하나, 안전을 위해 구조화)
    except Exception as e:
        print(f"❌ 책 재고 불러오기 중 오류 발생: {e}")


    # 2. 대여 기록 불러오기
    try:
        borrow_records.clear() # 기존 기록 초기화
        with open(BORROW_RECORDS_FILE, 'r', encoding='utf-8') as f:
            for line in f:
                # 각 줄을 콤마(,)로 분리
                title, isbn, borrow_time = line.strip().split(',')
                # 딕셔너리 형태로 borrow_records 리스트에 추가
                borrow_records.append({
                    "title": title,
                    "isbn": isbn,
                    "borrow_time": borrow_time
                })
        print(f"✅ 대여 기록을 '{BORROW_RECORDS_FILE}'에서 불러왔습니다.")
    except FileNotFoundError:
        # 파일이 없을 경우 예외 처리
        print(f"⚠️ '{BORROW_RECORDS_FILE}' 파일이 없어 새로운 대여 기록을 시작합니다.")
    except Exception as e:
        print(f"❌ 대여 기록 불러오기 중 오류 발생: {e}")

# 프로그램 시작 시 데이터 불러오기 (초기 데이터 로드 후에 실행하여 기존 데이터를 덮어씀)
# load_data()



def show_statistics():
    """총 대여 횟수 및 가장 많이 대여된 책을 출력하는 함수"""
    print("\n📊 **도서관 통계 정보** 📊")
    print("-" * 30)

    # 1. 총 대여 횟수
    total_borrows = len(borrow_records)
    print(f"**총 대여 횟수:** {total_borrows}회")

    # 2. 가장 많이 대여된 책 (제목만)
    if borrow_records:
        # 대여 기록에서 모든 책 제목을 추출
        titles = [record['title'] for record in borrow_records]
        # Counter를 사용하여 각 책 제목의 등장 횟수(대여 횟수)를 계산
        borrow_counts = Counter(titles)

        # 가장 많이 대여된 책의 제목과 횟수를 추출
        most_borrowed = borrow_counts.most_common(1) # 가장 흔한 항목 1개를 리스트로 반환
        
        # most_borrowed는 [('책 제목', 횟수)] 형태
        most_title = most_borrowed[0][0]
        most_count = most_borrowed[0][1]

        print(f"**가장 많이 대여된 책:** '{most_title}' ({most_count}회)")
    else:
        print("아직 대여 기록이 없어 통계를 계산할 수 없습니다.")
    
    print("-" * 30)        

def main():
    """메인 메뉴 시스템을 구동하는 함수"""
    
    # 4단계: 프로그램 시작 시 데이터 불러오기
    # 초기 데이터를 Book 객체로 변환하는 과정은 load_data() 내에서 처리됩니다.
    load_data() 
    
    # 만약 파일이 없어 초기 데이터를 사용했다면, books 리스트가 비어있을 수 있으므로 다시 채웁니다.
    # (실습의 일관성을 위해 1단계에서 이미 채웠다면 이 부분은 건너뛰어도 됩니다.)
    if not books:
        for data in initial_books_data:
            books.append(Book(data["title"], data["author"], data["isbn"], data["stock"]))


    while True:
        print("\n*** 📖 도서 관리 시스템 메뉴 📖 ***")
        print("1. 도서 목록 보기")
        print("2. 대여하기")
        print("3. 반납하기")
        print("4. 대여 기록 조회")
        print("5. 통계 보기")
        print("6. 종료")
        print("-" * 30)

        choice = input("메뉴를 선택하세요 (1-6): ").strip()

        try:
            if choice == '1':
                display_books()
            
            elif choice == '2':
                # 대여
                title = input("대여할 책 제목을 입력하세요: ").strip()
                borrowed_book = borrow_book(title)
                if borrowed_book:
                    record_borrow(borrowed_book) # 대여 성공 시 기록 추가

            elif choice == '3':
                # 반납
                title = input("반납할 책 제목을 입력하세요: ").strip()
                return_book(title)
            
            elif choice == '4':
                # 대여 기록 조회
                select_borrow()
            
            elif choice == '5':
                # 통계 보기
                show_statistics()
            
            elif choice == '6':
                # 종료 시 파일 저장
                print("\n프로그램을 종료합니다. 현재 데이터를 저장합니다.")
                save_data()
                break
            
            else:
                # 숫자 입력 잘못시 예외처리 (여기서는 범위 외의 문자를 포함하여 처리)
                print("⚠️ **오류:** 1부터 6 사이의 유효한 메뉴 번호를 입력해 주세요.")
        
        except ValueError as e:
            # 존재하지 않는 책 제목 입력 등 사용자 입력 관련 예외 처리
            print(f"❌ **사용자 입력 오류:** {e}")
        except Exception as e:
            # 기타 예상치 못한 오류 처리
            print(f"❌ **시스템 오류 발생:** {e}")

# 프로그램 실행
if __name__ == "__main__":
    main()

추가 도전 과제!

  • 회원 관리 기능: 대여자 정보를 저장하고 관리
    회원 정보에 대한 클래스 Member 정의
    회원 등록, 조회, 목록 기능
    회원 기반 대여 및 반납 기능 구현
    회원 정보 기반 대여 기록 관리하기
  • 연체 확인: 대여일로부터 7일 이상 지난 책 찾기
  • 검색 기능: 저자나 제목으로 책 검색

내가 작성한 프롬프트

자 그럼 여기서 요구사항이 더 추가 될거야. 
1. 회원관리 기능 - 대여자 정보를 저장및 관리하는 기능
2. 회원 정보에 대한 클래스 Member 정의 
3. 회원 등록, 조회, 목록 기능
4. 회원 기반 대여 및 반납기능
5. 회원 정보 기반 대여 기록 관리하기 
6. * 연체 확인: 대여일로부터 7일 이상 지난 책 찾기 
7. * 검색 기능: 저자나 제목으로 책 검색

제미나이가 만든 결과물

import datetime
import os
from collections import Counter
import random # 임시로 회원 ID를 생성하기 위해 사용

# --- 파일 이름 정의 ---
BORROW_RECORDS_FILE = "borrow_records.txt"
BOOK_STOCK_FILE = "book_stock.txt"
MEMBER_RECORDS_FILE = "member_records.txt"

# --- 글로벌 데이터 구조 ---
books = []        # Book 객체 리스트
borrow_records = [] # 대여 기록 리스트 (딕셔너리)
members = []      # Member 객체 리스트
next_member_id = 1 # 다음 회원 ID 생성을 위한 전역 변수

# --- 1. 클래스 정의 ---

class Book:
    """책 정보를 담는 클래스"""
    def __init__(self, title, author, isbn, stock):
        self.title = title
        self.author = author
        self.isbn = isbn
        self.stock = int(stock)

    def __str__(self):
        return f"제목: {self.title}, 저자: {self.author}, ISBN: {self.isbn}, 재고: {self.stock}권"

class Member:
    """회원 정보를 담는 클래스"""
    def __init__(self, member_id, name, contact):
        self.member_id = int(member_id)
        self.name = name
        self.contact = contact

    def __str__(self):
        return f"ID: {self.member_id:04d}, 이름: {self.name}, 연락처: {self.contact}"

# 초기 도서 목록 데이터 (이 데이터는 파일이 없을 경우에만 사용됩니다.)
initial_books_data = [
    {"title": "파이썬 완전정복", "author": "김코딩", "isbn": "9781234567890", "stock": 3},
    {"title": "데이터 과학 입문", "author": "이데이터", "isbn": "9781234567891", "stock": 2},
    {"title": "웹 개발의 모든 것", "author": "박웹개", "isbn": "9781234567892", "stock": 5},
    {"title": "AI 프로그래밍", "author": "최인공", "isbn": "9781234567893", "stock": 1},
    {"title": "알고리즘 기초", "author": "정알고", "isbn": "9781234567894", "stock": 4},
]


# --- 2. 헬퍼 함수 (조회용) ---

def get_member_by_id(member_id):
    """ID로 Member 객체를 찾는 헬퍼 함수"""
    try:
        return next(m for m in members if m.member_id == int(member_id))
    except StopIteration:
        return None

def get_book_by_title(title):
    """제목으로 Book 객체를 찾는 헬퍼 함수"""
    try:
        return next(book for book in books if book.title == title)
    except StopIteration:
        return None

# --- 3. 회원 관리 기능 ---

def register_member():
    """새로운 회원을 등록하는 기능 (메뉴 7)"""
    global next_member_id
    print("\n👤 **새 회원 등록** 👤")
    name = input("회원 이름: ").strip()
    contact = input("연락처 (예: 010-1234-5678): ").strip()
    
    if not name or not contact:
        print("❌ 이름과 연락처는 필수 입력 사항입니다.")
        return

    # ID 할당 및 객체 생성
    new_member = Member(next_member_id, name, contact)
    members.append(new_member)
    next_member_id += 1

    print(f"✅ 회원 등록 성공! {new_member}")

def display_members():
    """전체 회원 목록을 출력하는 기능 (메뉴 8)"""
    if not members:
        print("\n👥 **등록된 회원이 없습니다.**")
        return

    print("\n👥 **전체 회원 목록** 👥")
    print("-" * 50)
    for member in members:
        print(member)
    print("-" * 50)

# --- 4. 도서 및 회원 조회 기능 ---

def display_books():
    """현재 도서 목록 정보를 출력하는 함수 (메뉴 1)"""
    if not books:
        print("도서 목록이 비어 있습니다.")
        return

    print("\n📚 **현재 도서 목록** 📚")
    print("-" * 70)
    for i, book in enumerate(books, 1):
        print(f"{i}. {book}")
    print("-" * 70)

def search_books():
    """저자나 제목으로 책을 검색하는 기능 (메뉴 9)"""
    print("\n🔍 **책 검색** 🔍")
    print("1. 제목으로 검색 | 2. 저자로 검색")
    search_type_choice = input("검색 방법을 선택하세요 (1 또는 2): ").strip()
    keyword = input("검색할 키워드를 입력하세요: ").strip().lower()

    if not keyword:
        print("❌ 검색 키워드를 입력해야 합니다.")
        return

    found_books = []
    
    if search_type_choice == '1':
        # 제목 검색
        found_books = [book for book in books if keyword in book.title.lower()]
        search_label = "제목"
    elif search_type_choice == '2':
        # 저자 검색
        found_books = [book for book in books if keyword in book.author.lower()]
        search_label = "저자"
    else:
        print("⚠️ 잘못된 검색 방법 선택입니다.")
        return

    if found_books:
        print(f"\n✅ '{keyword}' ({search_label}) 검색 결과:")
        print("-" * 70)
        for i, book in enumerate(found_books, 1):
            print(f"{i}. {book}")
        print("-" * 70)
    else:
        print(f"🔎 '{keyword}' 키워드로는 검색 결과가 없습니다.")


# --- 5. 회원 기반 대여 및 반납 기능 ---

def record_borrow(book, member_id):
    """대여 성공 시 대여 기록 리스트에 정보를 추가하는 함수"""
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    record = {
        "title": book.title,
        "isbn": book.isbn,
        "borrow_time": now,
        "member_id": member_id # 회원 ID 추가
    }
    borrow_records.append(record)
    print(f"⭐ 대여 기록이 임시 저장되었습니다. (회원 ID: {member_id}, 시각: {now})")

def borrow_book():
    """책 제목과 회원 ID를 받아 책을 대여하는 함수 (메뉴 2)"""
    print("\n➡️ **도서 대여** ➡️")
    title = input("대여할 책 제목을 입력하세요: ").strip()
    member_input = input("회원 ID를 입력하세요: ").strip()

    try:
        member_id = int(member_input)
    except ValueError:
        print("❌ 회원 ID는 숫자만 입력 가능합니다.")
        return

    book_to_borrow = get_book_by_title(title)
    member_info = get_member_by_id(member_id)

    if not book_to_borrow:
        print(f"❌ '{title}' 책은 도서 목록에 존재하지 않습니다.")
        return
    
    if not member_info:
        print(f"❌ 회원 ID '{member_id}'를 가진 회원이 존재하지 않습니다.")
        return

    if book_to_borrow.stock > 0:
        book_to_borrow.stock -= 1
        print(f"✅ '{title}' 책이 회원 {member_info.name}(ID: {member_id})님에게 대여되었습니다.")
        record_borrow(book_to_borrow, member_id)
    else:
        print(f"❌ '{title}' 책은 현재 **대출 불가**합니다. (재고: 0)")


def return_book():
    """책 제목과 회원 ID를 받아 책을 반납하는 함수 (메뉴 3)"""
    print("\n⬅️ **도서 반납** ⬅️")
    title = input("반납할 책 제목을 입력하세요: ").strip()
    member_input = input("반납하는 회원 ID를 입력하세요: ").strip()

    try:
        member_id = int(member_input)
    except ValueError:
        print("❌ 회원 ID는 숫자만 입력 가능합니다.")
        return

    book_to_return = get_book_by_title(title)
    member_info = get_member_by_id(member_id)

    if not book_to_return:
        print(f"❌ '{title}' 책은 도서 목록에 존재하지 않습니다.")
        return

    # 대여 기록에서 해당 회원이 해당 책을 대여했는지 확인
    matching_records = [
        record for record in borrow_records 
        if record['title'] == title and int(record['member_id']) == member_id
    ]

    if not matching_records:
        print(f"❌ 회원 ID {member_id}는 '{title}' 책을 대여한 기록이 없습니다.")
        return

    # 가장 최근 기록 하나만 삭제 (반납 처리)
    borrow_records.remove(matching_records[-1])

    book_to_return.stock += 1
    print(f"✅ '{title}' 책이 반납되었습니다. (회원 ID: {member_id}, 현재 재고: {book_to_return.stock}권)")


def select_borrow():
    """회원 정보 기반 대여 기록을 조회하고 출력하는 함수 (메뉴 4)"""
    if not borrow_records:
        print("\n📝 **현재 대여 기록이 없습니다.**")
        return

    print("\n📝 **전체 도서 대여 기록** 📝")
    print("-" * 80)
    for i, record in enumerate(borrow_records, 1):
        member_info = get_member_by_id(record['member_id'])
        member_name = member_info.name if member_info else "알 수 없는 회원"
        
        print(f"{i:2d}. 제목: {record['title']:20s} | 대여 시각: {record['borrow_time']} | 회원: {member_name} (ID: {record['member_id']:04d})")
    print("-" * 80)


def check_overdue():
    """대여일로부터 7일 이상 지난 연체된 책을 찾는 기능 (메뉴 6)"""
    print("\n🚨 **도서 연체 확인 (7일 초과)** 🚨")
    overdue_limit = datetime.timedelta(days=7)
    now = datetime.datetime.now()
    overdue_found = False

    print("-" * 80)
    for record in borrow_records:
        try:
            # 문자열 시간을 datetime 객체로 변환
            borrow_time = datetime.datetime.strptime(record['borrow_time'], "%Y-%m-%d %H:%M:%S")
            time_difference = now - borrow_time
            
            if time_difference > overdue_limit:
                member_info = get_member_by_id(record['member_id'])
                member_name = member_info.name if member_info else "알 수 없는 회원"
                
                print(f"🔥 연체! ({time_difference.days}일 초과)")
                print(f"  > 책: {record['title']}")
                print(f"  > 회원: {member_name} (ID: {record['member_id']:04d})")
                print(f"  > 대여일: {record['borrow_time']}")
                overdue_found = True
        except ValueError:
            print(f"⚠️ 기록 날짜 형식 오류: {record['borrow_time']}")

    if not overdue_found:
        print("✅ 현재 연체된 도서가 없습니다.")
    print("-" * 80)


# --- 6. 파일 I/O 및 데이터 초기화 ---

def initialize_default_books():
    """초기 도서 목록 데이터를 books 리스트에 채워 넣는 함수"""
    global books
    books.clear()
    for data in initial_books_data:
        books.append(Book(data["title"], data["author"], data["isbn"], data["stock"]))
    print("📢 초기 도서 목록 데이터 로드 완료.")

def save_data():
    """대여 기록, 재고 상태, 회원 정보를 파일로 저장하는 함수 (메뉴 10 종료 시)"""
    print("\n💾 **데이터 저장 중...**")

    # 1. 책 재고 상태 저장 (title, author, isbn, stock)
    try:
        with open(BOOK_STOCK_FILE, 'w', encoding='utf-8') as f:
            for book in books:
                line = f"{book.title},{book.author},{book.isbn},{book.stock}\n"
                f.write(line)
        print(f"✅ 책 재고 상태가 '{BOOK_STOCK_FILE}'에 저장되었습니다.")
    except Exception as e:
        print(f"❌ 책 재고 저장 중 오류 발생: {e}")

    # 2. 대여 기록 저장 (title, isbn, borrow_time, member_id)
    try:
        with open(BORROW_RECORDS_FILE, 'w', encoding='utf-8') as f:
            for record in borrow_records:
                line = f"{record['title']},{record['isbn']},{record['borrow_time']},{record['member_id']}\n"
                f.write(line)
        print(f"✅ 대여 기록이 '{BORROW_RECORDS_FILE}'에 저장되었습니다.")
    except Exception as e:
        print(f"❌ 대여 기록 저장 중 오류 발생: {e}")
        
    # 3. 회원 정보 저장 (member_id, name, contact)
    try:
        with open(MEMBER_RECORDS_FILE, 'w', encoding='utf-8') as f:
            for member in members:
                line = f"{member.member_id},{member.name},{member.contact}\n"
                f.write(line)
        print(f"✅ 회원 정보가 '{MEMBER_RECORDS_FILE}'에 저장되었습니다.")
    except Exception as e:
        print(f"❌ 회원 정보 저장 중 오류 발생: {e}")

def load_data():
    """파일에서 모든 데이터를 불러오는 함수"""
    print("\n📥 **데이터 불러오는 중...**")
    global books
    global borrow_records
    global members
    global next_member_id

    # 1. 책 재고 상태 불러오기
    if os.path.exists(BOOK_STOCK_FILE):
        try:
            books.clear()
            with open(BOOK_STOCK_FILE, 'r', encoding='utf-8') as f:
                for line in f:
                    # 데이터 로드 시 필드가 4개인지 확인
                    parts = line.strip().split(',')
                    if len(parts) == 4:
                        title, author, isbn, stock = parts
                        books.append(Book(title, author, isbn, int(stock)))
            print(f"✅ 책 재고 상태를 '{BOOK_STOCK_FILE}'에서 불러왔습니다. ({len(books)}권)")
        except Exception as e:
            print(f"❌ 책 재고 불러오기 중 오류 발생: {e}. 초기 데이터를 로드합니다.")
            initialize_default_books()
    else:
        print(f"⚠️ '{BOOK_STOCK_FILE}' 파일이 없어 초기 데이터를 사용합니다.")
        initialize_default_books()


    # 2. 회원 정보 불러오기
    if os.path.exists(MEMBER_RECORDS_FILE):
        try:
            members.clear()
            max_id = 0
            with open(MEMBER_RECORDS_FILE, 'r', encoding='utf-8') as f:
                for line in f:
                    parts = line.strip().split(',')
                    if len(parts) == 3:
                        member_id, name, contact = parts
                        member_id = int(member_id)
                        members.append(Member(member_id, name, contact))
                        if member_id > max_id:
                            max_id = member_id
            next_member_id = max_id + 1 # 다음 ID 갱신
            print(f"✅ 회원 정보를 '{MEMBER_RECORDS_FILE}'에서 불러왔습니다. ({len(members)}명)")
        except Exception as e:
            print(f"❌ 회원 정보 불러오기 중 오류 발생: {e}. 새로운 회원 기록을 시작합니다.")
            next_member_id = 1
    else:
        print(f"⚠️ '{MEMBER_RECORDS_FILE}' 파일이 없어 새로운 회원 기록을 시작합니다.")


    # 3. 대여 기록 불러오기
    if os.path.exists(BORROW_RECORDS_FILE):
        try:
            borrow_records.clear()
            with open(BORROW_RECORDS_FILE, 'r', encoding='utf-8') as f:
                for line in f:
                    parts = line.strip().split(',')
                    if len(parts) == 4: # 새로운 형식 (title, isbn, borrow_time, member_id)
                        title, isbn, borrow_time, member_id = parts
                        borrow_records.append({
                            "title": title,
                            "isbn": isbn,
                            "borrow_time": borrow_time,
                            "member_id": int(member_id)
                        })
            print(f"✅ 대여 기록을 '{BORROW_RECORDS_FILE}'에서 불러왔습니다. ({len(borrow_records)}개)")
        except Exception as e:
            print(f"❌ 대여 기록 불러오기 중 오류 발생: {e}. 새로운 대여 기록을 시작합니다.")
    else:
        print(f"⚠️ '{BORROW_RECORDS_FILE}' 파일이 없어 새로운 대여 기록을 시작합니다.")


# --- 7. 메인 메뉴 시스템 ---

def show_statistics():
    """총 대여 횟수 및 가장 많이 대여된 책을 출력하는 함수 (메뉴 5)"""
    print("\n📊 **도서관 통계 정보** 📊")
    print("-" * 30)

    # 1. 총 대여 횟수
    total_borrows = len(borrow_records)
    print(f"**총 대여 횟수:** {total_borrows}회")

    # 2. 가장 많이 대여된 책 (제목만)
    if borrow_records:
        titles = [record['title'] for record in borrow_records]
        borrow_counts = Counter(titles)
        most_borrowed = borrow_counts.most_common(1) 
        
        most_title = most_borrowed[0][0]
        most_count = most_borrowed[0][1]

        print(f"**가장 많이 대여된 책:** '{most_title}' ({most_count}회)")
    else:
        print("아직 대여 기록이 없어 통계를 계산할 수 없습니다.")
    
    print("-" * 30)


def main():
    """메인 메뉴 시스템을 구동하는 함수"""
    
    load_data() 
    
    while True:
        print("\n*** 📖 도서 관리 시스템 메뉴 📖 ***")
        print("---------------------------------")
        print(" 1. 도서 목록 보기")
        print(" 2. 도서 대여하기 (회원 필요)")
        print(" 3. 도서 반납하기 (회원 필요)")
        print(" 4. 전체 대여 기록 조회")
        print(" 5. 통계 보기")
        print(" 6. 연체 도서 확인 (7일 초과)")
        print(" 7. 새 회원 등록")
        print(" 8. 전체 회원 목록 보기")
        print(" 9. 책 검색 (제목/저자)")
        print(" 10. 프로그램 종료")
        print("---------------------------------")

        choice = input("메뉴를 선택하세요 (1-10): ").strip()

        try:
            if choice == '1':
                display_books()
            
            elif choice == '2':
                borrow_book()

            elif choice == '3':
                return_book()
            
            elif choice == '4':
                select_borrow()
            
            elif choice == '5':
                show_statistics()
            
            elif choice == '6':
                check_overdue() # 신규 기능: 연체 확인
            
            elif choice == '7':
                register_member() # 신규 기능: 회원 등록

            elif choice == '8':
                display_members() # 신규 기능: 회원 목록 보기

            elif choice == '9':
                search_books() # 신규 기능: 책 검색
            
            elif choice == '10':
                print("\n프로그램을 종료합니다. 현재 데이터를 저장합니다.")
                save_data()
                break
            
            else:
                print("⚠️ **오류:** 1부터 10 사이의 유효한 메뉴 번호를 입력해 주세요.")
        
        except ValueError as e:
            print(f"❌ **사용자 입력 오류:** {e}")
        except Exception as e:
            print(f"❌ **시스템 오류 발생:** {e}")

# 프로그램 실행
if __name__ == "__main__":
    main()
profile
A Normal Programmer

0개의 댓글