Django의 템플릿 언어

Jun·2021년 4월 22일
0

Django

목록 보기
1/4
post-thumbnail

Django의 템플릿 언어에 대해 공식 문서의 내용을 정리해보자.^0

Django의 템플릿 언어는 실제 렌더링 되는 대상인 html 파일을 쉽게 작성할 수 있게 한다. 또한 익숙한 파이썬 키워드들을 통해 템플릿이 제공받는 변수들에 접근가능하게 한다.

템플릿 언어에서는 몇 가지 파이썬 태그들과 필터들을 제공한다. 템플릿 언어의 모양새가 파이썬과 비슷하긴 하지만 이를 렌더링 할 때 이 안의 파이썬 코드가 무작위로 실행되는 것은 아니다.

템플릿 파일

Django에서 템플릿이란 어떤 텍스트 파일을 의미한다.

템플릿을 통해 HTML, XML, CSV 등 어떠한 텍스트 기반 포맷의 파일도 생성할 수 있다. 템플릿은 렌더링시 실제 데이터로 대체되는 변수를 가지며 그 안의 로직을 통제 가능한 태그도 포함할 수 있다.

변수

템플릿 안에서 변수는 {{ variable }}과 같이 두 개의 중괄호로 감싸 표현한다.

변수명은 영문자와 언더스코어로 구성하고, "."을 통해 변수의 딕셔너리, 속성, 메서드, 인덱스를 차례대로 검색하며 접근한다. callable 속성에 접근하면 해당 함수를 인자 없이 호출하고 호출된 결과값으로 변수 부분을 대체한다.

변수가 존재하지 않을 시 템플릿 세팅에 있는 string_if_invalid 값이 입력된다. (디폴트는 '')

필터^1

pipe(|)와 필터를 사용해서 변수에 필터를 적용하고 체이닝(chaining)도 할 수 있다.

# 변수가 비어있으면 디폴트 문자열을 사용한다.
{{ value|default:"nothing" }}

# 변수의 길이를 리턴한다.
{{ value|length }}

태그^2

태그는 {% %} 내부에서 사용한다. 템플릿에서 반복문이나 조건문을 만들 수 있고 외부 정보를 로드해올 수 도 있다.

# 반복문 태그
<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>

# 조건문 태그
{% 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 %}

# 조건문 태그 + 필터링
{% if athlete_list|length > 1 %}
   Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
   Athlete: {{ athlete_list.0.name }}
{% endif %}

주석

