본문 바로가기
Python/프로젝트03(도서관리)

프로젝트03. 도서 관리 프로그램 만들기5-sqlite3

by To올라운더 2024. 1. 9.
반응형

이제 기본적인 Table이 완성 되었고,

도서 대여 상태에 따라 업데이트 등이 정상적으로 이루어지면 DB쪽 구성은 완성된다.

 

여기서 다시 정리를 해보면

우리가 만든 4개의 테이블은 각각 다른 동작을 위해 필요하지만

서로 연관 되어 있다.

 

1. 기본 테이블 정리

 

1) books_info 테이블

 - 도서를 전체적으로 관리하는 테이블로 해당 테이블 내에 도서명과 대여권수를 확인할 수 있기 때문에

   나중에 회원들이 도서명으로 대여 여부를 검색할 때 사용하게 될 테이블이다.

 

2) book_detail_info 테이블

 - 같은 도서명이더라도 각각의 고유번호와 상태를 알 수 있는 정보를 가지고 있는 테이블로

   해당 테이블에서 도서가 가지는 고유값을 통해 도서의 상태를 알 수 있는 것과

   대여나 반납 시 해당 값을 이용해 어느 도서가 대여, 반납되었는지 기록할 수 있다.

   일반적인 도서 앞에 바코드를 붙인다면, 해당 테이블의 book_detail_key 컬럼을 통해 진행하면

   고유 값이 할당 된다.

 

3) members 테이블

 - 회원 정보를 기록하는 테이블로 해당 회원의 상태가 확인 가능하고,

  현재 대여 권수를 확인하여 특정 권수(현재는 5권) 이상 대여하지 못하도록 제한할 수 있다.

 

 

2. 대여 과정 및 필요 기능 확인

이제 대여하는 기능을 만들어 볼텐데, 

우리가 일반적으로 경험해 봤던 대여 과정을 떠올려보면,

 

1) 대여하려는 책을 들고 직원 또는 키오스크에 가져가서 

회원증(또는 회원 ID, PW)를 확인한 뒤, 해당 도서의 바코드를 인식하여 대여했을 것이다.

그리고 대여를 진행하게 되면 우리가 만든 모든 테이블에 update 작업이 필요하다.

 

2) books_info 테이블에서는 해당 도서명의 대여 가능 권수를 1 감소시켜야 한다.

3) book_detail_info 테이블에서는 해당 도서의 상태를 변경시켜야 하는데,

 : user_key 컬럼을 해당 회원의 members_no 를 입력해주고,    

 : rent_stat 컬럼을 '대여중' 으로 변경하고, 

 : rent_start_date 컬럼에 오늘 날짜를 입력한 뒤,

 : rent_expire_date 컬럼에 10일 뒤 날짜를 입력을 해주고,

4) members 테이블에 대여 권수를 1권 증가시켜주면 끝이난다.

 

3. 날짜 입력 방법 확인

해당 작업을 위해 오늘 날짜와 10일 뒤 날짜를 입력하는 방법을 먼저 연습해보면

import datetime

today = datetime.date.today()
print(type(today))
print(today)

rent_period = datetime.timedelta(days=10)
expire_date = today + rent_period
print(type(expire_date))
print(expire_date)

 

 - datetime 모듈을 통해 datetime.date.today()의 결과 값을 today 라는 함수에 저장하여 출력해보면

  datetime.date 타입을 가지고 오늘의 날짜를 가진 값을 출력한다.

 - 대여 기간을 할당하기 위해 datetime.timedelta 를 이용하는데, timedelta 를 이용하면 날짜 계산 시 

  10일 뒤, 1달 뒤 같은 특정 일자 경과 시 날짜 계산이 유용하다. 

  (*timestamp가 아닌 text로 처리하게되면 마지막날이 30일인지 혹은 31일인지, 또 2월이 윤년인지 등 

  계산이 쉽지 않기 때문에 날짜는 꼭 날짜 형식을 갖추는게 좋다.)

반응형

 

그런데, 여기까지는 어렵지 않지만, 이 값들을 sqlite에 insert를 하게되면 생각과는 다른 동작이 내부에서 일어난다.

 

아래의 테스트 코드와 같이 rent_start_date 와 rent_expire_date 값을 timestamp 형식으로 받는 테스트 DB를 생성한 뒤,

위에서 확인한 today 값과 expire_date 값을 우리가 일반적으로 사용했던 f string 이나 format 방식으로 입력하면

초록색 영역과 같이 (2003, 1993) 이 나타나는데, 

오늘 날짜 [2023-12-08] 을 date나 string 이 아닌 연산(뺄셈) 으로 계산되기 때문이다.

2023 - 12 - 8 = 2003 / 2023 - 12 - 18 = 1993

import datetime

today = datetime.date.today()
# print(type(today))
print(today)

rent_period = datetime.timedelta(days=10)
expire_date = today + rent_period
# print(type(expire_date))
print(expire_date)

import sqlite3

