Juni_Dev_log

첫 페이지 만들기 ② "개발 코딩하기" with Django 본문

Project/첫 페이지 만들기(Django)

첫 페이지 만들기 ② "개발 코딩하기" with Django

Juni_K 2020. 9. 1. 15:59

작업/코딩 순서에서 설명한 것처럼 테이블에는 변화가 없기 때문에, 모델 코딩을 불필요하다.

URLconf 코딩부터 시작한다.

 

① 뼈대 만들기

앱을 신규로 만드는 것이 아니므로, 뼈대 작업은 없다.

② 모델 코딩하기

테이블 설계 단계에서 이미 파악한 내용이다. 테이블에 변경사항은 없다.

 

③ URLconf 코딩하기

mysite/urls.py 파일에 임포트 문장 및 루트(/) URL 두 줄만 추가하면된다.

뷰 클래스는 HomeView, URL 패턴명은 'home'으로 정의한다.

 

(mysite/urls.py)

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from mysite.views import HomeView

urlpatterns = [
    path('admin/', admin.site.urls),
		# include() 함수를 통해서, 북마크앱/블로그앱의 APP_URLCONF로 처리를 위임한다.
		path('bookmark/', include('bookmark.urls')),
		path('blog/', include('blog.urls')),
		path('', HomeView.as_view(), name='home')
]

: path('', HomeView.as_view(), name='home')

 

④ 뷰 코딩하기

URLconf 에서 지정한 HomeView 를 코딩한다.

HomeView는 특별한 처리 로직없이 단순히 템플릿만 보여주는 로직이므로, TemplateView 제너릭 뷰를 상속받아 코딩한다.

mysite 하위에 views.py 를 만들어서 코딩을 진행한다.

 

(mysite/views.py)

from django.views.generic import TemplateView

#TemplateView
# generic 뷰를 상속받아 사용한다.
class HomeView(TemplateView):
    # template 이름은 'home.html'으로 한다.
    template_name = 'home.html'

 

⑤ 템플릿 코딩하기 - 부트스트랩 메인 메뉴 : home.html

템플릿 코딩은 조금 복잡하다.

첫 페이지의 템플릿을 코딩할 파일의 이름은 home.html 이다.

views.py 에서 지정한 파일명이다. 파일의 위치는 개별 애플리케이션 템플릿이 아니라, 프로젝트 템플릿이므로, 프로젝트 템플릿 디렉터리에 생성한다.

 

(settings.py)

TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]

home.html 에는 모든 페이지에서 공통으로 사용하는 제목과 메뉴도 포함되는데, 장고의 상속 기능을 활용하여 base.html 로 옮길 예정이다.

 

부트스트랩에 들어가서, Documentation > Components > Navbar 를 들어간다.

부트스트랩의 CDN 링크를 소스에 추가해야한다.

 

(home.html)

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">

	<title>Django Web Programming</title>

	<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>

<body>
	<nav class="navbar navbar-expand-lg navbar-light bg-primary">
		<a class="navbar-brand" href="#">Navbar</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>
				<li class="nav-item">
					<a class="nav-link" href="#">Link</a>
				</li>
				<li class="nav-item dropdown">
					<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
						Dropdown
					</a>
					<div class="dropdown-menu" aria-labelledby="navbarDropdown">
						<a class="dropdown-item" href="#">Action</a>
						<a class="dropdown-item" href="#">Another action</a>
						<div class="dropdown-divider"></div>
						<a class="dropdown-item" href="#">Something else here</a>
					</div>
				</li>
				<li class="nav-item">
					<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
				</li>
			</ul>
			<form class="form-inline my-2 my-lg-0">
				<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
				<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
			</form>
		</div>
	</nav>

	<div class="container bg-warning" style="margin-top:30px;">
		<h4>This is CONTENT area.</h4>
	</div>

	<footer class="fixed-bottom bg-info">
		<h4>This is FOOTER area.</h4>
	</footer>
	
	<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
	<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>

</body>
</html>

: 하단 <script> 태그에 부트스트랩 CDN 링크를 추가했다.

 

 

⑥ 템플릿 코딩하기 - 상속 기능 : base.html

home.html 코드에 장고의 상속 기능을 적용해서, base.html 과 home.html 두 개의 파일로 나눈다.

상속 기능을 사용하는 이유는, 모든 페이지에 공통인 메인 메뉴를 base.html 파일에 코딩하고, 각 페이지에서는 base.html 코드를 재활용하기 위함이다.

 