{# #} 구문을 통해 템플릿 파일에 주석을 달 수 있다.

템플릿 상속

템플릿 상속은 Django의 템플릿 엔진에서 가장 중요한 부분이다. 상속을 통해 전체 뼈대를 짜 두고 각 부분을 block으로 정의해서 해당 템플릿을 상속하는 템플릿이 그 부분을 오버라이드 할 수 있게 한다.

부모 템플릿을 상속하는 템플릿은 extends 태그를 이용해 부모 템플릿을 오버라이드하고 상속자 템플릿의 내용에 따라 부모 템플릿의 block 부분을 대체한다.

이때 부모 템플릿의 block 태그 안에 있는 내용은 상속자 템플릿이 오버라이드 하지 않았을 때 들어가는 fallback 이라고 보면 되겠다.

부모 템플릿

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css">
    <title>{% block title %}My amazing site{% endblock %}</title>
</head>

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

    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

상속자 템플릿

{% extends "base.html" %}

{% block title %}My amazing blog{% endblock %}

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

결과 html

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css">
    <title>My amazing blog</title>
</head>

<body>
    <div id="sidebar">
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
    </div>

    <div id="content">
        <h2>Entry one</h2>
        <p>This is my first entry.</p>

        <h2>Entry two</h2>
        <p>This is my second entry.</p>
    </div>
</body>
</html>

템플릿 상속 시 유의할 사항들

  • {% extends %} 태그는 상속자 템플릿의 첫 태그여야 상속이 발생한다.
  • 템플릿 작성 시 반복되는 사항이 발생하면 block화 하는 것이 좋다.
  • {{ block.super }}를 통해 부모 템플릿의 블록 내용을 가져올 수 있다.
  • {% block %} 외부에서 as 구문으로 생성된 변수는 블록 내부에서 사용될 수 없다.
  • {% endblock <blockname> %}식으로 블록 끝에도 블록 이름을 줄 수 있다.
  • 같은 템플릿 안에서는 같은 이름을 갖는 block 태그는 쓸 수 없다.

자동 HTML 이스케이프

Hello, {{ name }}과 같은 템플릿에 name 변수로 <script>alert('hello')</script> 과 같은 렌더링 될 수 있는 코드가 들어올 수 있다.
또는 변수에 <과 같은 문자가 포함되어 템플릿이 변수에 렌더링 될 시 템플릿 HTML의 완전성에 피해를 줄 수 있다. 이러한 취약점을 허용할 시 악성 유저에 의해 Cross Site Scripting(XSS) 공격이 발생할 수 있다.

이를 방지하기 위해 잠재적으로 위험한 HTML이 될 수 있는 요소들을 걸러내는 필터가 디폴트로 Django에 존재하며, 자동으로 아래와 같은 HTML 이스케이핑을 수행한다.

  • < -> &lt
  • > -> &gt
  • '(홑따옴표) -> &#x27
  • "(쌍따옴표) -> &quot
  • & -> &amp

HTML 오토 이스케이핑을 원하지 않는다면?

특정 변수는 의도적으로 HTML 요소를 포함하도록 설계되어있을 수 있기에 자동 이스케이핑을 끌 방법이 필요하다. 이를 위해서는 이스케이핑을 해제하고 싶은 변수에 safe 필터를 적용시키면 된다.

This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
This will be escaped: &lt;b&gt;
This will not be escaped: <b>

또는 템플릿 전체에 대해 이스케이핑을 끄고 싶을 경우 autoescape 태그를 사용할 수 있다.

{% autoescape off %}
    Hello {{ name }}
{% endautoescape %}

더욱 복잡하게도 가능하며, autoscape 블록의 효과는 해당 블록 내부에 존재하는 자식 블록들에게도 적용된다.

Auto-escaping is on by default. Hello {{ name }}

{% autoescape off %}
    This will not be auto-escaped: {{ data }}.

    Nor this: {{ other_data }}
    {% autoescape on %}
        Auto-escaping applies again: {{ name }}
    {% endautoescape %}
{% endautoescape %}

디폴트 문자열은 이미 이스케이프 되어있다

{{ data|default:"This is a string literal." }}

변수의 디폴트 폴백값을 줄 경우 이 디폴트 문자열은 마치 이미 safe 필터를 통과한 것 처럼 이미 이스케이프 되어있다. 이는 디폴트 값은 개발자가 컨트롤 하기 때문이다.

그러므로 디폴트 문자열을 줄때는 미리 이스케이프되어 Django에 의해 자동 처리 되지 않음을 염두해 두어야 한다.

{{ data|default:"3 < 2" }}  {# Bad! Don't do this. #}

템플릿에서 메서드 호출하기

템플릿은 변수의 클래스 속성 뿐만 아니라 메서드에까지 접근이 가능하다.
예를들어, QuerySets 클래스의 count() 메서드는 다음과 같이 접근 가능하다.

{{ task.comment_set.all.count }}

이 뿐 아니라 커스텀으로 정의된 모델에 붙어있는 메서드 또한 템플릿에서 접근이 가능하다.

커스텀 태그와 필터 라이브러리^3

특정 Django 앱은 커스텀 태그와 필터 라이브러리를 제공하기도 한다. 이 경우 해당 앱을 세팅의 INSTALLED_APPS에 추가하고 템플릿에 해당 앱을 추가하면 된다. 대신 이렇게 추가된 기능은 해당 템플릿에서만 사용 가능하며, 부모나 자식등 상속 관계에 까지는 적용되지 않는다.

profile
개발합니다.

0개의 댓글