티스토리 뷰

※본 자료는 김석훈, 『파이썬 웹 프로그래밍(개정판)』, 한빛미디어, pp151-218. 를 참고하여 만들었습니다.

 

3. 템플릿 시스템

 

 MTV 방식에서 UI를 담당하고 있는 기능이 템플릿 시스템입니다. 템플릿 코드를 작성시에는 HTML 코드와 장고의 템플릿 코드가 섞이지만, 중요한 점은 템플릿에서는 로직을 표현하는 것이 아니라 사용자에게 어떻게 보여줄지에 대한 룩앤필을 표현한다는 것입니다. 장고의 템플릿 시스템은 템플릿 문법으로 작성된 템플릿 코드를 해석하여 템플릿 파일로 결과물을 만들어줍니다. 이렇게 템플릿 코드를 템플릿 파일로 해석하는 과정을 장고에서는 렌더링이라고 합니다.

 또한, 템플릿 코드에 if 태그, for 태그 등이 있지만 이들은 파이썬 프로그래밍 언어의 문법과는 다른 것이며 템플릿 시스템에서만 사용되는 고유의 문법입니다.

 

(1) 템플릿 변수

 

  • {{ varialbe }}템플릿 시스템은 변수를 평가해서 변수값으로 출력해줍니다.※ Template 문법에서 . (dot)의 lookup 순서 ( ex. foo.bar )
    1. foo가 dict 타입인지 확인. dict 타입이라면 foo['bar']로 해석
    2. 그 다음 foo의 속성을 찾는다. bar라는 특성이 있으면 foo.bar로 해석
    3. 그것도 아니면 foo가 리스트인지 확인. 그렇다면 foo[bar]로 해석

      템플릿 시스템에서 정의되지 않은 변수를 사용하는 경우 default로 빈 문자열 (' ')로 채워줌

       settings.py의 TEMPLATE_STRING_IF_INVALID 를 지정하여 default 값을 바꿀 수 있음.

 

(2) 템플릿 필터

https://docs.djangoproject.com/en/3.0/ref/templates/builtins/

 

 필터란 일반적인 용어로 어떤 객체나 처리 결과에 추가적으로 명령을 적용하여 해당 명령에 맞게 최종 결과를 변경하는 것을 말합니다. 장고의 템플릿 문법에서도 템플릿 변수에 필터를 적용하여 변수의 출력 결과를 변경 할 수 있습니다.

 

{{ variable | filter }}

{{ variable | filter1 | filter2 }}

필터는 위의 예시와 같이 파이프 ( | ) 문자를 사용해서 나타냅니다. 두 번째와 같이 필터를 체인으로 연결 할 수도 있습니다.

 

  • {{ name | lower }} : name 변수의 모든 값을 소문자로 바꿔주는 필터입니다.
  • {{ text | escape | linebreak }} : text 변수 중에 특수문자를 이스케이프해주고(escape), 그 결과 스트링에 <P> tag를 붙여줍니다.(linebreaks)
  • {{ bio | truncatewords:30 }} : bio 변수값 중에서 앞에 30개의 단어만 보여주고 줄 바꿈 문자는 모두 없애줍니다.
  • {{ list | join : "//" }} : list=['a','b','c'] 라면 'a//b//c'가 결과물이 됨.
  • {{ value | default : "nothing" }} : value 변수값이 False 이거나 없는 경우 "nothing"으로 보여줍니다.
  • {{ list | length }} : list(혹은 string)의 경우 길이를 반환합니다.
  • {{ value | striptags }} : value 변수값에서 HTML 태그를 모두 없애준다. 그러나 100% 보장은 아니다.
  • {{ value | pluralize }} or  {{ value | pluralize:"es"}} : value 변수값이 1이 아니면 복수 접미사 s(or es)를 붙여줍니다. 필터의 인자값을 바꾸면 접미사를 바꿀 수 있습니다.
  • {{ value | add:"2" }} : value값에 2를 더하여 반환합니다. 만약 '+' 연산이 정의되지 않았다면 빈 문자열을 반환합니다.

 

(3) 템플릿 태그

 

 템플릿 태그는 {% tag %} 형식을 갖는다. 어떤 태그는 시작 태그와 끝 태그 둘 다 있어야 되기도 한다. 텍스트 결과물을 만들기도 하고, 템플릿 로직을 제어하기도 하며, 외부 파일을 템플릿 내로 로딩하기도 합니다. 

 템플릿 태그는 {% for %} 태그와 {% if %} 태그를 가장 많이 사용합니다. 이번 절에서는 가장 많이 사용되는 템플릿 태그와 폼 템플릿에서 많이 사용되는 {% csrf_token %} 태그 및 예제에서 사용했던 {% url %}, {% with %}, {% load %} 태그 등을 살펴보겠습니다.

 

  • {% for %} 태그