장고의 템플릿 상속은 보통 3단계로 구성된다. 우리의 예제는 2단계로 구성한다.

 

base.html 파일에 입력한다. 모든 페이지에서 공통으로 사용하는 제목과 메뉴, 그리고 상속 기능에 맞춰 페이지 구성요소들을 배치하는 {% block %} 태그 기능이 들어있다.

 

(base.html)

<!DOCTYPE html>
<html lang="ko">
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">

	<!-- title은 페이지마다 달라지기 때문에, 태그를 사용한다. -->
	<title>{% block title %}Django Web Programming{% endblock %}</title>

	<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">

	<!-- style 태그를 추가할 가능성이 있기 때문에, {% block %} 을 이용했다. -->
	{% block extra-style %}{% endblock %}
</head>

<body>
	<nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top">
		<a class="navbar-brand" href="#">Navbar</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>
				<li class="nav-item">
					<a class="nav-link" href="#">Link</a>
				</li>
				<li class="nav-item dropdown">
					<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
						Dropdown
					</a>
					<div class="dropdown-menu" aria-labelledby="navbarDropdown">
						<a class="dropdown-item" href="#">Action</a>
						<a class="dropdown-item" href="#">Another action</a>
						<div class="dropdown-divider"></div>
						<a class="dropdown-item" href="#">Something else here</a>
					</div>
				</li>
				<li class="nav-item">
					<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
				</li>
			</ul>
			<form class="form-inline my-2 my-lg-0">
				<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
				<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
			</form>
		</div>
	</nav>

	<div class="container bg-warning" style="margin-top:30px;">
		<!-- 내용은 각 페이지마다 달라지기 때문에 block 처리를 했다. -->
		{% block content %}{% endblock %}
	</div>

	{% block footer %}{% endblock %}

	<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
	<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>

	{% block extra-style %}{% endblock %}

</body>
</html>
  • {% block title %} : 하위 페이지마다 페이지 제목을 다르게 정의할 수 있다.
  • {% block extra-style %} : base.html 에서 사용하는 base.css 파일 이외에, 하위 페이지에서 필요한 CSS 파일을 정의할 수 있다.
  • {% block content %} : 하위 페이지마다 실제 본문 내용을 정의할 수 있다.
  • {% block footer %} : 하위 페이지마다 FOOTER 내용을 다르게 정의할 수 있다.

 

⑦ 템플릿 코딩하기 - 상속 기능 : home.html

상위 base.html 파일이 준비되었으므로, home.html을 base.html 파일을 상속받는 방식으로 변경한다.

 

템플릿 상속기능을 통해서, home.html 코드가 간단해지고, base.html 코드를 재사용하고 있다는 점이 굉장히 중요하다.

home.html 파일의 내용을 변경한다. 상위 base.html 파일에서 정의한 블록태그들을 하위 home.html 파일에서 오버라이딩하면 된다.

 

(home.html)

{% extends 'base.html' %}

{% block title %}home.html{% endblock %}

{% block content %}
	<h4>This is CONTENT area.</h4>
{% endblock %}

{% block footer %}
<footer class="fixed-bottom bg-info">
	<h4>This is FOOTER area.</h4>
</footer>
{% endblock %}
  • {% extends 'base.html' %} : base.html 파일을 상속받는다.
  • {% block title %}home.html{% endblock %} : title 을 재정의한다.

base.html 파일에서 정의한 extra-style 블록과 extra-script 블록처럼 하위 home.html 파일에서 필요하지 않으면 사용하지 않아도 된다.

runserver 를 실행하고 확인해보자.

 

⑧ 템플릿 코딩하기 - base.html 완성

bootstrap 을 사용했기 때문에, 이제 우리 프로젝트에 맞게 수정해보자.

 

(base.html)

<!DOCTYPE html>
<html lang="ko">
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">

	<!-- title은 페이지마다 달라지기 때문에, 태그를 사용한다. -->
	<title>{% block title %}Django Web Programming{% endblock %}</title>

	<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">

	{% block extra-style %}{% endblock %}
</head>

