TOP
class="layout-aside-left paging-number">
본문 바로가기
[파이썬 Projects]/<파이썬 웹개발>

[파이썬] FastAPI - FastAPI와 Jinja2 고급 문법

by 기록자_Recordian 2024. 8. 22.
728x90
반응형
시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.

이전 내용
 

[파이썬] FastAPI - 템플릿

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.이전 내용 [파이썬] FastAPI - 예외 처리(exception handling)

puppy-foot-it.tistory.com


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은 전역 범위에서 변수를 설정하므로 어느 위치에서든 사용할 수 있다.


다음 내용

 

 

728x90
반응형