Jinja2의 정의
- Jinja2는 파이썬에서 가장 많이 사용되는 템플릿 엔진 중 하나입니다. Django의 템플릿 시스템에서 영감을 얻었지만 템플릿 작상자에게 더 강력한 도구 세트를 제공하는 표현 언어로 확장했습니다. 또한 보안이 중요한 애플리케이션을 위해 샌드박스 실행 및 선택적 자동 이스케이프를 추가합니다.
Flask 템플릿 렌더링 방법
- 기본적으로 Flask는 애플리케이션 폴더 안에 위치한 templates 서브폴더에서 템플릿을 검색한다.
- 그 곳에 위치한 HTML 파일을 이용하여 렌더링 한다.
**예제 코드** from flask import Flask, render_template #... @app.route('/user/<name>') def user(name): return render_template('user_html', name=name)
예제 코드
from flask import Flask, render_template
#...
@app.route('/user/<name>')
def user(name):
return render_template('user_html', name=name)
템플릿 엔진의 정의
- 서버에서 받아온 데이터를 효과적으로 보여줄 중간 매체
- 템플릿 엔진을 사용하면 비교적 간략한 표현(조건문, 변수 , 반복문)을 통해 효과적으로 데이터를 가공하여 웹페이지를 보여 줄 수 있다.
템플릿 엔진 설명의 예시
템플릿 엔진을 사용하는 이유
-
재사용성이 높다.
웹페이지 혹은 웹앱을 만들 때 똑같은 디자인의 페이지에서 보이는 데이터만 바뀌는 경우가 많다.한 페이지를 템플릿 엔진 문법으로 만들어 놓고 여기에 데이터를 바꿔가면서 렌더링을 해주면 수 백의 페이지들을 나타낼수 있다.
-
유지보수에 용이하다
템플릿 엔진을 사용하면 동일한 템플릿을 사용한다는 전제하에 템플릿과 넘겨주는 데이터만 수정하면 된다.
-
코드를 많이 줄일 수 있다.
대부분의 템플릿 엔진은 기존의 HTML에 비해서 간단한 문법을 사용한다. 코드가 길어지면 길어질 수록 템플릿 엔진을 사용하면 좋다.
템플릿 사용의 예시
Jinja2의 기본 문법
{{ . . . }}
변수 나 표현식
**{{ . . . }} 사용의 예시** **변수의 예시** {% for head in table['thead'] %} <th>{{ head }}</th> {% endfor %} **표현식의 예시** <h1 class="h3 mb-2 text-gray-800">{{ page['manager.index'].get('title') }}</h1> <p class="mb-4">{{ page['manager.index'].get('sub_title') }}</p>
{{ . . . }} 사용의 예시
변수의 예시
{% for head in table['thead'] %}
<th>{{ head }}</th>
{% endfor %}
표현식의 예시
<h1 class="h3 mb-2 text-gray-800">{{ page['manager.index'].get('title') }}</h1>
<p class="mb-4">{{ page['manager.index'].get('sub_title') }}</p>
{% . . . %}
if나 for 같은 제어문
**{%. . . %} 사용의 예시** {% for i in range(body|length) %} <tr> {% set index = body[0] %} {% for i in range(body|length) %} {% if i == 1 %} <td> <a href="{{ url_for('manager.item', id=[index]) }}"> {{ body[i] }} </a> </td> {% else %} <td> {% if body[i] == '' %} - {% else %} {{ body[i]|replace('\\n', '<br>')|safe }} {% endif %} </td> {% endif %} {% endfor %} </tr> {% endfor %}
{%. . . %} 사용의 예시
{% for i in range(body|length) %}
<tr>
{% set index = body[0] %}
{% for i in range(body|length) %}
{% if i == 1 %}
<td>
<a href="{{ url_for('manager.item', id=[index]) }}">
{{ body[i] }}
</a>
</td>
{% else %}
<td>
{% if body[i] == '' %}
-
{% else %}
{{ body[i]|replace('\n', '<br>')|safe }}
{% endif %}
</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
{# . . . #}
주석문
{# . . . #} **사용의 예시** <head> {# 주석문입니다 #} {% block header %} {% include '_common/header.html' %} {% endblock %} </head>
{# . . . #} 사용의 예시
<head>
{# 주석문입니다 #}
{% block header %}
{% include '_common/header.html' %}
{% endblock %}
</head>
필터
- Jinja2는 어떠한 타입의 변수라도 인식한다. (예 :리스트, 딕셔너리, 오브젝트 등)
- 변수는 필터를 사용하여 수정 할 수 있으며 | 기호를 분리자로 하여 추가한다.
필터 사용의 예시
**safe의 예시** <label class="col-sm-{{ label_size }} col-form-label {% if is_required %}required{% endif %}"> {{ label|safe }} </label> **replace와 safe의 예시** {% else %} {{ body[i]|replace('\\n', '<br>')|safe }} {% endif %} **length의 예시** {%for i in range(body|length)%} {% if i == 0 %}
safe의 예시
<label class="col-sm-{{ label_size }} col-form-label {% if is_required %}required{% endif %}">
{{ label|safe }}
</label>
replace와 safe의 예시
{% else %}
{{ body[i]|replace('\n', '<br>')|safe }}
{% endif %}
length의 예시
{%for i in range(body|length)%}
{% if i == 0 %}
제어구조
if문
**if문의 구조** {% if <조건> %} <실행코드> {% elif <조건> %} <실행코드> {% else %} <실행코드> {% endif %} **if문의 예시** {% if body[i] == '' %} - {% else %} {{ body[i]|replace('\\n', '<br>')|safe }} {% endif %}
if문의 구조
{% if <조건> %}
<실행코드>
{% elif <조건> %}
<실행코드>
{% else %}
<실행코드>
{% endif %}
if문의 예시
{% if body[i] == '' %}
-
{% else %}
{{ body[i]|replace('\n', '<br>')|safe }}
{% endif %}
for문
**for문의 구조** {% for <개별요소> in <리스트> %} <실행코드> {% endfor %} **for문의 예시** {% for body in table['tbody'] %} <tr> {% set index = body[0] %} {% for i in range(body|length) %} {% if i == 1 %} <td> <a href="{{ url_for('manager.item', id=[index]) }}"> {{ body[i] }} </a> </td> {% else %} <td> {% if body[i] == '' %} - {% else %} {{ body[i]|replace('\\n', '<br>')|safe }} {% endif %} </td> {% endif %} {% endfor %} </tr> {% endfor %}
for문의 구조
{% for <개별요소> in <리스트> %}
<실행코드>
{% endfor %}
for문의 예시
{% for body in table['tbody'] %}
<tr>
{% set index = body[0] %}
{% for i in range(body|length) %}
{% if i == 1 %}
<td>
<a href="{{ url_for('manager.item', id=[index]) }}">
{{ body[i] }}
</a>
</td>
{% else %}
<td>
{% if body[i] == '' %}
-
{% else %}
{{ body[i]|replace('\n', '<br>')|safe }}
{% endif %}
</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
include
css 나, 자바스크립트를 가져오는 header의 부분이나 상단의 navigation 부분, 하단의 footer 부분이 그러한데 일일히 모든 템플릿에 넣어 주기는 힘들다. 그래서 jinja2 에서는{% include %} 를 통해서 하나의 html 에서 다른 html 을 가져 올 수 있도록 해준다. 단순히 가져오는 것이라고 생각 할 수 있지만 내부적으로는 랜더링된 결과를 리턴한다고 한다.
- include의 다른 형태
리스트의 형태로 template를 지정할 수 있다. 첫번째 HTML파일을 찾지 못하면 두번째 HTML 파일을 보여주고 두번째 파일도 없고 ignore missing 이라는 옵션이 켜져있으면 에러를 내뱉지 않는다.
{% include ['첫번째.html', '두번째.html'] %} {% include ['첫번째.html', '두번째.html'] ignore missing %}
{% include ['첫번째.html', '두번째.html'] %}
{% include ['첫번째.html', '두번째.html'] ignore missing %}
block/extends(템플릿의 상속)
flask 템플릿은 계층 구조를 지원한다. 하나의 웹사이트는 일반적으로 동일한 테마를 갖는다. 각각의 페이지는 동일한 구성요소를 갖는데 이를 매번 html 파일에 삽입하게 되면 구성요소를 변경할 때, 모든 파일에 수정사항을 반영해야 한다.
사용되는 동일한 구성요소는 base.html파일에 넣어두고, 나머지 페이지들은 이를 상속(extends) 받아서 사용하도록 하였다. 이를 통해 base.html 파일 하나를 변경함으로써 전 페이지에 변경사항을 적용할 수 있다.
기타 템플릿
커스텀 에러 페이지
에러에 대해 커스텀 핸들러를 제공하는 예제 코드는 아래와 같다.
**기본형태** @app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404
아래는 BS Traders의 응용코드의 예시이다. 404.html을 만들어 404에러시 띄울 수 있다.
**응용 형태** @web.app_errorhandler(404) def page_not_found(e): if request.accept_mimetypes.accept_json and \\ not request.accept_mimetypes.accept_html: response = jsonify({'error': 'not found'}) response.status_code = 404 return response return render_template('error/layout.html', contents='error/404.html'), 404 **404.html** <!-- 404 Error Text --> <div class="text-center"> <div class="error mx-auto" data-text="404">404</div> <p class="lead text-gray-800 mb-5">Page Not Found</p> <p class="text-gray-500 mb-0">{{ info }}</p> <a href="/">← Back to Home</a> </div>
기본형태
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
응용 형태
@web.app_errorhandler(404)
def page_not_found(e):
if request.accept_mimetypes.accept_json and \
not request.accept_mimetypes.accept_html:
response = jsonify({'error': 'not found'})
response.status_code = 404
return response
return render_template('error/layout.html',
contents='error/404.html'), 404
404.html
<!-- 404 Error Text -->
<div class="text-center">
<div class="error mx-auto" data-text="404">404</div>
<p class="lead text-gray-800 mb-5">Page Not Found</p>
<p class="text-gray-500 mb-0">{{ info }}</p>
<a href="/">← Back to Home</a>
</div>
링크/정적파일
url_for()
템플릿에 직접 링크하는 것 처럼 URL을 작성하는 것은 간단한 라우팅 방법이지만 , 이는 원하지 않는 의존성을 생성하여 링크가 깨질 수 있다. 이러한 문제를 해결하기 위해 플라스크에서는 url_for() 헬퍼 함수를 제공하여 애플리케이션 URL맵에 저장된 정보를 통해 URL을 생성한다.
**기본형태** url_for('index') # '/' url_for('index', _external=True) # '<http://localhost:5000>' url_for('user', name='john', _external=True) # '<http://localhost:5000/user/john>' url_for('index', page=2) # '/?page=2' **예시** <form method="post" action="{{ url_for('buyer.new') }}"> ***정적인 파일도 url_for()을 사용해 불러 올 수 있다.** <link href="{{ url_for('static', filename='vendor/datatables/dataTables.bootstrap4.min.css') }}" rel="stylesheet">
기본형태
url_for('index') # '/'
url_for('index', _external=True) # 'http://localhost:5000'
url_for('user', name='john', _external=True) # 'http://localhost:5000/user/john'
url_for('index', page=2) # '/?page=2'
예시
<form method="post" action="{{ url_for('buyer.new') }}">
*정적인 파일도 url_for()을 사용해 불러 올 수 있다.
<link href="{{ url_for('static', filename='vendor/datatables/dataTables.bootstrap4.min.css') }}" rel="stylesheet">