<body style="padding-top:90px;">
	<nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top">
		<span class="navbar-brand mx-5 mb-0 font-weight-bold font-italic">Django - Python Web Programming</span>
		<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 mx-1 btn btn-primary">
					<a class="nav-link text-white" href="{% url'home'%}">Home</a>
				</li>
				<li class="nav-item mx-1 btn btn-primary">
					<a class="nav-link text-white" href="{% url 'bookmark:index' %}">Bookmark</a>
				</li>
				<li class="nav-item mx-1 btn btn-primary">
					<a class="nav-link text-white" href="{% url 'blog:index' %}">Blog</a>
				</li>
				<li class="nav-item mx-1 btn btn-primary">
					<a class="nav-link text-white" href="">Photo</a>
				</li>
				<li class="nav-item dropdown mx-1 btn btn-primary">
					<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
						Until
					</a>
					<div class="dropdown-menu">
						<a class="dropdown-item" href="{% url 'admin:index' %}">Admin</a>
						<div class="dropdown-divider"></div>
						<a class="dropdown-item" href="{% url 'blog:post_archive' %}">Archive</a>
						<a class="dropdown-item" href="">Search</a>
					</div>
				</li>
			</ul>
			<form class="form-inline my-2 my-lg-0" action="" method="post">{% csrf_token %}
				<input class="form-control mr-sm-2" type="search" placeholder="global_search" name="search_word" aria-label="Search">
			</form>
		</div>
	</nav>

	<div class="container">
		<!-- 내용은 각 페이지마다 달라지기 때문에 block 처리를 했다. -->
		{% block content %}{% endblock %}
	</div>

	{% block footer %}{% endblock %}

	<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
	<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
	<script src="https://kit.fontawesome.com/f614572e90.js" crossorigin="anonymous"></script>

	{% block extra-script %}{% endblock %}

</body>
</html>
  • {% csrf token %} : 보안을 위해서 토큰화시키는 것
  • fontawesome 에서 CDN 링크를 추가. 인증 후, Kit Code 와 Kit Code 에 따른 CDN 을 구할 수 있다.

https://fontawesome.com/

 

Font Awesome

The world’s most popular and easiest to use icon set just got an upgrade. More icons. More styles. More Options.

fontawesome.com

⑨ 템플릿 코딩하기 - home.html 완성

base.html 첫 페이지의 윤곽을 잡았다면, 첫 페이지의 내용. 즉 CONTENT 영역과 FOOTER 영역을 채우는 작업을 진행한다.

 

(home.html)

{% extends 'base.html' %}

{% load static %}

{% block title %}home.html{% endblock %}

{% block extra-style %}
<style type="text/css">
	.home-image{
		background-image: url("{% static 'img/cat.jpg' %}");
		background-repeat: no-repeat;
		background-position: center;
		background-size: 100%;
		height: 650px;
		border-top: 10px solid #ccc;
		border-bottom: 10px solid #ccc;
		padding: 20px 0 0 20px;
	}
	.title{
		color: #c80;
		font-weight: bold;
	}
	.powered{
		position: relative;
		top: 77%;
		color: #cc0;
		font-style: italic;
	}
</style>
{% endblock %}

{% block content %}
	<div class="home-image">
		<h2 class="title">Django - Python Web Programming</h2>
		<h4 class="powered"><i class="fas fa-arrow-circle-right"></i>powered by django and bootstrap</h4>
	</div>
	
	<hr style="margin: 10px 0;">

	<div class="row text-center">
		<div class="col-sm-6">
			<h3>Bookmark App</h3>
			<p>Bookmark is a Uniform Resource Identifier (URI) that is stored for later retrieval in any of various storage formats. you can store your own bookmarks by Bookmark application. It's also possible to update or delete your bookmarks.</p>
		</div>
		<div class="col-sm-6">
			<h3>Blog App</h3>
			<p>This application makes it possible to log daily events or write your own interests such as hobbies, techniques, etc. A typical blog combines text, digital images, and links to other blogs, web pages, and other media related to its topic.</p>
		</div>
	</div>
{% endblock content %}

{% block footer %}
<footer class="fixed-bottom bg-info">
	<div class="text-white font-italic text-right mr-5">Copyright &copy; 2020 DjangoBook by jhkim</div>
</footer>
{% endblock %}

{% static %} 템플릿 태그 기능에 따르면 cat.jpg 파일은 아래 위치에 있어야한다.

/ch99/static/img/cat.jpg

 


(이전 포스팅)

2020/09/01 - [Project/첫 페이지 만들기(Django)] - 첫 페이지 만들기 ① "애플리케이션 설계하기" with Django

 

첫 페이지 만들기 ① "애플리케이션 설계하기" with Django

장고의 MTV 패턴에 대해 익숙해졌을 것이다. 이제 좀 더 틀을 잡아서 본격적으로 실전에 사용할 수 있는 사이트를 만들어보자. 사용자가 우리 사이트에 접속했을 때, 처음으로 보여주는 페이지를

juni-dev-log.tistory.com

 

Comments