FastAPI에서 Jinja2Templates 완벽 가이드: 기본 개념부터 실전 활용까지

034179·2025년 2월 16일
post-thumbnail

1. Jinja2Templates란?

템플릿 엔진

템플릿 엔진은 미리 정의된 템플릿(HTML, XML 등)에 동적으로 데이터를 결합하여 최종적으로 사용자에게 제공될 HTML 등의 출력물을 생성하는 도구입니다. 이를 통해 정적인 HTML 파일을 유지하면서도 데이터베이스에서 가져온 내용을 삽입하여 동적인 웹 페이지를 쉽게 생성할 수 있습니다.

템플릿 엔진을 사용하면 프레젠테이션(뷰)과 비즈니스 로직을 분리할 수 있어 코드의 유지보수성과 재사용성이 향상됩니다. 또한 조건문, 반복문, 변수 치환, 필터, 매크로 등 다양한 기능을 제공하여 동적인 데이터를 효과적으로 관리하고 표현할 수 있습니다.

Jinja2

Jinja2는 Python 기반의 강력한 템플릿 엔진으로, HTML 등의 템플릿을 작성하고 렌더링하는 기능을 제공합니다. 그러나 Jinja2 자체는 특정 웹 프레임워크와 직접적인 연동 기능을 제공하지 않으므로, FastAPI와 함께 사용할 경우 별도의 설정이 필요합니다.

예를 들어, Environment, Loader, autoescape 등 모든 설정을 직접 정의해야 합니다. 개발자는 템플릿이 저장된 디렉토리를 지정하고, 자동 이스케이프 여부를 설정하며, 필요한 경우 사용자 정의 필터를 추가하는 등의 작업을 수행해야 합니다.

Jinja2Templates

Jinja2Templates는 FastAPI에서 Jinja2를 더욱 쉽게 활용할 수 있도록 설계된 헬퍼(wrapper) 클래스입니다. Jinja2의 기능을 내부적으로 활용하면서, FastAPI와의 통합을 간편하게 만들어주는 역할을 합니다.

즉, Jinja2Templates는 FastAPI 환경에서 Jinja2를 보다 효율적으로 사용할 수 있도록 기능을 확장한 도구라고 할 수 있습니다.

내부적으로는 Jinja2의 Environment 객체를 생성하고 사용하기 때문에, Jinja2의 모든 기능을 그대로 활용할 수 있습니다. 실제로 templates.env를 통해 Jinja2의 Environment에 접근할 수 있으며, 이를 이용해 커스터마이징이 가능합니다.

다음과 같은 기능을 수행할 수 있습니다:

  • 서버 사이드 렌더링(SSR): HTML 템플릿과 데이터를 결합하여 동적인 웹 페이지를 생성합니다.
  • FastAPI 엔드포인트에서 HTML 반환: TemplateResponse를 활용하여 FastAPI에서 직접 HTML 응답을 제공합니다.
  • 컨텍스트 프로세서 지원: 공통적인 데이터를 모든 템플릿에서 쉽게 접근할 수 있도록 설정할 수 있습니다.
  • 사용자 정의 필터 및 글로벌 변수 추가: Jinja2의 기본 기능을 확장하여, 커스텀 필터 및 글로벌 변수를 쉽게 추가할 수 있습니다.
  • 자동 URL 생성 지원: FastAPI의 url_for을 템플릿에서 바로 사용할 수 있어 동적 URL 생성을 간편하게 처리할 수 있습니다.

Jinja2Templates를 활용하면 FastAPI에서 템플릿 기반 웹 애플리케이션을 보다 효율적으로 개발할 수 있으며, 설정의 간소화 및 유연한 커스터마이징이 가능합니다.

2. Jinja2Templates 기본 설정 및 사용법

