이번에는 템플릿에 디자인을 입혀보려고 합니다.
디자이너가 어떤 디자인을 입혀달라고 요청했을 때, 그 요청을 진행할 수 있도록 디자인 파트도 공부해야 할 필요가 있을 거에요.
'부트스트랩'을 적용시켜서 해봅니다!!
그럼 나만의 디자인을 입혀보면서 개인 공부를 하며 진행합니다.
Code: https://github.com/ghk0409/Django_bookmark/
<디자인 입히기>
1. 템플릿 확장하기
- 여러 웹 서비스를 둘러보면 어느 페이지에서든 동일하게 보이는 메뉴바나 타이틀 같은 부분이 많습니다. 만약 이런 부분의 수정사항이 발생하면 각 템플릿들(페이지)마다 다 수정해야 할까요?? 이런 비효율을 방지하기 위해서 '템플릿 확장' 방법을 사용합니다.
+ 기준이 되는 레이아웃 부분을 담은 템플릿을 별도로 만들어둔 다음, 다른 템플릿들이 기준 템플릿을 상속받아 사용하는 것처럼 만들어주는 것이죠!!
- 템플릿 확장을 사용하기 위해 Project의 루트 경로에 [templates] 폴더를 추가해준 뒤, 기준 템플릿 'base.html' 파일을 만들어 봅니다.
+ 최상위 경로 bookmark에 templates 폴더를 만들어줍니다. (bookmark/templates)
# templates/base.html
<!--템플릿 확장은 block을 기준으로 동작-->
<!--다른 템플릿에서 껴넣을 공간을 block 태그를 사용해 만들어줌-->
<!--하위 템플릿에서 사용할 block에 껴넣을 내용을 결정하여 내용을 채움-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--브라우저 탭에 보이는 이름 결정-->
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<!--출력하고자 하는 내용 결정-->
{% block content %}
{% endblock %}
</body>
</html>
<!--여기서는 title과 content 2개의 block 생성-->
- 위에서 만든 기준 템플릿 파일(base.html)을 사용할 수 있도록 settings.py를 수정해봅니다.
+ settings.py의 TEMPLATES 부분을 수정 할꺼에요
# config/settings.py
# DIRS: [] 에 os.path.join(BASE_DIR, 'templates') 값을 추가
import os
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
+ 위처럼 수정하면 템플릿 검색을 할 때, 내가 지정한 'templates' 폴더를 살펴보게 될껍니다.
- 각 템플릿들을 base.html에 있는 [title] 블록과 [content] 블록을 적용해서 수정해봅니다.
# bookmark/templates/bookmark/bookmark_confirm_delete.html
<!--삭제할 때는 확인이 필요해서 confirm_delete라는 이름을 사용-->
<!--extends로 base.html 상속받음-->
{% extends 'base.html' %}
<!--title 블록 내용 채우기-->
{% block title %}
Confirm Delete
{% endblock %}
<!--content 블록 내용 채우기-->
{% block content %}
<!--form 태그와 csrf_token은 혼연일체라고 생각!!-->
<form action="" method="post">
{% csrf_token %}
<div class="alert alert-danger">Do you want to delete Bookmark "{{object}}"?</div>
<input type="submit" value="Delete" class="btn btn-danger">
</form>
{% endblock %}
# bookmark/templates/bookmark/bookmark_create.html
{% extends 'base.html' %}
{% block title %}
Bookmark Add
{% endblock %}
{% block content %}
<form action="" method="post"> <!--action 메서드 비워두면 현재 페이지로 전달-->
{% csrf_token %} <!--CSRF 공격을 막기 위한 용도-->
{{form.as_p}} <!--클래스형 뷰의 옵션 값으로 설정한 필드를 출력할 때 P태그로 감싸줌-->
<input type="submit" value="Add" class="btn btn-info btn-sm">
</form>
{% endblock %}
# bookmark/templates/bookmark/bookmark_detail.html
{% extends 'base.html' %}
{% block title %}
Bookmark Detail
{% endblock %}
{% block content %}
<!--북마크 하나의 정보만 출력-->
<!--DetailView가 object라는 이름으로 북마크 값을 전달함-->
<!--object변수를 이용해 값을 하나씩 출력-->
{{object.site_name}}<br/>
{{object.url}}
{% endblock %}
# bookmark/templates/bookmark/bookmark_update.html
{% extends 'base.html' %}
{% block title %}
Bookmark Update
{% endblock %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Update" class="btn btn-info btn-sm">
</form>
{% endblock %}}
# bookmark/templates/bookmark/bookmark_list.html
{% extends 'base.html' %}
{% block title %}
Bookmark List
{% endblock %}
{% block content %}
<!--북마크 추가하기 링크-->
<div class="btn-group">
<a href="{% url 'add' %}" class="btn btn-info">Add Bookmark</a>
</div>
<p></p>
<!--북마크 목록 출력-->
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Site</th>
<th scope="col">URL</th>
<th scope="col">Modify</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
<!--제네릭 뷰에서는 모델의 오브젝트가 여러 개일 경우 object_list 변수로 전달함!!-->
<!--object_list에서 bookmark 이름으로 북마크를 하나씩 꺼내 한 줄씩 출력-->
{% for bookmark in object_list %}
<tr>
<td>{{forloop.counter}}</td>
<!--pk값에 bookmark의 id값을 담아서 detail 페이지로 전달-->
<td><a href="{% url 'detail' pk=bookmark.id %}">{{bookmark.site_name}}</a></td>
<td><a href="{{bookmark.url}}" target="_blank">{{bookmark.url}}</a></td>
<td><a href="{% url 'update' pk=bookmark.id %}" class="btn btn-success btn-sm">Modify</a></td>
<td><a href="{% url 'delete' pk=bookmark.id %}" class="btn btn-danger btn-sm">Delete</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
+ [title] 블록과 [content] 블록 안에 넣을 코드만 제외하고 나머지는 전부 깔끔하게 지워줍니다.
+ 'base.html'로부터 나머지 html 코드를 전부 상속받기 때문에 문제없이 동일하게 구동될 수 있습니다!!
2. 부트스트랩 적용하기
- 템플릿을 분리/확장까지 끝낸 후, 디자인을 입혀봅니다.
+ CSS 프레임워크 중 하나인 부트스트랩을 적용해보겠습니다.
+ 부트스트랩에 접속해서 부트스트랩을 사용할 수 있도록 css 파일과 js 파일들을 불러옵니다.
+ 위 링크로 접속하면 Quick Start에서 CSS와 JS의 'Bundle', 'Seperate' 부분에 있는 코드를 복사해서 base.html의 <head> 태그에 넣어줍니다.
# templates/base.html
<!--head 태그 안에 부트스트랩 적용 코드 복붙!!-->
<head>
<meta charset="UTF-8">
<!--브라우저 탭에 보이는 이름 결정-->
<title>{% block title %}{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x"
crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"
integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.min.js"
integrity="sha384-Atwg2Pkwv9vp0ygtn1JAojH0nYbwNJLPhwyoVbhoPwBhjQPR5VtM2+xf0Uwh9KtT"
crossorigin="anonymous"></script>
</head>
<!--기존 html 코드에 각 태그 속 class에 css를 미리 지정해놔서 이쁘게 바로 변신함-->
- 위 부트스트랩을 적용한 후, 목록 페이지를 확인해봅니다.
- 부트스트랩을 적용해봤으니 좀 더 수정해서 메뉴바도 넣어봅니다.
# templates/base.html
<!--body 태그를 아래처럼 수정-->
<body>
<div class="container">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Django Bookmark</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
</ul>
</div>
</nav>
<p></p>
<div class="row">
<div class="col">
<!--출력하고자 하는 내용 결정-->
{% block content %}
{% endblock %}
<!--새로 추가된 block!!-->
{% block pagination %}
{% endblock %}
</div>
</div>
</div>
</body>
- 그리고나서 목록 페이지를 다시 새로고침 후 확인하면 짜잔~
3. 페이징 기능 만들기
- 위에서 새로 추가된 [pagination] 블록을 이용해서 페이징 기능을 만들어 넣어 봅니다.
+ 클래스형 뷰에서는 간단하게 페이징 기능을 구현할 수 있습니다.
+ paginate_by = N을 추가해서 목록 페이지에 북마크 목록을 N개까지만 출력하게 만들어줍니다.
# bookmark/views.py
class BookmarkListView(ListView):
model = Bookmark
paginate_by = 5 # 이 코드를 추가하기
- 수정 후, 목록 페이지를 확인 해봅니다.
+ paginate_by 값을 5로 지정해서 5개만 출력되는 것으로 보아 정상적으로 동작 성공!!
- 목록 아래쪽에 페이지 목록을 출력해서 제대로 된 페이징 기능을 사용할 수 있게끔 만들어 봅니다.
+ bookmark_list.html 파일을 열어 가장 하단부에 아래 코드처럼 수정해봅니다.
# bookmark/templates/bookmark/bookmark_list.html
<!--[pagination] 블록을 적용하기-->
{% block pagination %}
{% if is_paginated %}
<ul class="pagination justify-content-center pagination-sm">
<!--출력 페이지보다 이전 목록을 가지고 있을 경우-->
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="{% url 'list' %}?page={{ page_obj.previous_page_number }}" tabindex="-1">Previous</a>
</li>
<!--이전 목록이 없을 경우, Previous 버튼 비활성화-->
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Previous</a>
</li>
{% endif %}
<!--페이지 수만큼 페이지 버튼 넣기-->
{% for object in page_obj.paginator.page_range %}
<!--해당 페이지로 이동하면 해당 페이지 버튼은 비활성화-->
<li class="page-item {% if page_obj.number == forloop.counter %} disabled{% endif %}">
<a class="page-link" href="{{ request.path }}?page={{ forloop.counter }}">{{ forloop.counter }}</a>
</li>
{% endfor %}
<!--출력 페이지보다 다음 목록을 가지고 있을 경우-->
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="{% url 'list' %}?page={{ page_obj.next_page_number }}">Next</a>
</li>
<!--다음 목록이 없으면 Next 버튼 비활성화-->
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Next</a>
</li>
{% endif %}
</ul>
{% endif %}
{% endblock %}
+ paginate_by 값을 사용하면 자동으로 Page 객체를 생성합니다. 이 객체를 이용해 이전 페이지, 다음 페이지, 현재 페이지를 알 수 있고 페이지의 범위도 알 수 있습니다.
+ 이를 이용해 템플릿 문법을 통해 각각의 값들을 출력해서 조건에 따라 페이징 버튼들을 설정해줍니다.
+ 참고 문서 : https://docs.djangoproject.com/en/3.1/topics/pagination/#django.core.paginator.Paginator
Pagination | Django documentation | Django
Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate
docs.djangoproject.com
+ 이제 다시 목록 페이지를 확인 해보겠습니다!!
4. 정적(Static) 파일 사용하기
- 부트스트랩을 이용해 온라인 상으로 css파일과 js파일을 불러와서 사용해 봤습니다. 그럼 로컬 서버에 있는 정적 파일을 사용해보도록 하겠습니다.
+ 로컬에 있는 이미지 파일이나 css, js파일들을 이용해서 디자인을 입힐 수도 있습니다!!
+ 정적 파일도 템플릿 파일처럼 정해진 위치가 있습니다. 보통은 각 앱 폴더 밑으로 'static' 폴더를 만들어 사용하고 'templates' 폴더처럼 별도의 폴더를 사용하려 하기 때문에 settings.py에서 설정을 해주도록 합니다.
+ 그렇긴 하지만 여기서는 Project 루트에 static 폴더를 만들어서 사용해볼께요!! (경로 설정만 다를 뿐이죠^^)
# 최상위 bookmark 폴더에 static 폴더를 먼저 추가해줍니다!!
# config/settings.py
# settings.py 가장 하단부에 아래 변수를 추가해줍니다.
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
- static 폴더에 'style.css' 파일을 생성해줍니다.
+ 우클릭 -> New -> File 선택해서 style.css 그대로 파일명 입력 후 만들기
+ css파일 만들고 아래 코드를 입력!!
# static/style.css
body {
width:100%;
}
- base.html에서 style.css를 사용할 수 있도록 base.html 코드를 수정해줍니다.
+ base.html 파일에서 <head> 태그 안쪽에 아래 코드를 추가합니다.
# templates/base.html
<!--static 폴더의 style.css 파일 불러오기-->
{% load static %}
<link rel="stylesheet" href="{% static 'style.css' %}">
- 북마크 서비스 페이지를 새로고침하고 css파일이 잘 불러와졌는지 개발자 도구를 이용해 확인해봅니다.
여기까지 북마크 웹 서비스를 완성 해봤습니다!! 기본적인 기능 외에 디자인적인 측면은 css 파일을 추가로 수정해서 더욱 예쁘게 입힐 수 있습니다. 이 부분은 내가 하고 싶은대로!! 꾸며보면 더 좋을거에요. css 공부도 간단하게 해볼겸!!
+ 현재 본인이 css를 잘 몰라서 추가를 더 안했다는건 안 비밀....;;;;;;
+ html 코드에 넣은 태그들이 몇 개 없어서 꾸미는건 나중으로 미뤄미뤄...ㅎ
물론 매우 간단하게 보이는 웹 페이지 수준이겠지만 Django를 사용했기 때문에 더 적은 양의 코드??와 시간으로 완성할 수 있었다고 봅니다ㅎㅎ
나만의 웹페이지를 잘 만들어보고 서버에 업로드 시켜서 다른 사람들도 사용할 수 있도록 '배포'를 해보면 좋을 것 같아요~
해당 프로젝트는 "베프의 오지랖 파이썬 웹 프로그래밍(디지털북스)"를 참고합니다.
'Django > Project' 카테고리의 다른 글
[Project] Ongstagram Service #2 (0) | 2021.06.02 |
---|---|
[Project] Ongstagram Service #1 (0) | 2021.06.01 |
[Project] Ong's BookMark Service #4 (0) | 2021.05.29 |
[Project] Ong's BookMark Service #3 (0) | 2021.05.29 |
[Project] Ong's BookMark Service #2 (0) | 2021.05.27 |