장고를 통해 웹페이지를 만들기 위해서는 Model을 통해 데이터베이스를 구축해야 한다.
✅ 데이터베이스란?
- 전자적으로 보관되는 정보의 양은 어마어마하고 이를 체계적이고 효율적으로 보관, 처리할 필요가 있다.
- 데이터베이스라는 데이터의 모음이며 데이터를 관리하기 위한 체계를 데이터베이스 관리 시스템(DBMS)라고 한다.
- Django는 기본적으로 SQLite를 사용하여 데이터베이스를 활용할 수 있다.
- 데이터 테이블의 경우 models.py를 통해 스키마를 구성하고 Migration을 통해 이러한 변경 내역을 적용한다.
Django의 settings.py를 확인해보면 DATABASES 항목을 확인할 수 있다.
📑 djangoProject/settings.py
... DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } ...
이러한 기본 설정은 경로 상의 db.sqlite3라는 파일에 저장함을 의미한다.

실제로 경로상이 파일을 확인할 수 있다.
이전에는 회사 서버의 mySQL에 데이터베이스를 활용했는데 이러한 경우에는 데이터베이스를 다음과 같이 설정하면 된다.
📑 djangoProject/settings.py
... DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': '테이블 이름', 'USER': '사용자 이름', 'PASSWORD': '암호', 'HOST': 'mysql 주소', 'PORT': '포트 번호', } } ...
해당 .sqlite3 파일의 내부를 확인하기 위해서는 추가적으로 프로그램을 설치해서 사용해야한다.

해당 프로그램을 다운받아 실행하면 프로그램을 확인할 수 있다.

sqlite3파일을 드래그해서 넣어주면

현재 테이블이 나타나는데 데이터 테이블을 만들지 않았기 때문에 기본 생성된 테이블만 볼 수 있다.
도서 웹페이지를 만들것이기 때문에 Book이라는 class를 만들어준다.
📑 djangoBooks/models.py
from django.db import models class Book(models.Model): isbn13 = models.CharField(primary_key=True, max_length=13) vol = models.CharField(max_length=20, blank=True, null=True) title = models.CharField(max_length=1200, blank=True, null=True) author = models.CharField(max_length=1000, blank=True, null=True) publisher = models.CharField(max_length=1000, blank=True, null=True) pub_date = models.CharField(max_length=10, blank=True, null=True) img_url = models.CharField(max_length=1000, blank=True, null=True) description = models.TextField(blank=True, null=True) kdc_class_no = models.CharField(max_length=20, blank=True, null=True) class Meta: db_table = u'books'
class에 해당하는 도서는 isbn이나 제목, 저자등의 문자열 정보들을 포함하고 있다.
중요한 점은 isbn13에 해당하는 데이터는 도서에 대한 주요 키로 다른 도서와 구분되는 고유한 정보에 해당한다.
이러한 주요 키의 경우 데이터베이스에서 대상을 구분하기 위해 사용되는 정보이므로 필수적으로 포함되어야 한다.

models.py에 정보가 변경되었다면
python manage.py makemigrations djangoBooks으로 migration을 만들고
python manage.py migrate djangoBooks으로 migration을 적용하면 된다.

sqlite3에도 해당 테이블이 생성되어있음을 확인할 수 있다.
물론 데이터가 비어있기 때문에 SQL을 통해 적당한 데이터를 기입해주자.
데이터를 변경하도록 웹을 구성할 수 있지만 먼저 데이터와 잘 연동하는지 확인하기 위해 임의에 데이터를 넣어주자.
간단하게 600여권 정보의 데이터를 저장한 내용이 csv로 정리되어 있다.
DB Browser를 쓰면 간단하게 데이터를 추가할 수 있다.

csv 파일에서 테이블 가져오기를 실행한다.

파일을 열고 테이블 이름만 변경해주면 간단하게 데이터를 기존 테이블에 추가할 수 있다.