{% for %} 태그를 사용하면 리스트에 담겨 있는 항목들을 순회하면서 출력할 수 있습니다.

<ul>
  {% for athlete in athlete_list %}
      <li>{{ athlete.name }}</li>
  {% endfor %}
</ul>

 

변수명 설명
forloop.counter 현재까지 루프를 실행한 루프 카운트(1부터 카운트함)
forloop.counter0 현재까지 루프를 실행한 루프 카운트(0부터 카운트함)
forloop.revcounter 루프 끝에서 현재가 몇 번째인지 카운트한 숫자(1부터 카운트함)
forloop.revcounter0 루프 끝에서 현재가 몇 번째인지 카운트한 숫자(0부터 카운트함)
forloop.first 루프에서 첫 번째 실행하면 True 값을 가짐
forloop.last 루프에서 마지막 실행하면 True 값을 가짐
forloop.parentloop 중첩된 루프에서 현재의 루프 바로 상위의 루프를 의미함

 

  • {% if %} 태그

다음은 {% if %} 태그입니다. 변수를 평가하여 True이면 바로 아래의 문장이 표시됩니다.

 

{% if athlete_list %}
	Number of athletes : {{ athlete_list | length }}
{% elif athlete_in_locker_room_list %}
	Athletes should be out of the locker room soon!
{% else %}
	No athletes.
{% endif %}

 만약 athlete_list 변수가 True이면 운동 선수 숫자가 표시될 것이고, athlete_in_locker_room_list 변수가 True이면 그 아래 문장이 화면에 나타날 것입니다. 또한, 두 조건 모두 아니라면 No athletes. 문장이 출력됩니다.

 

{% if athlete_list | length > 1 %} 와 같이 and, or, not, and not, ==, !=, <, >, <=, >=, in, not in 와 같은 연산자를 같이 사용 할 수 있다.

 

  • {% csrf_token %} 태그

 POST 방식의 <form>을 사용하는 템플릿 코드에서는 CSRF공격을 방지하기 위해 {% csrf_token %} 태그를 사용해야 합니다. 폼 데이터에는 악의적인 스크립트 문장이 들어있을 수도 있기 때문입니다.

<form action="." method="POST">
{%csrf_token%}

 장고에서는 POST method를 사용하면서 csrf_toke을 쓰지 않으면 403 error을 일으킨다. 주의할 점은 CSRF 토큰값이 유출될 수도 있으므로, 외부 URL로 보내는 <form>에는 사용하지 않도록 한다.

 

  • {% url %} 태그

 {% url %} 태그는 URL의 하드코딩을 방지하기 위한 것이다.

{% url 'namespace:view-name' arg1 arg2 %}

namespce : urls.py 파일의 include() 함수 또는 app_name 변수에 정의한 namespace의 이름

view-name : urls.py 파일에서 정의한 URL 패턴 이름

argN : 뷰 함수에서 사용하는 인자로 있을 수도 있고 없을 수도 있다. 여러 개를 입력하는 경우 빈칸으로 구분한다.

 

  • {% with %} 태그
{% with total = business.employess.count %}
	{{ total }} people works at business
{% endwith %}

{% with %} 태그는 특정 값을 변수에 저장해두는 기능을 한다.

{% endwith %}를 통하여 {% with %} block을 닫아줌으로 변수의 범위를 설정한다.

 

  • {% load %} 태그
{% load somelibrary package.otherlibrary %}

{% load %} 태그는 사용자 정의 태그 및 필터를 로딩해준다. 

태그 및 필터는 장고에서 기본적으로 제공하는 것 외에도, 개발자가 필요에 따라 스스로 정의하여 사용 할 수 있다. 이런걸 사용자 정의 태그, 필터 라고 하는데 사용자 정의 태그, 필터를 사용하기 위해선 사용하기 전에 파일을 로딩해줘야 한다.

