1. DB구성
1-1. sql파일 작성
첫번째 글에서 구상했던 아래 사진의 DB의 구조에 따라 sql코드를 작성했다.

1-2. DB구조 수정
다만 위 사진의 내용에서 몇가지를 수정했다.
1) user_name 이라는 속성 추가
user_id는 DB에서 이용자를 관리하는 목적으로만 사용하기로 했고,
로그인 등에서 실제로 이용자가 사용할 user_name 속성을 만들었다.
2) 예약 관련 속성 추가
reservation 테이블에서 book_code와 user_id만 있었지만,
reservation_id, reservation_date를 추가해 내부적으로 관리가 편해지고 예약의 우선순위를 따질 수 있게 바꾸었다.
drop database if exists library_db;
create database library_db;
use library_db;
create table users(
user_id int auto_increment primary key ,
user_name varchar(50),
user_pw varchar(20) not null,
user_role int not null -- 0: 일반 사용자, 1: 관리자
);
create table book_data(
book_code int auto_increment primary key,
title varchar(100) not null,
author varchar(50) not null
);
create table book_status(
book_id int auto_increment primary key,
book_code int not null,
is_rent boolean default false,
foreign key (book_code) references book_data(book_code)
);
create table category(
category_id int auto_increment primary key,
category_name varchar(50) not null
);
create table book_category(
book_code int not null,
category_id int not null,
foreign key (book_code) references book_data(book_code),
foreign key (category_id) references category(category_id),
primary key (book_code, category_id)
);
create table rent(
rent_id int auto_increment primary key,
book_id int not null,
user_id int not null,
rent_date datetime default current_timestamp,
return_date datetime,
foreign key (book_id) references book_status(book_id),
foreign key (user_id) references users(user_id)
);
create table reservation(
reservation_id int auto_increment primary key,
book_code int not null,
user_id int not null,
reservation_date datetime default current_timestamp,
foreign key (book_code) references book_data(book_code),
foreign key (user_id) references users(user_id)
);
1-3. 데이터 삽입
테이블 생성 코드 아래에는 테스트용 데이터를 여러개 insert해주었다.
[코드 보기↓]
2. 메인페이지(homepage.html)
2-1. 디자인
먼저 ppt로 대략적인 홈페이지 구성을 그려봤다.
1) 로고를 위한 구역
2) '모든서적', '인기차트'등의 페이지로 이동할 수 있는 버튼
3) 로그인이 되어있는지, 이용자의 정보와 로그인 / 회원가입 페이지로 이동하는 개인정보창
4) 가장 많이 책을 읽은 유저 top랭킹을 보여주는 왼쪽 사이드 랭킹 바
5) 새로 추가된 책을 보여주는 구역