# DB생성 및 Auto Commit, RollBack
conn = sqlite3.connect(r'.\time_test.db', isolation_level=None) # 해당 경로에 book_manager_database.db DB파일이 생성된다.

# 커서(Cursor)의 이용
cursor = conn.cursor()

# 테이블 생성(Data Type : TEXT, NUMERIC INTEGER, REAL, BLOB)
# 날짜 입력 테이블 생성
cursor.execute('CREATE TABLE IF NOT EXISTS times(rent_start_date timestamp, rent_expire_date timestamp)')

# 데이터 insert1 / 비정상
cursor.execute(f"insert into times(rent_start_date, rent_expire_date) values({today}, {expire_date})")

# 데이터 insert2 / 비정상
cursor.execute(f"insert into times(rent_start_date, rent_expire_date) values({str(today)}, {str(expire_date)})")

# 데이터 insert3 / 비정상
cursor.execute("insert into times(rent_start_date, rent_expire_date) values({0}, {1})".format(today, expire_date))

# 데이터 insert4 / 비정상
cursor.execute("insert into times(rent_start_date, rent_expire_date) values({0}, {1})".format(str(today), str(expire_date)))

# 데이터 insert5 / 정상
cursor.execute("insert into times(rent_start_date, rent_expire_date) values(?, ?)",(today, expire_date))


# 입력된 데이터 값 확인
cursor.execute('select * from times')
print(cursor.fetchall())

 

 - 해당 방법을 처리하기 위해 여러가지 시도를 해봤지만, 

   실제 받는 문자를 string으로 변경해서 입력하더라도 전체를 문자로 인식하지 못하고 값을 계산하여 정리하였다.

 - 그리고 string이 정상적으로 처리 되지 않는 다는 말은 변수 값이 아닌 날짜를 input 등으로 받더라도 정상동작이 되지 않을 거라는 의미이기도 하다. 

 - 그러다 '?' 를 통한 패킹 방법으로 해결 방법을 찾았지만....

   이러한 장애 증상은 sqlite에서 전달 받은 문자를 처리시 일어나는 일부 증상인것 같다는 결론을 내렸다.

 - 개인적으로 가시성이 좋아 f string을 선호하는데, 날짜 값을 넣기 위해 어쩔 수 없이 해당 insert 문은 '?' 를 이용한 입력을 진행해야 할 것 같다.

 - 해당 내용만 인지 했다면 이후 도서 대여 함수는 어렵지 않게 구현할 수 있다.

 

4. 코드 작성하기(도서 대여 하기)

 1) 먼저 해당 코드를 실행할 때 회원의 정보와 책의 정보를 입력 받아 정보를 확인해야 한다.

     그리고 DB를 검색하기 전, 해당 상태 값을 False로 초기화한다.

# 도서 대여 하기
def rent_book(members_id, book_detail_key):
    use_rent_stat = False # DB 검색 전, 대여 가능 상태 초기화

 

 2) 전달 받은 members_id 값을 통해 회원 등록 여부를 확인한다.

     확인 후, 등록 되어 있지 않을 경우, 경고 메세지를 출력하고

     등록이 되어 있을 경우, members 테이블의 members_stat 과 rent_count를 통해 대여 가능 여부를 확인한다.

     아직 구성되지는 않았지만, members_stat의 경우 승인, 거절, 블랙 등의 여러 단계를 만들어

     정상적으로 대여가 가능한 회원과 지연 반납 및 대여가 불가능한 블랙 리스트 등을 관리할 목적이며,

     rent_count는 한명의 회원이 5권 이상이 대여할 수 없도록 제한을 둘 예정이다.

     현재는 모두 5명이지만 VIP 등 특정 회원에 대해 조건문을 통해 그 이상을 대여하는 등의

     아이디어를 반영할 수도 있다. 

 

 3) 이 과정이 정상적으로 처리되면 우리가 처음 False로 초기화한 use_rent_stat 을 True로 변경한다.

# 도서 대여 하기
def rent_book(members_id, book_detail_key):
    # 해당 회원 도서 대여 가능 상태 확인, 조건 members_stat = '승인', rent_count < 6
    use_rent_stat = False # DB 검색 전, 대여 가능 상태 초기화 
    cursor.execute(f'select members_no, members_stat, rent_count from members where members_id = \'{members_id}\'')
    members_result = cursor.fetchall()
    if len(members_result) == 0 :
        print('등록 되지 않은 ID 입니다. 먼저 회원 가입을 진행해주세요.')

    elif len(members_result) == 1:
        if members_result[0][1] == '승인':
            if int(members_result[0][2]) < 5 :
                # 대여 가능 상태
                use_rent_stat = True

            else:
                print('대여 가능 권수가 초과하였습니다. 반납 후 이용하세요.') 
        else:
            print('대여가 불가능한 상태입니다. 관리자에게 문의하세요.')

 

4) 이제 여기에 대여 과정을 각각의 테이블에 update를 통해 반영하면 되는데...

     다음 포스팅에서 작성하도록 하겠다.

반응형