Skip to content

Django 템플릿과 css

기초 화면 구성, css와 Bootstrap을 활용한 스타일 적용


0. 개요

바로 로그인/로그아웃, 회원가입 등 주요 기능으로 넘어가고 싶지만, 화면이 없어 만들어도 확인을 할 수가 없으니 일단 화면부터 만들기로 한다.

1. 템플릿 폴더 설정

템플릿은 HTML을 동적으로 생성해주는 기능을 해서 Front-end 화면을 쉽게 만들 수 있게 해준다.

Django 프로젝트에서 사용할 템플릿을 생성하기 전에, 우선 config/settings.py에서 TEMPLATES 항목을 아래와 같이 수정해준다.

settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],  # use common templates folder
        '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',
            ],
        },
    },
]

위와 같이 설정하면 root 디렉토리의 templates 폴더에서 모든 템플릿을 통합하여 편리하게 관리할 수 있다.

2. 질문 목록/세부 내용 화면

2-1. view 생성

board_qnd/views.py 파일을 아래와 같이 수정해 질문 목록을 보여주는 index view와 질문의 세부 내용을 보여주는 detail view를 만들어 준다.

views.py
from django.shortcuts import render, get_object_or_404
from .models import Question

# Create your views here.


def index(request):
    """index view for question_list"""

    question_list = Question.objects.order_by('-id')  # order by id desc
    context = {'question_list': question_list}
    return render(request=request, template_name='board_qna/question_list.html', context=context)


def detail(request, question_id):
    """view for details of each question"""

    question = get_object_or_404(Question, pk=question_id)  # returns 404 instead of 500 when requested not existing question_id
    context = {'question': question}
    return render(request, 'board_qna/question_detail.html', context)

order_by는 QuerySet API로, QuerySet은 데이터베이스에서 가져온 객체들의 모음을 말한다. QuerySet API에 대한 자세한 설명은 공식 문서를 참고하자.

render는 템플릿과 데이터를 조합하여 HttpResponse 객체로 반환하는 함수로, 자세한 설명은 공식 문서를 참고하자.

2-2. URL 매핑

위에서 만든 view들의 링크를 매핑해주기 위해 board_qna/urls.py 파일을 아래와 같이 수정해준다.

urls.py
from django.urls import path
from . import views

app_name = 'board_qna'

urlpatterns = [
    path('', views.index, name='index'),  # name parameter is to set name of url variable for template
    path('<int:question_id>/', views.detail, name='detail'),  # url for listing board_qna
]

<int:question_id>에서 볼 수 있듯이 변수를 활용한 URL을 매핑해주려면 angle brackets으로 감싸주면 되고, 해당 변수를 특정 타입으로 변환하도록 지정할 수 있다. 자세한 내용은 공식 문서를 참고하자.

2-3. 템플릿 생성

앞에서 index view에서 지정한 question_list.html 템플릿을 templates/board_qna 폴더에 아래와 같이 생성해준다.

question_list.html
<h1>Hello World! Welcome to Q&A board.</h1>

{% if question_list %}
  <ul>
  {% for question in question_list %}
    <li><a href="{% url 'board_qna:detail' question.id %}">{{ question.id }} {{ question.subject }}</a></li>
  {% endfor %}
  </ul>
{% else %}
  <p>질문이 없습니다.</p>
{% endif %}

{% url [URL_name] %} 태그는 URL 매핑을 사용할 수 있도록 해준다. 자세한 내용은 공식 문서를 참고하자.

templates/board_qna 폴더에 question_detail.html 파일을 아래와 같이 생성해준다.

question_detail.html
<h1>{{ question.subject }}</h1>
<div>
  {{ question.content }}
</div>

admin 권한으로 샘플 데이터를 몇 개 생성한 후 확인해보면 결과물을 아래 화면과 같이 확인할 수 있다.

django_template_01

django_template_02

Django에서는 Django 템플릿 언어를 사용해서 템플릿을 작성한다. Jekyll에서 사용하는 liquid와 별 차이는 없는 것 같다.

3. 스타일 적용

3-1. static 기초 설정

static은 웹 페이지에 정적 디자인을 부여해준다. Django 프로젝트에 사용할 디자인을 통합하여 관리하기 위해 config/settings.py에서 Static 항목을 아래와 같이 수정해준다.

settings.py
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.1/howto/static-files/

STATIC_URL = 'static/'
STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

위와 같이 설정하면 root 디렉토리의 static 폴더에서 모든 스타일을 통합하여 편리하게 관리할 수 있다.

3-2. 스타일시트 작성

원래라면 static/style.css 파일을 생성해 css로 스타일을 만들어줘야 하나 빠르게 구현해보기 위해 Bootstrap을 사용하기로 한다. 본 프로젝트에서는 v5.2를 사용했다.

Bootstrap 홈페이지에서 Compiled CSS and JS를 다운받아 bootstrap.min.css 파일과 bootstrap.min.js 파일을 static 디렉토리에 저장하자.

3-3. 기본 템플릿 생성 및 포함

templates 디렉토리에 <html>, <head>, <body> 태그를 포함하여 표준 HTML 문서의 구조를 가지고, 템플릿 상속을 통해 다른 템플릿의 기초가 되는 base.html을 먼저 생성한다.

base.html
{% load static %}
<!doctype html>
<html lang="ko">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" type="text/css" href="{% static 'bootstrap.min.css' %}">
    <!-- board_qna CSS -->
    <link rel="stylesheet" type="text/css" href="{% static 'style.css' %}">
    <title>Hello, World!</title>
  </head>
  <body>
  <!-- nav bar -->
  {% include "navbar.html" %}
  <!-- content start -->
  {% block content %}
  {% endblock %}
  <!-- content end -->
  <!-- Bootstrap JS -->
  <script src="{% static 'bootstrap.min.js' %}"></script>
  </body>
</html>

템플릿 포함

{% include [source] %} 태그는 다른 템플릿을 포함시킨다는 뜻으로, 아래 코드는 해당 위치에 navbar.html을 포함시켜서 같이 렌더링 한다는 뜻이다.

{% include "navbar.html" %}

스타일 적용

템플릿에 스타일을 적용하려면 아래와 같이 {% load static %} 태그와 <link> 태그를 사용해서 연결해주면 된다.

base.html
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'style.css' %}">

3-4. 내비게이션 바 추가

templates 디렉토리에 아래와 같이 navbar.html 템플릿을 생성해준다.

navbar.html
<nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
  <div class="container-fluid">
    <a class="navbar-brand" href="/">Hello World!</a>
    <button
      class="navbar-toggler"
      type="button"
      data-bs-toggle="collapse"
      data-bs-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 me-auto mb-2 mb-lg-0">
        <li class="nav-item">
          <a class="nav-link" href="{% url 'board_qna:index' %}">Q&A</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">로그인</a>
        </li>
      </ul>
    </div>
  </div>
</nav>

내비게이션 바는 모든 화면 상단에서 공통적으로 보여줘야 하므로 아래와 같이 base.html 파일에서 <body>의 가장 위에 {% include [source] %} 태그를 이용하여 공용 템플릿에 포함시켜준다.


Reference