FastAPI에서 Jinja2Templates 설정하기

  1. 템플릿 폴더 지정: HTML 파일 등이 위치한 폴더(예: templates/)를 준비합니다.

  2. Jinja2Templates 인스턴스 생성:

    from fastapi.templating import Jinja2Templates
    templates = Jinja2Templates(directory="templates")
  3. 템플릿 렌더링: FastAPI 엔드포인트에서 TemplateResponse를 사용해 템플릿을 렌더링합니다.

    from fastapi import FastAPI, Request
    from fastapi.responses import HTMLResponse
    
    app = FastAPI()
    
    @app.get("/", response_class=HTMLResponse)
    async def read_root(request: Request):
        return templates.TemplateResponse("templates/index.html", {"request": request, "message": "Hello, World!"})
  4. 템플릿 내부 작성: Jinja2 문법을 활용해 동적 데이터 렌더링이 가능하며, 변수 삽입, 제어문 등이 지원됩니다.

이와 같이 기본 설정과 사용법을 통해 간단하게 템플릿 렌더링이 가능합니다.

주의할 점

FastAPI에서는 템플릿 렌더링 시 반드시 {"request": request, ...}와 같이 request 객체를 포함시켜야 합니다.
이를 통해 URL 생성이나 기타 컨텍스트 기능을 올바르게 사용할 수 있습니다.

3. Jinja2Templates 실전 활용 Tip

3.1 매크로 (macro)

템플릿 구조 설계 시, 공통 레이아웃이나 헤더, 푸터 등 반복되는 요소는 Base Template으로 만들고 개별 페이지에서는 이를 상속하여 재사용성을 높이는 방식이 일반적입니다.

이와 유사하게, 반복되는 템플릿 코드가 있다면 Jinja2의 매크로(Macro) 기능을 활용하여 보다 효율적으로 사용할 수 있습니다.

매크로 정의하기

예를 들어, 블로그 게시글에서 카드 스타일(Post Card)을 반복적으로 렌더링할 경우, 다음과 같이 매크로를 정의할 수 있습니다:

{% macro render_post(title, author, date, content) %}
  <div class="post-card">
    <h2>{{ title }}</h2>
    <p><strong>{{ author }}</strong> - {{ date }}</p>
    <p>{{ content }}</p>
  </div>
{% endmacro %}

템플릿 내에서 매크로를 호출하면 개별 블로그 게시글을 효율적으로 렌더링할 수 있습니다:

{{ render_post('FastAPI 시작하기', '홍길동', '2024-02-16', 'FastAPI는 빠르고 강력한 웹 프레임워크입니다.') }}

매크로를 외부 파일에서 가져오기

매크로는 별도의 템플릿 파일에서 정의하고 이를 import하여 사용할 수도 있습니다. 이를 통해 공통으로 사용되는 템플릿 코드를 한 곳에 모아두고 여러 템플릿에서 재사용할 수 있습니다.

macros.html 파일

{% macro render_button(label, url) %}
  <a href="{{ url }}" class="btn">{{ label }}</a>
{% endmacro %}

템플릿에서 import 후 사용

{% import 'macros.html' as macros %}
{{ macros.render_button('자세히 보기', '/details') }}

