시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.
이전 내용
FastAPI와 Jinja2의 고급 문법
◆ 필터: {{ name | lower }}
변수에 함수를 적용한다.
[주로 사용되는 필터]
- capitalize: 문자열의 첫 글자를 대문자로 만든다
- lower: 문자열을 소문자로 만든다
- upper: 문자열을 대문자로 만든다
- title: 문자열의 각 단어의 첫 글자를 대문자로 만든다
- trim: 문자열의 앞뒤 공백을 제거한다
- replace: 문자열 내에서 지정한 부분을 다른 문자열로 교체한다
- length: 리스트나 문자열의 길이를 반환한다
- default: 변수가 정의되지 않았거나 None일 경우, 지정한 기본값을 반환한다
- round: 숫자를 지정한 자릿수로 반올림 한다
- float: 숫자나 문자열을 부동소수점 숫자로 변환한다
- int: 숫자나 문자열을 정수로 변환한다
- json: 파이썬 객체를 JSON 문자열로 변환한다
◆ 매크로: {% macro_my_macro(x,y) %} ... {% endmacro %}
코드를 재사용할 수 있는 블록을 정의한다.
FastAPI 코드
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/")
def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request, "name": "John"})
HTML 코드
<!DOCTYPE html>
<html>
<head>
<title>Filter & Macro Test</title>
</head>
<body>
<!-- 'lower' 필터 적용-->
<p>{{ name | lower }}</p>
<!-- 매크로 선언-->
{% macro greet(name) %}
<p>Hello, {{ name }}</p>
<p>Hello, Macro!</p>
{% endmacro %}
<!-- 매크로 호출-->
{{ greet(name) }}
{{ greet(name) }}
</body>
</html>
[테스트]
http://127.0.0.1:8000/ 에 접속하면
"Hello, John" 이라는 문구와 "Hello, Macro!" 가 두 번씩 출력된다. 매크로를 사용하여 여러 라인의 코드를 함수처럼 호출할 수 있다.
필터와 매크로는 각각 테스트할 수 있고, 같이 사용할 수도 있다.
필터는 변수를 쉽게 수정할 수 있게 해주고, 매크로는 코드를 재사용할 수 있게 해준다.
◆ 상속: {% extends 'base.html' %}
이 기능을 사용하면 기본 HTML 구조를 base.html 에 작성하고, 그 구조를 다른 HTML 템플릿에서 상속받을 수 있다.
이는 웹사이트에서 공통으로 사용하는 헤더, 푸터 등을 효율적으로 관리할 수 있게 해준다.
파이썬 코드
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/inherit")
def template_inherit(request: Request):
my_text = "FastAPI와 Jinja2를 이용한 예시"
return templates.TemplateResponse("index.html", {"request": request, "text": my_text})
HTML 코드 (base.html)
<!DOCTYPE html>
<html>
<head>
<title>FastAPI와 Jinja2 상속 예제</title>
</head>
<body>
<header>
<h1>내 웹사이트에 오신 것을 환영합니다!</h1>
</header>
{% block content %}
{% endblock %}
</body>
</html>
base.html은 {% block content %}{% endblock %} 을 사용하여 상속받는 템플릿에서 내용을 추가할 수 있는 위치를 마련해 두었다.
HTML 코드 (index.html)
{% extends "base.html" %}
{% block content %}
<p>{{ text }}</p>
{% endblock %}
index.html은 {% extends "base.html" %}을 사용하여 base.html을 상속받고,
{% block content %} 부분을 오버라이딩하여 FastAPI로부터 전달받은 text 변수를 렌더링한다.
[테스트]
http://127.0.0.1:8000/inherit 에 접속하면
기본 템플릿에서 나온 "내 웹사이트에 오신 것을 환영합니다!" 와
자식 템플릿에서 나온 "FastAPI와 Jinja2를 이용한 예시" 문구를 확인할 수 있다.
◆ Jinja2 상속과 block의 주요 문법
- {% extneds '파일명.html' %}: 다른 HTML 파일의 구조를 상속받는다
- {% block 블록명 %}...{% endblock %}: 상속받을 HTML 파일에서 재정의할 수 있는 영역을 지정한다.
[base.html에서 헤더와 바디, 푸터로 나눠 각각 다른 block으로 지정]
HTML 코드(base.html)
<!DOCTYPE html>
<html>
<head>
<title>여러 개의 Block을 가진 Base</title>
</head>
<body>
<header>
{% block header %}
<h1>기본 헤더</h1>
{% endblock %}
</header>
<main>
{% block content %}
<p>기본 바디</p>
{% endblock %}
</main>
<fotter>
{% block footer %}
<p>기본 푸터</p>
{% endblock %}
</footer>
</body>
</html>
이 base.html을 상속받아 index.html에서 각 block 재정의
{% extends "base.html" %}
{% block content %}
<p>사용자 정의 본문</p>
{% endblock %}
파이썬 코드
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/multi_block")
def multi_block(request: Request):
my_text = "FastAPI와 Jinja2를 이용한 예시"
return templates.TemplateResponse("index.html", {"request": request})
[테스트]
http://127.0.0.1:8000/multi_block 에 접속하면
base.html 에서 정의한 content block만 index.html에서 재정의한 내용으로 바뀌어 나타나고, 나머지 block은 base.html에 정의한 내용이 보인다.
여러 개의 block을 활용하면 기본 HTML 구조를 매 페이지마다 작성하지 않고, 기본 HTML 구조에 특정 부분만을 선택적으로 오버라이드하여 일관된 페이지를 만들 수 있다. (공통적인 부분을 한곳에서 관리하면 유지보수가 훨씬 쉬워진다)
◆ Jinja2 의 include 태그 주요 문법
- {% include '파일명.html' %}: 지정된 HTML 파일을 현재 파일에 포함시킨다.
헤더 부분을 별도의 파일로 만든다.
<h1>공통 헤더</h1>
이제 이 헤더를 index.html에 포함시킨다.
<!DOCTYPE html>
<html>
<head>
<title>Include Example</title>
</head>
<body>
{% include 'header.html' %}
<p>이것은 바디</p>
</body>
</html>
파이썬 코드
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/include_example")
def include_example(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
[테스트}
http://127.0.0.1:8000/include_example 에 접속하면
header.html 내용이 index.html에 잘 포함되어 나타난다.
같은 헤더를 여러 페이지에서 사용해야 할 때 include 태그를 활용하면 헤더의 내용을 한 번만 변경하면 모든 페이지에 반영되기 때문에 유지보수가 쉽고, 개발 생산성을 높일 수 있다.
◆ 임포트: {% import 'macros.html' as macros %}
다른 파일에 정의된 매크로를 현재 파일에서 사용할 수 있도록 가져온다.
먼저, 매크로를 정의한다.
HTML 코드 (macros.html)
{% macro input(type, name, value='', size=20) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}" size="{{ size }}">
{% endmacro %}
[매크로 매개변수 종류]
- 위치 매개변수(positional parameters): 호출 시 순서대로 값을 전달해야 한다. (상단 코드 중 type, name 의 예)
- 기본 매개변수(default parameters): 생략 가능하며, 생략 시 설정된 기본값이 사용된다. (상단 코드 중 value='', size=20)
▶ type과 name은 매크로에서 필수적으로 필요한 위치 매개변수이다. 이것들은 호출할 때 순서대로 지정해 주어야 한다.
value와 size는 기본값을 가지고 있기 때문에 생략 가능하며, 만약 값을 명시적으로 지정하고 싶다면 그렇게 할 수 있다.
<!-- vlaue와 size를 명시적으로 지정 -->
{{ macros.input('text', 'username', 'default_username', 40) }}
<!-- vlaue와 size는 기본값을 사용 -->
{{ macros.input('text', 'username') }}
▶ 기본값이 있는 매개변수는 선택적으로 사용할 수 있어 매크로의 사용성을 높이고 코드의 유연성을 증가시킨다.
HTML 코드(index.html)
<!DOCTYPE html>
<html>
<head>
<title>Import Example</title>
</head>
<body>
{% import 'macros.html' as macros %}
<form action="/submit" method="post">
{{ macros.input('text', 'username') }}
{{ macros.input('password', 'password') }}
<input type="submit" value="Submit">
</form>
</body>
</html>
파이썬 코드
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/import_example")
def import_example(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
[테스트]
http://127.0.0.1:8000/import_example 에 접속하면
macros.html에 정의된 매크로가 index.html에 잘 적용되어 나타난다.
import 태그를 사용하면 여러 페이지에 걸쳐 동일한 HTML 구조를 쉽게 재사용할 수 있다.
매크로를 한 번 정의해두면, 다른 HTML 페이지에서 쉽게 가져와 사용할 수 있기 때문에 코드의 중복을 줄이고 유지보수를 쉽게 할 수 있다.
◆ 세트: {% set variable=value %}
새로운 변수를 설정하거나 기존 변수의 값을 변경한다.
[변수를 사용하는 기본적인 사용법]
HTML 코드(index.html)
<!DOCTYPE html>
<html>
<head>
<title>Set Example</title>
</head>
<body>
{% set username = 'John' %}
<p>Hello, {{ username}}!</p>
</body>
</html>
파이썬 코드
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/set_example")
def set_example(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
[테스트]
http://127.0.0.1:8000/set_example 에 접속하면
index.html 에서 설정한 username 변수가 잘 적용되어 "Hello, John!" 이라는 문구가 나타난다.
set 태그를 사용하면 코드 중간에서 변수를 설정하거나 변경할 수 있다. 이렇게 하면 동적인 내용을 쉽게 표현할 수 있으며, 코드의 가독성과 유지보수성도 향상된다.
◆ do문: {% do navigation.append('a string') %}
do문: 템플릿 엔진 내에서 '부작용(side_effects)'을 일으키기 위한 구문.
* 부작용: 화면에는 출력되지 않지만, 내부적으로 데이터를 변경하거나 조작하는 작업.
do문을 사용하려면 jinja2.ext.do 확장을 Environment 에서 활성화해야 하는데, Environment를 생성할 때 extension 매개변수에 ' jinja2.ext.do'를 추가하면 된다.
파이썬 코드
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from jinja2 import Environment, select_autoescape, FileSystemLoader
app = FastAPI()
env = Environment(
loader=FileSystemLoader('templates'),
autoescape=select_autoescape(['html']),
extensions=['jinja2.ext.do']
)
templates = Jinja2Templates(directory="templates")
templates.env = env
@app.get("/do_example")
def do_example(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
[코드 설명]
1. from jinja2 import Environment, select_autoescape, FileSystemLoader: Jinja2 라이브러리에서 필요한 클래스와 함수를 가져옴
- Environment: 템플릿 환경을 설정하는 클래스
- select_autoescape: 자동 이스케이프 기능을 설정하는 함수
- FileSystemLoader: 파일 시스템에서 템플릿을 로드하는 클래스
2. env = Environment(...): Jinja2 환경 생성
- loader=FileSystemLoader('templates'): 템플릿 파일을 로드할 디렉토리 지정 (여기서는 templates 디렉터리)
- autoescape=select_autoescape(['html']): 자동 이스케이프 기능 설정. HTML 템플릿에 대해 자동으로 특수 문자를 이스케이프 처리
- extensions=['jinja2.ext.do']: Jinja2 확장 활성화. (여기서는 do 문법을 사용하기 위해 'jinja2.ext.do' 확장 추가)
3. templates = Jinja2Templates(directory="templates"): Jinja2Templates 인스턴스를 생성하고, 템플릿 파일이 위치한 디렉터리 지정
4. templates.env = env: 생성한 Jinja2 환경(env)을 templates 인스턴스의 env 속성에 할당. templates 인스턴스가 생성한 Jinja2 환경을 사용하게 된다.
[do문법 사용 예시]
예시1. 리스트에 값 추가하기
{% set my_list = [] %}
{% do my_list.append('apple') %}
{% do my_list.append('banana') %}
my_list 라는 이름의 빈 리스트를 선언하고, 그 후에 do문을 사용하여 리스트에 'apple'과 'banana' 추가
예시2. 딕셔너리 값 변경하기
{% set my_dit = {'key': 'old_value' %}
{% do my_dict.update({'key': 'new_value'}) %}
my_dict 라는 딕셔너리에 'key'라는 키와 'old_value'라는 값을 설정한 후, do문을 사용하여 'key'의 값을 'new_value'로 변경
예시3. 변숫값 증가시키기
{% set counter = 0 %}
{% do counter = counter + 1 %}
counter 라는 변수에 0을 할당하고, do문을 사용하여 counter 값을 1 증가시킴
HTML 코드
<!DOCTYPE html>
<html>
<head>
<title>Do Example</title>
</head>
<body>
{% set navigation = [] %}
{% do navigation.append('Home') %}
{% do navigation.append('About') %}
{% do navigation.append('Contact') %}
<ul>
{% for item in navigation %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
</html>
[테스트]
http://127.0.0.1:8000/do_example 에 접속하면
navigation 리스트에 추가된 값들이 화면에 잘 나타난다.
do문은 변수를 변경하거나 리스트에 값을 추가하는 등의 작업을 할 때 유용하다.
코드가 복잡해질 경우, 이런 작업을 템플릿 안에서 처리하는 것보다는 파이썬 코드에서 처리하는 것을 일반적으로 권장한다.
◆ with문: {% with %}...{% endwith %}
with문: 새 변수를 임시로 설정할 수 있는 범위를 지정하는 구문.
이 범위 내에서만 새로운 변수가 유효하며, 범위 밖에서는 변수 사용 불가.
HTML 코드
<!DOCTYPE html>
<html>
<head>
<title>With Example</title>
</head>
<body>
{% set global_username = 'Jane' %}
{% set global_fruits = ['mango', 'orange', 'grape'] %}
<p>Global username: {{ global_username }}</p>
<ul>
{% for fruit in global_fruits %}
<li>global_fruits: {{ fruit }}</li>
{% endfor %}
</ul>
{% with username = 'John', fruits = ['apple', 'banana', 'cherry'] %}
<p>Hello, {{ username }}!</p>
<ul>
{% for fruit in fruits %}
<li>{{ fruit }}</li>
{% endfor %}
</ul>
{% endwith %}
<p>Global username after with block: {{ global_username }}</p>
<ul>
{% for fruit in global_fruits %}
<li>Global fruit after with block: {{ fruit }}</li>
{% endfor %}
</ul>
</body>
</html>
- set문으로 설정한 global_username 과 global_fruits 는 with문의 범위와 상관없이 전체 템플릿에서 사용할 수 있다
- with문에서는 username과 fruit 변수를 설정하고, 그 범위 내에서만 사용한다. endwith 이후에는 이 변수들은 더 이상 유효하지 않다.
파이썬 코드
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/with_example")
def with_example(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
[테스트]
http://127.0.0.1:8000/with_example 에 접속하면
set으로 설정한 전역 변수와 with로 설정한 지역 변수가 각자의 범위에서 어떻게 작동하는지 확인할 수 있다.with문을 사용하면 변수의 유효 범위를 명확하게 지정할 수 있어 코드의 가독성과 유지보수성이 높아진다.set은 전역 범위에서 변수를 설정하므로 어느 위치에서든 사용할 수 있다.
다음 내용
'[파이썬 Projects] > <파이썬 웹개발>' 카테고리의 다른 글
[파이썬] FastAPI - 템플릿 (0) | 2024.08.21 |
---|---|
[파이썬] FastAPI - 예외 처리(exception handling) (0) | 2024.08.20 |
[파이썬] FastAPI - 요청 (0) | 2024.08.20 |
[파이썬] FastAPI - 응답 클래스 (0) | 2024.08.19 |
[파이썬] FastAPI - 응답 모델 (0) | 2024.08.18 |