새로 추가된 책을 보여주는 구역은 좌우의 화살표 버튼을 통해 슬라이드 방식으로 동작하게끔 계획했다.
이 프로젝트는 학교과제이고 디자인 요소는 채점기준에 포함하지 않는다고 고지되어있었기 때문에
디자인에는 크게 신경을 쓰지 않을 예정이다.
2-2. New books 관련
먼저 homepage에서 New books 부분을 위한 데이터를 가져오기로 했다.
가장 최근에 추가된 책 6권이 보이게끔 계획했다.
이미 존재하는 책이 추가로 들어올 경우는 new book이라고 할 수 없고 아예 새로운 종류의 책이 데이터에 추가되어야지만 new book이라고 할 수 있으므로 book_id가 아닌 book_code로 기준을 잡기로 했다.
따라서 아래와 같은 sql문을 사용했다.
SELECT * FROM book_data
ORDER BY book_code DESC
LIMIT 6;
아래는 db.py파일의 내용이다.
python에서 sql 서버에 연결하고 쿼리를 실행하는 것과 관련된 함수를 모아놓은 파일이다.
이전 글의 test_func()처럼 함수 안에 연결을 하고 끊는 부분을 넣게 되면,
앞으로 db.py파일 안에 많아질 함수들에 모두 이 코드가 중복되어 들어가야하기 때문에
execute_query()라는 함수를 만들어 이후로의 함수들은 sql쿼리에만 집중할 수 있게 하여 효율을 높였다.
def execute_query(query, params=None, fetch=None):
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute(query, params or ())
if fetch == 'all':
result = cursor.fetchall()
elif fetch == 'one':
result = cursor.fetchone()
else:
conn.commit()
result = None
cursor.close()
conn.close()
return result
def get_newbooks():
query = '''
SELECT * FROM book_data
ORDER BY book_code DESC
LIMIT 6;
'''
print('get_newbooks called')
return execute_query(query, fetch='all')
이 get_newbooks()은 main/routes.py의 homepage()함수에서 호출되어 new_book_list라는 변수에 잠시 저장된다.
이 함수는 render_template()을 리턴하는데, 이 인자로 실행시킬 main/homepage.html이라는 html파일의 경로와 파라미터로 new_book_list가 들어간다.
return render_template('main/homepage.html', new_books = new_book_list)
최종적으로 render_template()에서 html파일을 보여주게 된다.
그럼 html파일에서 인자로 들어온 이 new_book_list를 어떻게 사용할 수 있을까?
아래 코드는 main/homepage.html의 해당 부분이다.
매우 간단하게 {% for book in new_books %}을 통해 값을 순차적으로 접근할 수 있다.
<section class="new-books"> <!-- 새로 들어온 책 슬라이드로 보여줄 부분-->
<h2>New Books</h2>
<div class="slider-container">
<div class="slider-wrapper">
<!-- main/routes.py에서 return render_template('main/homepage.html', new_books = new_book_list) -->
{% for book in new_books %}
<div class="book-slide">
{% if loop.index is odd %}
<img src="{{ url_for('static', filename='images/book_img_1.jpg') }}" alt="Book Icon 1">
{% else %}
<img src="{{ url_for('static', filename='images/book_img_2.jpg') }}" alt="Book Icon 2">
{% endif %}
<p class="book-title">{{ book.title }}</p>
</div>
{% endfor %}
</div>
<button class="slider-btn prev-btn"><</button>
<button class="slider-btn next-btn">></button>
</div>
</section>
코드 중간의 if와 loop.index는 이미지를 위한 요소이다.
반복문이 몇번째 돌고 있는지 확인해 이 값이 홀수일 때는 image1을 아닐 때는 image2를 보여주게끔 되어있다.
모든 책의 실 이미지를 저장하고 보여줄 수는 없기 때문에 아이콘으로 대체해 두 가지 아이콘이 번갈아 가며 나오게끔 했다.
아래 영상을 보면 조금 더 이해가 편하다.
main/homepage.html 파일의 다른 코드 부분은 기능과는 관계없는 디자인 부분이므로 언급하지 않을 예정이다.
다만 전체 코드가 궁금하다면 아래 github에서 확인할 수 있다.
GitHub - april2901/Flask-mySQL-library-web
Contribute to april2901/Flask-mySQL-library-web development by creating an account on GitHub.
github.com
2-3. 버튼들
new books구역 이외에 menu와 로그인 버튼, 그리고 로고를 눌렀을 때 다시 홈페이지로 돌아오는 버튼 기능을 추가했다.
아래 코드에서 보듯이 url_for()을 통해 구현할 수 있다.
<header class="header">
<div class="logo">
<a href="{{ url_for('main.homepage') }}">ITE2038 Library</a>
</div>
<nav class="navigation">
<a href="#">menu1</a>
<a href="#">menu2</a>
</nav>
<div class="personal-info"> <!-- 개인정보 창 -->
{% if session.user_id %}
<span>환영합니다, <strong>{{ session.user_name }}</strong>님!</span>
<div class="buttons">
<a href="{{ url_for('mypage.mypage') }}" class="btn">마이페이지</a>
<a href="{{ url_for('auth.logout') }}" class="btn">로그아웃</a>
</div>
{% else %}
<span>로그인이 필요합니다</span>
<div class="buttons">
<a href="{{ url_for('auth.login') }}" class="btn">로그인</a>
</div>
{% endif %}
</div>
</header>
중간의 session은 사용자별로 저장되는 작은 저장소의 개념이다.
from flask import session
을 통해 사용할 수 있다.
session에 대한 내용은 이어서 로그인 페이지를 구현하며 조금 더 설명될 예정이다.
3. 로그인 페이지 (login.html)
3-1. SQL 처리 (db.py)
이번에도 먼저 db.py에 sql문을 작성했다.
유저의 로그인을 확인하는 코드이다.
함수의 인자를 쿼리에 %s를 통해 집어넣을 수 있다.
def verify_user(name, pw):
print('verify_user called with:', name, pw)
query = '''
SELECT user_id, user_name FROM users
WHERE user_name = %s AND user_pw = %s;
'''
params = (name, pw)
result = execute_query(query, params=params, fetch='one')
print('verify_user result:', result)
return result
아래의 코드는 auth/routes.py의 일부이다.
main/routes.py와는 다르게 코드가 복잡하다.
먼저 login()함수에서는 웹사이트의 아이디/비밀번호 입력창에서 받은 문자열을 가져온다.
이 값들을 verify_user의 인자로 전달해 SQL쿼리를 실행 후 결과를 user에 받아온다.
이미 DB에 있는 유저라면 값이 리턴될 것이고 아니라면 none이 리턴될 것인데,
값이 존재한다면 아까 위에서 잠깐 봤던 session에 정보를 등록한다.
session은 이 routes파일에서 수정되지만 다른 파일에서도 접근가능한 기능을 제공한다.
이후 메인 홈페이지로 리디렉션 한다.
위 3-2의 html코드가 이제 이해가 갈 것이다.
session.user_id값이 있는지 여부에 따라 보여주는 내 상태 창이 달라진다.
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
user_name = request.form.get('user_name')
user_pw = request.form.get('user_pw')
print('로그인 시도')
user = db.verify_user(user_name, user_pw) # DB에서 사용자 확인
print(user)
if user:
session['user_id'] = user['user_id']
session['user_name'] = user['user_name']
print('로그인 성공')
print('세션:',session)
#flash("로그인에 성공했습니다!")
return redirect(url_for('main.homepage'))
else:
print('로그인 실패')
flash("아이디 또는 비밀번호가 올바르지 않습니다.")
return render_template('auth/login.html')
@auth_bp.route('/logout')
def logout():
session.pop('user_id', None)
session.pop('user_name', None)
print('로그아웃')
return redirect(url_for('main.homepage'))
3-2. 결과