(https://docs.djangoproject.com/en/3.0/ref/templates/builtins/)

 

{% load somelibrary package.otherlibarary %} 

이 코드는 somelibrary.py 와 package.otherlibarary.py 파일에 정의된 사용자 정의 태그 및 필터를 로딩해준다. 

 

(4) 템플릿 주석

{# 한 줄 주석 #}
{% commnet %}
    여러 줄 주석 달기
    주석 안에 템플릿 태그가 들어있어도 작동하지 않는다.
    
    <ul>
	{% if polls_list %}
    		{% for poll in  polls_list %}
			<li> {{poll.text}} </li>
		{% endfor %}
        {% endif %}
    </ul>
    
{% endcomment %}

 

(5) HTML 이스케이프

 템플릿 코드를 렌더링 하여 HTML 텍스트를 만들 때, 주의해야 할 사항이 하나 있습니다. 만일 템플릿 변수에 HTML의 태그가 들어있는 경우 있는 그대로 렌더링하면 원하지 않는 결과가 나올 수도 있습니다. 따라서 장고에서는 자동 이스케이프 기능을 제공하여 이를 방지하여 줍니다.

 하지만, 자동이스케이프 기능을 비활성화시켜야 하는 경우도 발생합니다. 다음과 같은 두 가지 방법으로 비활성화가 가능합니다.

{{ data | safe }}
{% autoescape off %}

	Hello {{ name }}

{% endautoescape %}

 추가적으로, 필터의 인자에 사용되는 스트링 리터럴에는 자동 이스케이프 기능이 적용되지 않습니다.

{{ data|default: "3 < 5" }}      #스트링 리터럴에서는 자동 이스케이프 안 됨
{{ data | default: " 3 %lt 5" }} # 따라서 이 방법이 적절함

 

(6) 템플릿 상속

 템플릿 상속을 통해서 템플릿 코드를 재사용할 수 있고, 사이트의 룩앤필을 일관성 있게 보여줄 수 있다. 부모 템플릿은 템플릿의 뼈대를 만들어주고 {% block %} 태그를 통해 하위로 상속해줄 부분을 지정해주면, 자식 템플릿은 부모 템플릿의 뼈대는 그대로 재사용하고 {% block %} 부분만 채워주면 된다.

 

//부모템플릿

<!DOCTYPE html>
<html lang="en">
<head>
	<link rel="stylesheet" herf="style.css" />
    <title> {% block title %} </title>
</head>

<body>
	<div id="sidebar">
    	{% block sidebar %}
            <ul>
                <li><a href="/">Home</a></li>
                <li><a href="/blog">Blog</a></li>
            </ul>
	{% endblock sidebar %}
     	</div>
     
     <div id = "cntent">
     	{% block content%}
        {% endblock %}
     </div>
 </body>
 
 </html>

 

# 자식 템플릿

{% extends "parent.html" %}  #부모 템플릿 상속

{% block title%}
	My title
{% endblock %}

{% block content %}
{% for entry in blog_entries %}
    <h2> {{ entry.title }} </h2>
    <p> {{ entry.body }} </p>
{% endfor %}
{% endblock %}
    

 

 부모 템플릿에 {% block sidebar %} 가 선언되어 있지만 자식템플릿에서 sidebar bolck의 컨텐츠를 정의하고 있지 않기 때문에 부모의 내용이 그대로 출력됩니다.

 

사이트 전체적으로 조화로운 룩앤필을 위하여 일반적으로 템플릿 상속을 3단계로 사용하는 것을 권장하고 있습니다.

  1.  사이트 전체의 룩앤필을 담고 있는 base.html을 만듭니다.
  2. 사이트 하위의 섹션별 스타일을 담고 있는 템플릿을 만듭니다. 이때 base.html을 상속 받습니다.
  3. 개별 페이지에 대한 템플릿을 만듭니다. 개별페이지에 대한 템플릿은 2단계의 템플릿을 상속 받습니다.

템플릿 상속을 정의할 때는 다음 사항을 유의하여 사용해야 합니다.

  • {% extends %} 태그는 사용하는 태그 중에서 가장 먼저 나와야 합니다.
  • 템플릿의 공통 사항을 가능하면 많이 뽑아서 1단계 부모 템플릿에 {% block %} 태그가 많아질수록 좋습니다.
  • 부모 템플릿의 {%  block %} 안에 있는 내용을 그대로 사용하고 싶다면 자식 템플릿에서 {{block.super}} 변수를 사용하면 됩니다. 부모 템플릿의 내용을 그대로 사용하면서 자식 템플릿에서 내용을 추가하는 경우에 사용 할 수 있습니다.
  • 가독성을 높이시 위하여 {% endblock content %}처럼 블록명을 기입해도 됩니다.
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함