변경사항을 저장해야 모든 결과가 적용된다.
데이터가 잘 들어갔다면 페이지에서 이를 리스트로 출력해주어야 한다.
📑 djangoBooks/views.py
... from .models import * #모델에서 정의한 클래스를 불러올 수 있게 해준다. ... class books_list(View): context = {} template_name = 'books_list.html' def get(self,request): books_list = Book.objects.all() #도서 클래스에서 모든 도서 데이터를 불러온다. self.context = {"books_list":books_list} #해당 데이터를 html로 보내기 위해 매핑해준다. return render(request, self.template_name, self.context) def post(self,request): return render(request, self.template_name, self.context) ...
먼저 도서 데이터를 전체 불러 리스트로 나타내기 위해서는 모델로부터 Book class를 import 한다.
html로 이 데이터를 보내기 위해서는 context에 넣어주는데 이때 괄호 안에는 html에서 사용할 이름으로 매핑을 해준다.
📑 djangoBooks/templates/books_list.html
... <body> 도서 목록 페이지 {% if books_list %} <!-- 매핑된 도서 리스트가 있는지, 있다면 내용이 있는지 확인 --> <div class="row"> <div class="book-items"> {% for book in books_list %} <!-- 반복문을 통해 도서에 대한 내용 출력 --> <div class="col-12 item"> <img src="{{book.img_url}}"> <div class="metadata"> <div class="title"> {{book.title}} <!-- 각 반복문 대상의 요소 불러오기 --> </div> <table class="bibil"> <tr> <th>저자</th> <td>{{book.author}}</td> <th>출판사</th> <td>{{book.publisher}}</td> <th>출판년도</th> <td>{{book.pub_date}}</td> </tr> <tr> <th>주제</th> <td>{{book.kdc_class_no}}</td> <th>ISBN</th> <td>{{book.isbn13}}</td> <th></th> <td></td> </tr> </table> </div> </div> {% endfor %} <!-- 중요한 점은 반복문의 끝에는 항상 이렇게 end를 넣어주어야 한다. --> </div> </div> {% else %} <div class="row"> <div class="col-12"> <div class="no-result"> <p><b>{{search_input}}</b>도서가 없습니다</p> </div> </div> </div> {% endif %} </body> ...
Django에선 html에 코드로 영향을 주기 위해서는 { } 안에 코드 를 넣어야 한다.
html의 바디 안에 books_list가 있는지 확인하는데 이는 views.py에서 넘겨준 이름으로 받는다.
또한 else를 이용해 리스트가 반환되지 않은 경우에 대해서 예외처리도 빠짐없이 넣어주어야 한다.
조건문은 {% endif %}로 조건문을 닫아준다.