로그인 페이지의 html코드에서는 크게 설명할 부분이 없다.
윗 부분은 홈페이지와 똑같이 되어있고 입력을 받는 코드도 간단하다.
<form method="POST" action="{{ url_for('auth.login') }}">
<div class ="form-group">
<label for="user_name">아이디</label>
<input type="text" id="user_name" name="user_name" required>
</div>
<div class ="form-group">
<label for="user_pw">패스워드</label>
<input type="password" id="user_pw" name="user_pw" required>
</div>
<button type="submit" class="btn">로그인</button>
</form>
아무리 기능적 구현에 충실하더라도 이 프로젝트는 웹사이트 제작이다 보니 코드의 일정 부분은 css 및 js와 관련된 부분인데,
글에서 디자인적 요소를 설명하지는 않기 때문에 해당 부분의 코드는 블로그에 사용하지 않았다.
위에서도 얘기했듯 css파일과 js파일등 프로젝트의 모든 코드를 보고 싶다면 github를 참고하면 된다.
다음 글에서는 파일들의 구조와 연결에 대해, 어떤식으로 데이터 밎 정보가 오가는지를 설명할 예정이다.
'프로젝트, 연구 > 도서관 관리 사이트' 카테고리의 다른 글
| [도서관 관리 사이트] 6. 마이페이지, 모든서적 페이지, 인기차트 페이지 (0) | 2025.10.24 |
|---|---|
| [도서관 관리 사이트] 5. 회원가입, 관리자페이지, 책/카테고리 수정 (0) | 2025.10.20 |
| [도서관 관리 사이트] 4. 동작 흐름, 파일 간 데이터 연결 (0) | 2025.10.13 |
| [도서관 관리 사이트] 2. 웹페이지 설계, 프로젝트 폴더 구성 (2) | 2025.10.11 |
| [도서관 관리 사이트] 1. 기능 확인, DB구조 설계, 프레임워크 선택 (0) | 2025.10.10 |