Jinja2 소개
Jinja2는 서버에서 완성된 HTML을 만들어 넘겨주는 서버 사이드 렌더링(SSR)에 사용되는 Python 기반의 템플릿 엔진인데요. 보통 Flask 같은 웹 프레임워크에서 HTML을 동적으로 생성하고자 할 때 많이 사용됩니다.
Jinja2 시작하기
대부분 Flask와 함께 사용하게 되는데 이 경우 Flask에 Jinja2가 포함되어 있기 때문에 따로 패키지 등을 설치하지 않아도 됩니다.
기본적인 정보 전달
Jinja2에서는 서버로부터 키-값의 형태로 정보를 전달받게 되는데요. Flask에서 render_template 을 통해 페이지를 이동시킬 때 키=값의 형태로 데이터들을 함께 전달하게 됩니다. 이때 하나의 값만을 전달할 수도 있지만 여러 값을 담은 데이터, 그리고 그 데이터들을 담은 리스트 또한 전달할 수 있습니다.
하나의 값 전달하기
def test_func():
render_template('index.html', name="홍길동")
딕셔너리 값 전달하기
여러 키-값을 중괄호로 감싸 딕셔너리로 만들어준 뒤 전달합니다.
def test_func():
user = {
"name": "홍길동",
"is_admin": True,
"age": 25,
"email": "test@example.com"
}
render_template('index.html', user=user)
딕셔너리 리스트 전달하기
여러 딕셔너리들을 대괄호로 감싸 리스트로 만들어준 뒤 전달합니다.
def test_func():
users = [
{"name": "홍길동", "email": "test1@example.com"},
{"name": "김길동", "email": "test2@example.com"}
]
return render_template('index.html', users= users)
Jinja2 활용하기
기본적인 정보 활용
서버로부터 받은 데이터를 {{ 키이름 }} 형태로 해당 키의 값을 활용할 수 있습니다.
하나의 값을 받았을 때
<!-- 서버에서 name = "홍길동" 을 넘겨받았을 때 "안녕하세요, 홍길동님!" 이 출력 -->
<p>안녕하세요, {{ name }}님!</p>
딕셔너리 값을 받았을 때
키이름에 . (마침표) 를 찍어 해당 딕셔너리의 특정 속성 값을 얻을 수 있습니다.
<!-- 서버에서 user = { "name": "홍길동", "age": 27 } 을 받았을 때 -->
<p>고객명 : {{ user.name }}</p>
<p>나이 : {{ user.age }}</p>
딕셔너리 리스트를 받았을 때
리스트 인덱스를 통해 특정 딕셔너리에 접근하고, 이를 딕셔너리 값을 받았을 때와 동일하게 활용할 수 있는데요. 다만 딕셔너리 리스트를 활용할 때 리스트 인덱스보다는 보통 반복문을 더 많이 사용합니다. 반복문에 대한 설명은 아래에서 다루고 있습니다.
<!-- 서버로부터 users = [{ "name": "홍길동", "age": 27 }, { "name": "김영미", "age": 32 }] 을 받았을 때 -->
<p>첫번째 고객명 : {{ users[0].name }}</p>
<p>첫번째 고객 나이 : {{ users[0].age }}</p>
<p>두번째 고객명 : {{ users[1].name }}</p>
<p>두번째 고객 나이 : {{ users[1].age }}</p>
조건문 사용하기
서버로부터 받은 데이터를 조건문에도 활용할 수 있는데요. 이를 활용해서 조건에 맞는 HTML 코드를 출력할 수 있습니다.
<!-- 서버에서 name = "홍길동" 을 넘겨받았을 때 조건에 맞는 문장이 출력된다. -->
{% if name == "홍길동" %}
<p>홍길동이 맞습니다.</p>
{% elif name == "김길동" %}
<p>홍길동이 아니라 김길동입니다.</p>
{% else %}
<p>홍길동도 김길동도 아닙니다.</p>
{% endif %}
반복문 사용하기
jinja2에서는 아쉽게도 while 반복문은 사용할 수 없고 for 반복문을 사용할 수 있습니다. 이를 통해 데이터의 목록을 받았을 때 각 데이터에 대한 정보들을 HTML 코드로 출력할 수 있습니다.
<!-- 서버에서 고객들의 이름과 이메일이 담긴 users를 넘겨받았을 때 -->
{% for user in users %}
<p>고객명 : {{ user.name }} / 이메일 : {{ user.email }}</p>
{% endfor %}
<!-- 출력 -->
<!-- 고객명 : 홍길동 / 이메일 : test1@example.com -->
<!-- 고객명 : 김길동 / 이메일 : test2@example.com -->
필터 함수 사용하기
서버로부터 받은 데이터를 필터를 활용해서 다양하게 변형할 수 있는데요. 기본적으로 키이름 뒤에 | (바) 를 붙이고 원하는 필터 함수를 붙여 {{ 키이름 | 필터 }} 형태로 사용할 수 있습니다. 자주 쓰이는 필터들과 함께 그 활용법을 소개해보고자 합니다.
문자열 관련 필터 활용
- lower : 문자열을소문자로 변환
- upper : 문자열을 대문자로 변환
- capitalize : 첫 글자만 대문자로 변환
- title : 모든 단어 첫 글자를 대문자로 변환
- replace("A", "B") : 특정 문자열을 다른 문자열로 변경
- trim : 문자열의 앞뒤 공백 제거
<!-- 서버로부터 text = "Hello world" 를 받았을 때 -->
<p>{{ "Hello World" | lower }}</p> {# 출력: hello world #}
<p>{{ "hello world" | capitalize }}</p> {# 출력: Hello world #}
<p>{{ "hello world" | title }}</p> {# 출력: Hello World #}
리스트 관련 필터 활용
- length : 리스트(또는 문자열)의 길이 반환
- first : 리스트 첫 번째 요소 반환
- last : 리스트 마지막 요소 반환
- reverse: 리스트 뒤집기
- sort : 리스트 정렬
- unique : 중복 제거
- join(", ") : 리스트를 문자열로 합치기
- map(attribute="속성명") : 각 요소의 특정 속성 값들만 추출
<!-- 서버로부터 users = [{ "name": "홍길동", "age": 27 }, { "name": "김영미", "age": 32 }]를 받았을 때 -->
<p>총 사용자 수: {{ users | length }}</p> {# 출력: 총 사용자 수: 2 #}
<p>첫 번째 사용자: {{ users | first }}</p> {# 출력: 첫 번째 사용자: {'name': '홍길동', 'age': 27} #}
<p>마지막 사용자: {{ users | last }}</p> {# 출력: 마지막 사용자: {'name': '김영미', 'age': 32} #}
<p>이름 목록: {{ users | map(attribute="name") | list }}</p> {# 출력: 이름 목록: ['홍길동', '김영미'] #}
조건 관련 필터 활용
- default("기본값") : 값이 None(또는 빈 값)인 경우 설정한 기본값 반환
- int : 문자열을 정수로 변환
- float : 문자열을 실수로 변환
- abs : 절댓값 변환
<!-- 서버로부터 user = { "name": "홍길동", "age": 27, "score":80 }를 받았을 때 -->
<p>이메일: {{ user.email | default("이메일 없음") }}</p> {# 출력: 이메일: 이메일 없음 #}
<p>점수: {{ user.score | string+"점" }}</p> {# 출력: 점수: 80점 #}
<p>절댓값: {{ -10 | abs }}</p> {# 출력: 절댓값: 10 #}
기타 유용한 필터 활용
- safe : HTML 태그가 그대로 표시되도록 처리
- escape : HTLM 태그를 이스케이프 처리(XSS 방지 가능)
- truncate(10, True) : 문자열이 일정 길이 이상이면 뒷 내용을 ...처리
<p>{{ "<script>alert('XSS')</script>" | escape }}</p> {# HTML 이스케이프 처리 #}
<p>{{ "Hello World" | truncate(5, True) }}</p> {# Hello... #}
템플릿 상속하기
기본 레이아웃 템플릿을 만든 다음 편리하게 재사용할 수 있는 기능도 제공하고 있는데요. 부모 템플릿을 만들고 자식 템플릿에서 이를 상속받아 원하는 내용만 바꿔서 쓸 수 있습니다. 겹치는 내용이 많다면 편리하게 이용할 수 있겠죠?
부모 템플릿 예시
{% block 원하는변수명 %} ~~ {% endblock %} 형식으로 자식 템플릿에서 바꿔 쓸 수 있는 부분을 지정해 둘 수 있습니다.
<!-- 부모 템플릿 base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}기본 제목{% endblock %}</title>
</head>
<body>
<header>공통 헤더</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>공통 푸터</footer>
</body>
</html>
자식 템플릿 예시
먼저 {% extends "부모템플릿.html %} 을 통해 부모 템플릿을 상속받은 뒤, 부모 템플릿에서 지정해 놓은 부분들을 {% block 지정한변수명 %}바꿀 내용{% endblock %} 형식을 통해 자유롭게 바꿀 수 있습니다.
<!-- 자식 템플릿 home.html -->
{% extends "base.html" %}
{% block title %}홈페이지{% endblock %}
{% block content %}
<p>환영합니다!</p>
{% endblock %}
출력 결과
<!-- 사이트 제목 : 홈페이지 -->
공통 헤더
환영합니다!
공통 푸터
마치면서
입학 시험에서 AJAX를 활용한 클라이언트 사이드 렌더링(CSR)만을 사용하다 jinja2를 이용해 서버 사이드 렌더링(SSR)을 사용하게 되니 문법부터 배워야 하고, 페이지도 이동시켜야 하고 불편하고 복잡한 것 같은 느낌입니다.
그렇지만 완성된 HTML을 클라이언트에게 바로 제공해 줄 수 있어 사용자 컴퓨터 사양에 관계없이 일정한 성능으로 웹사이트를 제공해 줄 수 있고, 서버 안에서 모든 데이터를 처리하기 때문에 보안적으로도 강력하다는 점이 참 매력적인 것 같기도 합니다.
당장 jinja2를 배워서 활용해야 한다면 기본적인 데이터 전달과 활용법, 그리고 jinja2만의 조건문, 반복문들을 우선적으로 보고 써먹으시면 좋을 것 같습니다.
'크래프톤 정글 > CS기초(키워드, 개념정리)' 카테고리의 다른 글
[CS기초] 알고리즘 복잡도(Big-O Notation) (0) | 2025.03.19 |
---|---|
[CS기초] 반복문(Loop), 재귀 함수(Recursion Function) (0) | 2025.03.19 |
[CS기초] 배열(Array), 문자열(String) (0) | 2025.03.19 |
[CS기초] 32비트 vs 64비트 차이점 (0) | 2025.03.18 |
[인 더 정글] JWT, 이것만 알고 가기 (0) | 2025.03.14 |