페이지로 이동해보면 도서 목록이 길게 표시된다.
이미지의 경우 링크가 없을 경우에는 오류로 표시되는 것도 확인할 수 있다.
물론 도서 리스트가 잘 표시되지만 당연히 이를 페이지 형식으로 잘라주어야 한다. 페이지네이션을 활용하여 이러한 리스트를 효율적으로 표시할 수 있다.
페이지네이션을 화면에서 적용하기 위해서는 Ajax를 통해 페이지를 다시 불러오지 않고 페이지를 변화시켜줄 수 있어야 한다.
그러기 위해서는 ajax를 추가하고 페이지에 변경될 부분만 분할해 줄 필요가 있다.
📑 djangoBooks/templates/books_list.html
<html lang="ko"> <head> <meta charset="utf-8"> <!-- Ajax 설정--> <script src="https://kit.fontawesome.com/f9a809ddea.js" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="http://code.jquery.com/jquery-latest.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> <title>장고북스</title> </head> <body> 도서 목록 페이지 {% include 'books_table.html' %} <!-- 도서 목록 불러오기 --> </body> </html>
📑 djangoBooks/templates/books_table.html
<div id="table"> <!-- 도서 목록 출력 부분 --> {% if books_list %} <!-- 매핑된 도서 리스트가 있는지, 있다면 내용이 있는지 확인 --> <div class="row"> <div class="book-items"> {% for book in books_list %} <!-- 반복문을 통해 도서에 대한 내용 출력 --> <div class="col-12 item"> <img src="{{book.img_url}}"> <div class="metadata"> <div class="title"> {{book.title}} <!-- 각 반복문 대상의 요소 불러오기 --> </div> <table class="bibil"> <tr> <th>저자</th> <td>{{book.author}}</td> <th>출판사</th> <td>{{book.publisher}}</td> <th>출판년도</th> <td>{{book.pub_date}}</td> </tr> <tr> <th>주제</th> <td>{{book.kdc_class_no}}</td> <th>ISBN</th> <td>{{book.isbn13}}</td> <th></th> <td></td> </tr> </table> </div> </div> {% endfor %} <!-- 중요한 점은 반복문의 끝에는 항상 이렇게 end를 넣어주어야 한다. --> </div> </div> <!-- 페이지네이션 부분 --> <div class="row justify-content-center"> <div class="col-8 pagination"> <!-- 처음으로 버튼 --> <a class="first_page"><i class="fa-solid fa-chevron-left"></i></a> <!-- 각 페이지 버튼 --> {% for page in books_list.paginator.page_range %} {% if page >= books_list.number|add:-5 and page <= books_list.number|add:5 %} <a class="now_page">{{page}}</a> {% endif %} {%endfor %} <!-- 마지막으로 버튼 --> <a class="last_page"><i class="fa-solid fa-chevron-right"></i></a> </div> </div> <!-- 도서 목록이 없는 경우 --> {% else %} <div class="row"> <div class="col-12"> <div class="no-result"> <p><b>{{search_input}}</b>도서가 없습니다</p> </div> </div> </div> {% endif %} </div> <!-- Ajax 기능 구현 --> <script> // 각 페이지네이션 버튼 클릭에 따른 결과 전달 $('.first_page').click(function () { num_pages = parseInt($('.now_page').first().text()); page_click(); }) $('.now_page').click(function () { num_pages = parseInt($(this).text()); page_click(); }) $('.last_page').click(function () { num_pages = parseInt($('.now_page').last().text()); page_click(); }) //페이지 클릭 시 작동방식 function page_click() { $.ajax({ type: "get", //GET 방식 사용 url: "{% url 'djangoBooks:list' %}", //djangoBooks의 list url을 불러옴 data: { 'page': num_pages //GET 방식에 페이지 번호를 넣어서 전송 }, success: function (data) { //전송 성공시에 전송받는 데이터를 table class에 변경을 추가 console.log(data) $('#table').html(data); } }) } </script>
페이지의 페이지네이션 결과에 따라 변하게될 테이블 부분만 따로 html로 잘라낸다.
해당 내용은{% include 'books_table.html' %}라는 부분으로 잘라진 부분을 불러올 것이다.
books_table.html 내부에서 달라진 점은 하단에 페이지네이션 부분이다.
어떤 페이지를 출력할지는 page라는 변수에 해당한다.
현재 페이지인 page 변수에 따라 앞 뒤로 5개의 페이지를 반복문으로 표시하고 클릭된 페이지의 번호를 전달해 다시 해당 페이지를 <script>로 보내게 된다. Ajax를 통해 books_table.html을 변경된 page변수와 함께 다시 불러오게 된다.
📑 djangoBooks/views.py
... from .models import * #추가 ... class books_list(View): context = {} template_name = 'books_list.html' #기본 템플릿 이름 설정 #GET 통신 def get(self,request): books = Book.objects.all() page = int(request.GET.get('page', 1)) #페이지값을 전달받지 못하면 1로 설정 # 해당 통신이 Ajax인지 확인 -> 페이지네이션 요청인지 확인하는 방법 if request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest': # Ajax라면 표현할 페이지 템플릿을 변경 self.template_name = 'books_table.html' # 페이지네이터로 구분 -> 5개 아이템을 표시 paginator = Paginator(books, 5) # 페이지 선택 books_list = paginator.get_page(page) #도서 리스트를 저장 self.context = {"books_list":books_list} return render(request, self.template_name, self.context) #POST 통신 def post(self,request): return render(request, self.template_name, self.context) ...
해당 클래스의 get 부분에 Ajax에 대한 코드가 추가된다.
page = int(request.GET.get('page', 1))
페이지네이션을 위한 page함수가 있고 이를 get을 통해 받게된다.
만약 받을 수 없다면 이는 처음 페이지 접근을 의미하고 1을 기본값으로 가지게 됨을 의미한다.
if request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest':
현재 접근이 Ajax인지 확인하는 코드에 해당한다.
Ajax라면 해당 페이지가 아니라 table과 변경된 페이지 정보를 보내게 된다.
paginator = Paginator(books, 5)
페이지를 5개씩 묶는 것을 의미한다.
books_list = paginator.get_page(page)
도서 목록을 페이지네이션의 페이지 결과로 반환한다.
GET을 통해 html에서 보내준 페이지 번호를 받아 이에 대한 결과를 다시 페이지에 적용하는 방식이 적용된다. 페이지를 다시 불러오지 않고 깔끔하게 페이지에서 데이터를 바꿀 수 있다.
📑 djangoProject/templates/mainpage.html
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <title>장고북스</title> </head> <body> <h1>Django Books</h1> <p>이제 페이지를 하나씩 채워보도록 합시다.</p> <span class="menu"><a href="{% url 'djangoBooks:list' %}">도서목록</a></span> </body> </html>
메인 페이지에 도서 목록을 불러올 수 있는 버튼을 추가해서 도서 목록 페이지로 이동한다.
페이지 이동은 간단하게 a 태그의 href를 사용하면 되지만 django에서 적용한 이름을 이용하면 편리하다.