이렇게 하면 DRY(Don't Repeat Yourself) 원칙을 지키면서 템플릿 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.

3.2 컨텍스트 프로세서 활용법

컨텍스트 프로세서를 활용하면 템플릿에서 모든 페이지에서 공통적으로 사용할 변수를 자동으로 제공할 수 있습니다. 이를 통해 템플릿마다 동일한 변수를 수동으로 전달할 필요 없이, 전역적으로 데이터를 공유할 수 있습니다.

컨텍스트 프로세서 추가하기

def global_context(request: Request):
    return {"app_name": "My FastAPI App", "user_ip": request.client.host}

templates = Jinja2Templates(directory="templates", context_processors=[global_context])

템플릿에서 사용 예시

<h1>Welcome to {{ app_name }}!</h1>
<p>Your IP: {{ user_ip }}</p>

컨텍스트 프로세서를 활용하면 전역적으로 사용할 데이터를 한 번만 정의하면 되므로 유지보수가 쉬워지고, 코드의 중복이 줄어듭니다.

3.3 커스텀 필터와 글로벌 함수 추가

Jinja2에서는 사용자 정의 필터(Custom Filters)를 등록하여 템플릿 내 데이터를 변환하거나 가공할 수 있습니다. 기본적으로 제공되는 필터(예: upper, lower, safe 등) 외에도 커스텀 필터를 직접 정의하여 특정 데이터 변환 로직을 쉽게 적용할 수 있습니다.

커스텀 필터 추가하기

from datetime import datetime

def format_date(value, format="%Y-%m-%d"):
    return value.strftime(format)

templates.env.filters["format_date"] = format_date

템플릿에서 사용

<p>오늘 날짜: {{ today | format_date("%B %d, %Y") }}</p>

커스텀 필터를 활용하면 템플릿 내에서 Python 코드 없이도 데이터를 변환하여 출력할 수 있어, 유지보수성과 가독성이 향상됩니다.

3.4 템플릿 캐싱 및 성능 최적화

Jinja2는 템플릿을 컴파일하여 캐싱하는 기능을 제공하여 렌더링 속도를 최적화할 수 있습니다. 이를 통해 자주 사용되는 템플릿을 캐싱하여 불필요한 컴파일 비용을 줄일 수 있습니다.

캐싱 설정

templates.env.cache_size = 50  # 최대 50개의 템플릿을 캐싱

불필요한 공백 제거

templates = Jinja2Templates(directory="templates", trim_blocks=True, lstrip_blocks=True)

공백 제거 옵션을 활성화하면 템플릿의 불필요한 공백을 줄여 렌더링 속도를 개선할 수 있습니다.

3.5 비동기 렌더링 처리 방법

Jinja2는 기본적으로 동기(Synchronous) 엔진이며, await을 지원하지 않습니다. 하지만 FastAPI는 비동기(Async) 웹 프레임워크이므로, 비동기 엔드포인트에서 Jinja2를 활용하려면 몇 가지 고려해야 할 사항이 있습니다.

비동기 템플릿 렌더링 예제

@app.get("/async", response_class=HTMLResponse)
async def async_page(request: Request):
    return templates.TemplateResponse("async_page.html", {"request": request})

주의할 점

  • Jinja2는 기본적으로 동기 엔진이므로 await을 사용할 수 없음
  • 비동기 데이터를 렌더링할 때는 FastAPI 엔드포인트에서 데이터를 먼저 가져온 후, 이를 템플릿에 전달해야 함

비동기 작업이 필요한 경우, 템플릿 렌더링 자체는 동기적으로 처리하고, 데이터 처리 부분을 비동기로 실행하는 방식이 일반적입니다.

4. Jinja2Templates 활용 정리

Jinja2Templates는 FastAPI에서 서버 사이드 렌더링(SSR)을 수행할 수 있도록 도와주는 강력한 도구입니다. 이를 활용하면 동적 데이터를 포함한 HTML을 효율적으로 생성할 수 있으며, 템플릿 구조를 잘 설계하면 코드의 유지보수성과 재사용성을 극대화할 수 있습니다.

이 글에서는 Jinja2Templates의 개념과 기본 설정, 그리고 실전 활용법을 다루었습니다. 특히, 매크로(Macro)를 활용한 코드 재사용, 컨텍스트 프로세서를 통한 전역 변수 관리, 커스텀 필터 적용, 템플릿 캐싱 및 성능 최적화, 비동기 FastAPI 엔드포인트에서의 활용 등 핵심적인 주제를 살펴보았습니다.

Jinja2Templates를 효과적으로 활용하면, FastAPI 기반의 웹 애플리케이션에서 빠르고 안전한 서버 사이드 렌더링을 구현할 수 있습니다. 또한, 템플릿 엔진의 다양한 기능을 활용하여 코드를 더욱 모듈화하고 가독성을 높이며, 개발 생산성을 향상시킬 수 있습니다.

참고 문서

0개의 댓글