FastAPI 서버개발, Jinja, 템플릿 상속

1. FastAPI 개요

FastAPI는 Python으로 작성된 현대적인 웹 프레임워크로, 빠르고 간편하게 API 서버를 구축할 수 있도록 설계되었습니다.
비동기 처리를 지원하고, 자동적인 문서화 (Swagger, ReDoc)을 제공하는 것이 큰 장점입니다.
FastAPI를 사용하면 개발자가 신속하게 RESTful API를 개발할 수 있습니다.
이번 포스트에서는 FastAPI의 기본적인 설정 방법과, Jinja2를 이용한 HTML 템플릿 랜더링, 그리고 템플릿 상속에 대해 알아보겠습니다.

2. FastAPI 설치 및 기본 설정

FastAPI를 사용하기 전에, 먼저 필요한 패키지를 설치해야 합니다. FastAPI는 Starlette 프레임워크 위에서 동작하기 때문에 Starlette도 함께 설치됩니다.
아래의 명령어를 사용하여 FastAPI와 uvicorn (ASGI 서버)를 설치합니다.

pip install fastapi uvicorn

2.1 기본 FastAPI 앱 작성

FastAPI 애플리케이션의 기본 코드는 다음과 같이 작성할 수 있습니다.
FastAPI 인스턴스를 생성하고, 간단한 GET 요청을 처리하는 API 엔드포인트를 만들어보겠습니다.

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"Hello": "World"}

위 코드를 main.py라는 파일에 저장한 후 다음 명령어로 서버를 실행할 수 있습니다.

uvicorn main:app --reload

“http://127.0.0.1:8000″에 접속하면 {“Hello”: “World”}라는 JSON 응답을 받을 수 있습니다.

3. Jinja2 템플릿 엔진과의 통합

FastAPI는 Jinja2 템플릿 엔진과 쉽게 통합할 수 있습니다. Jinja2는 Python 코드 안에서 HTML을 생성할 수 있는 템플릿 엔진입니다.
먼저 다음 패키지를 설치하여 Jinja2를 사용할 수 있도록 합니다.

pip install jinja2

3.1 템플릿 디렉토리 설정 및 기본 HTML 파일 작성

템플릿 파일을 저장할 디렉토리를 생성합니다. 예를 들어, templates라는 디렉토리를 생성하겠습니다.
그 안에 index.html 파일을 작성합니다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FastAPI와 Jinja</title>
</head>
<body>
    <h1>환영합니다!</h1>
    <p>FastAPI와 Jinja2 템플릿 엔진을 사용하는 방법을 배우고 있습니다.</p>
</body>
</html>

3.2 FastAPI에서 Jinja2 템플릿 사용하기

FastAPI 애플리케이션에 Jinja2 템플릿을 통합하는 방법은 다음과 같습니다.
아래의 코드를 main.py에 추가합니다.

from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse

app = FastAPI()
templates = Jinja2Templates(directory="templates")

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

위와 같이 설정한 후 서버를 실행하면, 웹 브라우저에서 “http://127.0.0.1:8000″에 접속했을 때 index.html의 내용이 렌더링됩니다.

4. 템플릿 상속

Jinja2는 템플릿 상속 기능을 제공하여, 기본 템플릿을 정의하고 이를 상속해 서브 템플릿을 작성할 수 있습니다.
이는 코드의 재사용성을 높이고, 일관된 디자인을 유지하는 데 매우 유용합니다.

4.1 기본 템플릿 작성

먼저 base.html이라는 기본 템플릿 파일을 생성합니다. 이 파일은 페이지 레이아웃을 정의합니다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FastAPI 템플릿 상속</title>
</head>
<body>
    <header>
        <h1>사이트 헤더</h1>
    </header>
    <main>
        {% block content %}{% endblock %}
    </main>
    <footer>
        <p>사이트 푸터</p>
    </footer>
</body>
</html>

4.2 서브 템플릿 작성

이제 아래와 같이 index.html 템플릿을 수정하여 base.html을 상속받도록 합니다.

{% extends "base.html" %}

{% block content %}
<h2>환영합니다!</h2>
<p>템플릿 상속의 예제입니다.</p>
{% endblock %}

4.3 FastAPI에서 템플릿 렌더링

이제 다시 main.py의 해당 엔드포인트로 돌아가서, 서브 템플릿이 올바르게 렌더링되는지 확인해보겠습니다.

from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse

app = FastAPI()
templates = Jinja2Templates(directory="templates")

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

앞에서 작성한 서브 템플릿 index.htmlbase.html의 레이아웃을 상속받아 렌더링 됩니다.
이로써 코드의 재사용성이 보장되었습니다.

5. FastAPI의 Jinja2 활용 사례

FastAPI와 Jinja2의 조합은 동적인 웹 페이지를 만들 때 유용하게 사용됩니다.
예를 들어, 사용자 정보를 데이터베이스에서 가져와 템플릿에 렌더링하는 과정을 생각해볼 수 있습니다.
다음은 간단한 사용자 목록을 보여주는 예제입니다.

5.1 데이터베이스 모델 설정

여기서는 SQLAlchemy를 사용하여 데이터베이스 모델을 정의하겠습니다. 데이터베이스 연결 및 모델을 설정합니다.

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

DATABASE_URL = "sqlite:///./test.db"  # 간단한 SQLite 데이터베이스

engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    email = Column(String, index=True)

Base.metadata.create_all(bind=engine)

5.2 사용자 목록 반환 엔드포인트 작성

사용자 목록을 반환하는 엔드포인트를 FastAPI에 추가합니다.
아래와 같이 작성합니다.

from fastapi import Depends, FastAPI, Request
from sqlalchemy.orm import Session

app = FastAPI()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/users/", response_class=HTMLResponse)
async def read_users(request: Request, db: Session = Depends(get_db)):
    users = db.query(User).all()
    return templates.TemplateResponse("users.html", {"request": request, "users": users})

5.3 사용자 목록 템플릿 작성

사용자 목록을 보여주기 위한 users.html 템플릿을 작성합니다.

{% extends "base.html" %}

{% block content %}
<h2>사용자 목록</h2>
<ul>
    {% for user in users %}
        <li>{{ user.name }} ({{ user.email }})</li>
    {% endfor %}
</ul>
{% endblock %}

6. 결론

이번 포스트에서는 FastAPI로 백엔드 서버를 개발하고, Jinja2를 활용한 템플릿 렌더링 방법과 템플릿 상속에 대해 알아보았습니다.
FastAPI는 간단한 설정으로 API 서버를 구축할 수 있으며, Jinja2와 함께 사용하면 동적인 웹 페이지를 생성하는 데 강력한 도구가 됩니다.
이러한 조합을 통해 여러분의 웹 애플리케이션 개발에 많은 도움을 받을 수 있기를 바랍니다.

FastAPI 서버개발, APIRouter 클래스를 사용한 라우팅

오늘날의 웹 애플리케이션은 RESTful API를 통해 다양한 클라이언트 애플리케이션과 통신할 수 있는 기능이 필수적입니다. FastAPI는 Python으로 신속하게 웹 API를 구축할 수 있도록 설계된 최신 프레임워크입니다. 이번 글에서는 FastAPI의 APIRouter 클래스를 활용한 라우팅에 대해 심도 깊은 논의를 진행할 것입니다.

FastAPI란?

FastAPI는 Python의 비동기 프로그래밍을 지원하는 현대적인 웹 프레임워크로, 매우 빠른 속도를 자랑합니다. FastAPI는 또한 Pydantic과 Starlette를 기반으로 하여 데이터 검증과 처리에서 뛰어난 성능을 보여줍니다. FastAPI의 주요 특징은 다음과 같습니다:

  • 빠른 속도: Starlette를 기반으로 하여 비동기적으로 요청을 처리할 수 있습니다.
  • 자동 문서화: OpenAPI 및 JSON Schema를 통해 API 문서를 자동으로 생성합니다.
  • 데이터 검증: Pydantic을 사용하여 요청 데이터의 유효성을 검사합니다.
  • 비동기 작업 지원: async/await 문법을 지원하여 비동기 작업을 효율적으로 처리합니다.

APIRouter란?

APIRouter 클래스는 FastAPI에서 라우팅을 관리하는 데 유용한 도구입니다. 이를 통해 API 엔드포인트를 모듈화하여 코드의 재사용성과 가독성을 높일 수 있습니다. APIRouter는 특정 경로에 대한 엔드포인트를 그룹화하여 좀 더 깔끔한 구조의 코드를 작성할 수 있도록 도와줍니다.

APIRouter 사용하기

APIRouter를 사용하는 기본적인 방법은 다음과 같습니다:

from fastapi import FastAPI, APIRouter

app = FastAPI()
router = APIRouter()

@router.get("/items/")
async def read_items():
    return [{"item_id": 1}, {"item_id": 2}]

app.include_router(router)

위의 코드는 FastAPI 애플리케이션을 생성하고, APIRouter를 통해 /items/ 경로에 대한 GET 요청을 처리하는 간단한 예제입니다. router.get 데코레이터를 사용하여 HTTP GET 요청에 대한 핸들러 함수를 정의합니다.

APIRouter의 이점

  • 모듈화된 코드 구조: 라우터를 사용하여 관련된 엔드포인트를 그룹화함으로써 코드의 가독성과 관리 용이성이 향상됩니다.
  • 코드 재사용성: 라우터를 다른 파일이나 패키지에서 불러와서 사용할 수 있어, 규모가 큰 애플리케이션의 유지 보수가 용이합니다.
  • 명확한 API 문서화: 각 라우터에 대한 문서화를 개별적으로 진행할 수 있어, API 사용자가 이해하기 쉽습니다.

예제: APIRouter와 함께 복잡한 라우팅 사용하기

이번 절에서는 APIRouter를 사용하여 더 복잡한 API 라우팅 구조를 구현해보겠습니다. 다음 예제에서는 상품과 관련된 엔드포인트를 정의해 보겠습니다.

from fastapi import FastAPI, APIRouter
from typing import List

app = FastAPI()
router = APIRouter()

class Item:
    def __init__(self, item_id: int, name: str):
        self.item_id = item_id
        self.name = name

items_db = [
    Item(item_id=1, name="Item One"),
    Item(item_id=2, name="Item Two"),
]

@router.get("/items/", response_model=List[Item])
async def read_items():
    return items_db

@router.get("/items/{item_id}")
async def read_item(item_id: int):
    for item in items_db:
        if item.item_id == item_id:
            return item
    return {"message": "Item not found"}

app.include_router(router)

위의 코드는 /items/ 경로에서 모든 아이템의 목록을 반환하고, /items/{item_id} 경로에서 특정 아이템을 조회하는 엔드포인트를 제공합니다. FastAPI의 response_model 기능을 활용하여 반환되는 데이터 모델을 정의했습니다.

비동기 I/O로 성능 최적화하기

FastAPI의 장점 중 하나는 비동기 처리가 가능하다는 점입니다. 이를 통해 대규모 애플리케이션에서도 높은 성능을 유지할 수 있습니다. 데이터베이스와의 비동기 I/O 통신을 위해 다음과 같이 코드를 개선할 수 있습니다.

from fastapi import FastAPI, APIRouter
from pydantic import BaseModel
from typing import List
import asyncio

app = FastAPI()
router = APIRouter()

class Item(BaseModel):
    item_id: int
    name: str

items_db = [
    Item(item_id=1, name="Item One"),
    Item(item_id=2, name="Item Two"),
]

async def fake_db_fetch_all():
    await asyncio.sleep(1)  # Simulating a database query delay
    return items_db

@router.get("/items/", response_model=List[Item])
async def read_items():
    return await fake_db_fetch_all()

@router.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int):
    items = await fake_db_fetch_all()
    for item in items:
        if item.item_id == item_id:
            return item
    return {"message": "Item not found"}

app.include_router(router)

위의 예제는 비동기적으로 데이터를 처리하는 방법을 보여줍니다. fake_db_fetch_all 함수는 실제 데이터베이스를 사용하는 대신, `asyncio.sleep`을 통해 지연을 시뮬레이션하고 있습니다.

중간자 미들웨어를 통한 보안 및 인증 추가하기

애플리케이션의 보안성을 높이기 위해 인증 기능을 추가할 수 있습니다. FastAPI는 다양한 미들웨어를 지원하여 라우터의 요청을 가로챌 수 있습니다. 예를 들어 JWT(JSON Web Token)를 사용하여 인증을 처리할 수 있습니다.

from fastapi import FastAPI, APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel
from typing import List

app = FastAPI()
router = APIRouter()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
    username: str
    email: str

def fake_decode_token(token):
    return User(username=token + "user", email=token + "@example.com")

async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    if not user:
        raise HTTPException(status_code=401, detail="Invalid authentication credentials")
    return user

@router.get("/users/me", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

app.include_router(router)

위의 코드는 JWT 토큰을 사용하여 사용자를 인증하는 방법을 보여줍니다. Depends를 사용하여 엔드포인트에 의존성을 추가하고, OAuth2PasswordBearer를 통해 토큰을 처리합니다.

결론

FastAPI의 APIRouter 클래스를 사용하면 간결하고 구조화된 API를 구축할 수 있습니다. 모듈화된 라우팅 구조를 통해 재사용성을 높이고, 비동기 I/O를 활용하여 성능을 개선할 수 있습니다. 또한, 보안 미들웨어를 통해 중요한 정보와 자원을 보호할 수 있습니다. 이러한 기능들은 현대 웹 애플리케이션을 개발하는 데 필요한 핵심 요소입니다.

FastAPI는 개발자가 필요한 모든 기능을 갖춘 탄탄한 프레임워크입니다. 이 글을 통해 APIRouter 클래스를 효과적으로 사용하는 방법을 이해하고, 실무에 적용할 수 있기를 바랍니다.

FastAPI 서버개발, 이벤트 라우트 변경

FastAPI는 현대적인 웹 API를 쉽게 개발할 수 있는 강력한 파이썬 웹 프레임워크입니다. 비동기 지원 및 자동 문서 생성을 통한 고성능 API 개발이 가능하여 많은 개발자들에게 사랑받고 있습니다. 이 포스트에서는 FastAPI 서버를 개발하는 과정에서 ‘이벤트 라우트 변경’에 대해 깊이 있게 알아보도록 하겠습니다.

목차

  1. FastAPI 소개
  2. 이벤트 라우트 변경이란?
  3. FastAPI에서 이벤트 라우트 변경 사용하기
  4. 예제 코드
  5. 결론

FastAPI 소개

FastAPI는 Python 3.6 이상을 지원하며, 뛰어난 성능을 자랑하는 웹 프레임워크입니다. 다음과 같은 특징을 가지고 있습니다:

  • 비동기 프로그래밍 지원: async/await 구문을 사용하여 비동기 작업을 간편하게 수행 가능
  • 자동화된 문서화: OpenAPI 및 JSON Schema를 기반으로 자동으로 API 문서를 생성
  • 높은 성능: Starlette와 Pydantic을 기반으로 하여 빠른 API 요청 처리
  • 쉬운 개발 경험: 타입 힌트 및 유효성 검사를 통해 개발자가 쉽게 이해하고 수정 가능

이벤트 라우트 변경이란?

이벤트 라우트 변경은 클라이언트의 요청에 따라 API의 동작 방식을 동적으로 변경하는 것을 의미합니다. 이러한 기능을 통해 API는 다양한 상황에 적절히 대응할 수 있습니다. 예를 들어, 사용자 인터페이스에서 특정 버튼 클릭 시 서버에서 처리해야 하는 작업을 변경할 수 있습니다.

FastAPI에서 이벤트 라우트 변경 사용하기

FastAPI에서 이벤트 라우트 변경을 구현하기 위해서는 라우터와 종속성 주입 기능을 활용할 수 있습니다. FastAPI는 이벤트가 발생했을 때 특정 경로로 요청을 라우팅하는 데 필요한 유연한 구조를 제공합니다.

필수 라이브러리 설치

pip install fastapi uvicorn

FastAPI와 Uvicorn 설치 후, 서버를 시작하기 위한 기본 코드를 작성해 보겠습니다.

기본 FastAPI 서버 설정


from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    id: int
    name: str
    age: int

users_db = {}

@app.post("/users/")
async def create_user(user: User):
    users_db[user.id] = user
    return JSONResponse(status_code=201, content=jsonable_encoder(user))

@app.get("/users/{user_id}")
async def read_user(user_id: int):
    return users_db.get(user_id, {"error": "User not found"})

위의 코드는 FastAPI의 기본적인 사용자 생성 및 조회 API를 설정한 것입니다. 이제, 사용자 이벤트에 따라 특정 라우트를 변경하는 기능을 추가해 보겠습니다.

이벤트 라우트 변경 예제

아래 예제에서는 클라이언트가 전송한 데이터에 따라 다른 라우트로 요청을 처리하게 됩니다. 사용자의 나이에 따라 다른 구매 추천 API로 라우팅하는 방식입니다.


from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class PurchaseRequest(BaseModel):
    user_id: int
    age: int

@app.post("/purchase/")
async def purchase(request: PurchaseRequest):
    # 사용자의 나이에 따라 다른 추천 경로로 라우팅
    if request.age < 18:
        return {"message": "추천 상품: 어린이 장난감"}
    elif 18 <= request.age < 65:
        return {"message": "추천 상품: 성인 패션"}
    else:
        return {"message": "추천 상품: 노인 건강 보조제"}

위의 코드는 /purchase/ 경로를 통해 사용자 나이에 따라 적절한 상품 추천 메시지를 반환합니다.

예제 코드

전체 코드를 종합하여 아래와 같이 FastAPI 서버를 설정할 수 있습니다.


from fastapi import FastAPI, HTTPException
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    id: int
    name: str
    age: int

users_db = {}

@app.post("/users/")
async def create_user(user: User):
    users_db[user.id] = user
    return JSONResponse(status_code=201, content=jsonable_encoder(user))

@app.get("/users/{user_id}")
async def read_user(user_id: int):
    return users_db.get(user_id, {"error": "User not found"})

class PurchaseRequest(BaseModel):
    user_id: int
    age: int

@app.post("/purchase/")
async def purchase(request: PurchaseRequest):
    # 사용자의 나이에 따라 다른 추천 경로로 라우팅
    if request.age < 18:
        return {"message": "추천 상품: 어린이 장난감"}
    elif 18 <= request.age < 65:
        return {"message": "추천 상품: 성인 패션"}
    else:
        return {"message": "추천 상품: 노인 건강 보조제"}

이제 이 API를 실행하기 위해 아래의 명령어를 사용합니다.

uvicorn main:app --reload

이제 브라우저에서 http://127.0.0.1:8000/docs에 접속하시면 자동 생성된 API 문서를 확인할 수 있습니다. 사용자 생성 및 상품 추천 API를 테스트해 보세요!

결론

FastAPI를 활용한 이벤트 라우트 변경 기능에 대해 알아보았습니다. 이 기능은 사용자 입력을 기반으로 API의 동작을 유연하게 변경할 수 있어 매우 유용합니다. API의 요청을 처리하는 방식에 더 많은 논리를 추가하고 싶다면 이를 구현할 수 있는 다양한 방법이 있습니다. FastAPI의 강력한 기능을 활용하여 복잡한 이벤트 기반 로직을 손쉽게 관리할 수 있습니다.

여기까지 FastAPI와 이벤트 라우트 변경에 대한 이벤트를 살펴보았습니다. 이 레시피를 활용해 유연하고 강력한 API를 개발해 보시기 바랍니다.

FastAPI 서버개발, 성능 최적화를 위한 캐싱 및 로드 밸런싱 전략

작성자: 조광형

발행일: [오늘 날짜]

소개

FastAPI는 Python으로 작성된 고성능 웹 프레임워크로, 빠른 개발 속도와 높은 성능 덕분에 많은 개발자들에게 사랑받고 있습니다.
하지만 성능이 좋다고 해서 모든 웹 애플리케이션이 원활하게 작동하는 것은 아닙니다. 특히 트래픽이 많은 서비스에서는
성능 최적화가 필수적입니다. 이 글에서는 FastAPI 서버의 성능을 최적화하기 위한 캐싱(caching)
로드 밸런싱(load balancing) 전략에 대해 심도 깊은 설명과 예제를 통해 소개하겠습니다.

캐싱의 필요성

캐싱은 데이터에 대한 접근 시간을 줄이기 위해 자주 요청되는 데이터를 메모리에 저장하는 기술입니다.
FastAPI와 같은 웹 애플리케이션에서 데이터를 매번 데이터베이스에서 가져오는 것은 성능 저하를
초래할 수 있습니다. 데이터베이스 쿼리는 종종 I/O 작업이 포함되어 있어 시간이 많이 소요됩니다.
따라서 캐싱을 통해 데이터를 메모리에 저장하고, 요청 시 빠르게 제공할 수 있습니다.

FastAPI에서의 캐싱 구현

FastAPI에서는 다양한 캐싱 방법을 사용할 수 있습니다. 여기서는 Redis를 사용한 캐싱을
소개하겠습니다. Redis는 오픈 소스 메모리 데이터 구조 저장소로, 높은 성능과 다양한 자료 구조를 지원합니다.

Redis 설치 및 설정

먼저 Redis를 설치합니다. 대부분의 운영 체제에서는 패키지 관리자를 통해 Redis를 쉽게 설치할 수 있습니다.
예를 들어, Ubuntu에서는 다음 명령어를 사용하여 설치할 수 있습니다.

sudo apt install redis-server

설치가 완료되면 Redis 서버를 시작합니다.

sudo systemctl start redis.service

Redis가 정상적으로 실행되고 있는지 확인하려면 다음 명령어를 사용합니다.

redis-cli ping

정상적으로 실행되고 있다면 “PONG”이라는 응답을 받을 수 있습니다.

FastAPI에 Redis 연결하기

FastAPI에서 Redis를 사용하기 위해 aioredis라는 비동기 Redis 클라이언트를 사용합니다.
먼저 필요한 패키지를 설치합니다:

pip install aioredis fastapi uvicorn

예제 코드

이제 FastAPI 애플리케이션을 작성해 보겠습니다. 아래는 Redis를 사용하여 캐싱을 구현한 예제입니다.
이 예제에서는 간단한 GET 요청을 처리하여 서버에서 데이터를 캐시하는 구현을 보여줍니다.


from fastapi import FastAPI, Depends
from aioredis import Redis, from_url
from typing import Optional
import asyncio

app = FastAPI()

# Redis 클라이언트 초기화
redis_client: Optional[Redis] = None

async def init_redis():
    global redis_client
    redis_client = await from_url("redis://localhost")

@app.on_event("startup")
async def startup_event():
    await init_redis()

@app.on_event("shutdown")
async def shutdown_event():
    await redis_client.close()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    # 캐시에서 데이터 조회 시도
    cached_item = await redis_client.get(f"item:{item_id}")
    
    if cached_item:
        return {"item_id": item_id, "value": cached_item.decode("utf-8"), "source": "cache"}
    
    # 데이터베이스에서 데이터 조회 (가정)
    value = f"Value of item {item_id}"
    
    # 캐시에 데이터 저장
    await redis_client.set(f"item:{item_id}", value)
    
    return {"item_id": item_id, "value": value, "source": "database"}
            

이 코드는 Redis에 연결하고 특정 item_id에 대한 요청이 들어올 때 캐시에서 먼저 데이터를 조회합니다.
캐시에 데이터가 없으면 데이터베이스 (여기서는 가상의 데이터베이스)에서 데이터를 조회하고,
이를 캐시에 저장합니다.

로드 밸런싱의 필요성

로드 밸런싱은 여러 서버 간에 들어오는 네트워크 트래픽을 분산시키는 기술로, 애플리케이션의 가용성을 높이고
성능을 최적화합니다. FastAPI 서버는 단일 인스턴스에서 실행될 경우 높은 트래픽을 처리하는 데 한계가 있을 수 있습니다.
로드 밸런싱을 통해 여러 인스턴스에 요청을 분산시킬 수 있습니다.

로드 밸런싱을 활용하면 다음과 같은 이점이 있습니다:

  • 높은 가용성: 서버가 다운되더라도 다른 서버가 서비스를 계속 제공할 수 있습니다.
  • 성능 향상: 여러 서버가 동시에 요청을 처리하므로 응답 시간이 단축됩니다.
  • 유지보수 용이: 새로운 서버를 추가하거나 기존 서버를 제거할 때 서비스 중단이 없습니다.

FastAPI와 로드 밸런싱

FastAPI 서버를 여러 인스턴스로 실행하고 로드 밸런싱을 구현하기 위한 방법 중 하나는 Nginx를 사용하는 것입니다.
Nginx는 고성능의 웹 서버이자 리버스 프록시로, 로드 밸런싱을 지원합니다.

Nginx 설치 및 설정

우선 Nginx를 설치합니다. Ubuntu에서 설치하는 방법은 다음과 같습니다:

sudo apt install nginx

Nginx가 설치되었다면 설정 파일을 수정하여 FastAPI 서버 인스턴스에 대한 로드 밸런싱 규칙을 추가해야 합니다.

예제 Nginx 설정

아래는 Nginx의 로드 밸런싱 구성 예제입니다:


http {
    upstream fastapi_app {
        server 127.0.0.1:8000;
        server 127.0.0.1:8001;
        server 127.0.0.1:8002;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://fastapi_app;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}
            

이 설정에서는 3개의 FastAPI 인스턴스를 로드 밸런싱합니다. 각 인스턴스는 동일한 서버의 서로 다른 포트에서 실행됩니다.
Nginx는 들어오는 요청을 로드 밸런싱하여 각 인스턴스에 분산합니다.

FastAPI 서버 인스턴스 실행

여러 FastAPI 인스턴스를 실행하려면 각 인스턴스를 별도의 프로세스로 실행하면 됩니다. 아래와 같이
`uvicorn`을 이용해 각각의 포트에서 FastAPI 인스턴스를 실행합니다:


uvicorn main:app --host 127.0.0.1 --port 8000 &
uvicorn main:app --host 127.0.0.1 --port 8001 &
uvicorn main:app --host 127.0.0.1 --port 8002 &
            

이제 Nginx가 80번 포트에서 로드 밸런싱을 수행하고, FastAPI 인스턴스는 각각 8000, 8001, 8002 포트에서 실행되고 있습니다.

테스트 및 성능 최적화

캐싱 및 로드 밸런싱 구현 후, 실제 트래픽을 테스트하여 성능을 측정하는 것이 중요합니다.
로드 테스트 도구인 Apache Benchmark 또는 Locust를 사용하여 성능을 측정하고
최적화를 진행할 수 있습니다.

Apache Benchmark 사용 예제

Apache Benchmark는 간단한 명령어로 웹 서버의 성능을 테스트할 수 있는 도구입니다. 다음 명령어로 성능을 테스트할 수 있습니다:

ab -n 1000 -c 10 http://localhost/

이 명령어는 1000개의 요청을 동시에 10개씩 보내는 테스트를 수행합니다.
테스트 결과를 분석하면 캐싱 및 로드 밸런싱이 성능에 미치는 영향을 평가할 수 있습니다.

결론

FastAPI는 뛰어난 성능을 제공하지만, 실제 운영 환경에서는 캐싱과 로드 밸런싱과 같은 추가적인 성능 최적화 노력이 필요합니다.
Redis를 활용한 캐싱은 데이터베이스 부하를 줄이고 응답 속도를 높이는 데 큰 도움이 됩니다.
또한, Nginx를 통한 로드 밸런싱은 웹 애플리케이션의 안정성과 속도를 향상시켜 줍니다.
이 두 가지 기술을 활용하여 FastAPI 서버의 성능을 최적화할 수 있으며, 여러분의 서비스가 더 많은 트래픽을 원활히 처리할 수 있도록 도와줄 것입니다.

FastAPI 서버개발, 비동기 대응 DB 접속 함수

FastAPI는 Python의 빠르고 현대적인 웹 프레임워크로, 비동기 프로그래밍을 통해 높은 성능을 발휘합니다. 이 글에서는 FastAPI에서 비동기 데이터베이스 접속 함수를 구현하는 방법에 대해 상세히 설명하고, 예제 코드를 통해 몸소 경험할 수 있도록 하겠습니다. FastAPI의 비동기 기능은 대규모 웹 애플리케이션이나 높은 동시성이 요구되는 API 서버를 구축할 때 특히 강력한 성능을 제공합니다.

1. 비동기 프로그래밍이란?

비동기 프로그래밍은 여러 작업을 동시에 처리할 수 있게 해주는 프로그래밍 모델입니다. 기존의 동기 프로그래밍에서는 작업 하나가 끝날 때까지 다음 작업을 기다려야 했지만, 비동기 프로그래밍에서는 하나의 작업이 진행되는 동안 다른 작업을 동시에 처리할 수 있습니다. 이는 특히 I/O 바운드 작업에서 큰 이점을 제공합니다.

1.1 비동기 프로그래밍의 장점

  • 성능 증가: 여러 I/O 작업을 동시에 처리하여 대기 시간을 줄일 수 있습니다.
  • 리소스 효율성: CPU 사용률을 최적화하고, 낮은 자원으로 많은 클라이언트를 처리할 수 있습니다.
  • 응답성 향상: 서버가 특정 작업을 기다리는 시간이 줄어들어 사용자가 더 빠르게 응답을 받을 수 있습니다.

2. FastAPI 소개

FastAPI는 Python 3.6+을 위한 현대적인 웹 프레임워크로, 빠르고, 직관적이며, 자동 완성을 제공합니다. FastAPI의 가장 큰 장점 중 하나는 ASGI(Asynchronous Server Gateway Interface)를 기반으로 하여 비동기 처리를 지원한다는 것입니다. 이는 웹 소켓이나 데이터베이스와 같은 비동기 I/O를 처리하는 데 매우 유리합니다.

FastAPI는 또한 Pydantic을 사용하여 데이터 유효성을 검사하고 JSON 직렬화를 제공하여 개발자가 보다 간편하게 API를 개발할 수 있도록 돕습니다.

3. 비동기 데이터베이스 접근 방법

데이터베이스에 비동기적으로 접근하기 위해서는 비동기 지원을 위한 ORM(Object Relational Mapping) 라이브러리를 사용해야 합니다. 여기서는 SQLAlchemyDatabases 라이브러리를 조합하여 비동기 DB 접속 함수를 구현하는 예제를 살펴보겠습니다.

3.1 필요한 라이브러리 설치

FastAPI 애플리케이션을 시작하기 위해 필요한 패키지를 설치합니다. 다음과 같은 명령어를 터미널에 입력하여 설치합니다:

pip install fastapi uvicorn sqlalchemy databases asyncpg

위 명령어는 FastAPI 프레임워크, ASGI 서버(Uvicorn), SQLAlchemy ORM, Databases 비동기 라이브러리, PostgreSQL 데이터베이스 드라이버를 설치합니다.

3.2 데이터베이스 모델 설정

이제 비동기 작업을 수행할 데이터베이스 모델을 정의하겠습니다. SQLAlchemy를 사용하여 데이터베이스 모델을 작성합니다.

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True, index=True)
    username = Column(String, unique=True, index=True)
    email = Column(String, unique=True, index=True)

3.3 데이터베이스 연결 설정

비동기 데이터베이스 연결을 위해 Databases 라이브러리를 사용하여 데이터베이스 연결을 설정합니다.

DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"
database = Database(DATABASE_URL)
engine = create_engine(DATABASE_URL)
Base.metadata.create_all(bind=engine)

위 코드에서 DATABASE_URL은 사용자의 데이터베이스 정보를 입력해야 합니다. 데이터베이스 사용자가 user, 비밀번호가 password이며, 데이터베이스 이름이 dbname인 PostgreSQL 데이터베이스에 연결하는 URI입니다.

3.4 비동기 데이터베이스 함수 구현

이제 필요한 비동기 데이터베이스 작업 함수를 구현합니다.

from databases import Database

async def get_user(database: Database, user_id: int):
    query = "SELECT * FROM users WHERE id = :id"
    return await database.fetch_one(query, values={"id": user_id})

async def create_user(database: Database, username: str, email: str):
    query = "INSERT INTO users(username, email) VALUES (:username, :email)"
    await database.execute(query, values={"username": username, "email": email})

위 함수는 각각 사용자를 데이터베이스에서 조회하고, 새 사용자를 생성하는 비동기 함수입니다. fetch_one 메소드는 데이터베이스 쿼리에 대한 단일 결과를 비동기적으로 반환하고, execute 메소드는 INSERT 작업을 수행합니다.

3.5 FastAPI 앱과 비동기 데이터베이스 함수 연결

FastAPI 애플리케이션을 설정하고, 비동기 데이터베이스 함수를 해당 엔드포인트에 연결합니다.

from fastapi import FastAPI

app = FastAPI()

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()

@app.get("/users/{user_id}")
async def read_user(user_id: int):
    user = await get_user(database, user_id)
    return user

@app.post("/users/")
async def create_new_user(username: str, email: str):
    await create_user(database, username, email)
    return {"msg": "User created successfully"}

위 코드에서 애플리케이션의 시작 시 database.connect()를 호출하여 데이터베이스에 연결하고, 종료 시 database.disconnect()를 호출하여 연결을 종료합니다. 사용자를 조회하는 read_user 엔드포인트와 새 사용자를 생성하는 create_new_user 엔드포인트도 설정했습니다.

4. 테스트와 검증

이제 FastAPI 애플리케이션을 실행하고 비동기 API를 테스트할 준비가 되었습니다. 다음 명령어로 서버를 실행합니다:

uvicorn main:app --reload

웹 브라우저에서 http://localhost:8000/docs에 접속하면 FastAPI의 문서화된 API 인터페이스를 확인할 수 있습니다. 여기서 비동기적으로 정의한 API를 테스트할 수 있습니다.

4.1 사용자 생성 테스트

/users/ 엔드포인트로 POST 요청을 보내어 사용자를 생성할 수 있습니다. 예시 요청 본문은 다음과 같습니다:

{
    "username": "testuser",
    "email": "test@example.com"
}

4.2 사용자 조회 테스트

/users/{user_id} 엔드포인트로 GET 요청을 보내어 사용자를 조회할 수 있습니다. 예를 들어 /users/1 요청을 보내면 ID가 1인 사용자의 정보를 받을 수 있습니다.

5. 결론

FastAPI 모바일 애플리케이션을 위한 강력하고 효율적인 백엔드 서버를 구축하는 데 필요한 기본적인 비동기 데이터베이스 접속 함수를 성공적으로 구현했습니다. 이 예제를 통해 FastAPI의 비동기 기능을 활용하여 동시성을 높이고, 더 많은 클라이언트 요청을 처리할 수 있는 옵션을 갖추게 되었습니다. 이러한 구조는 특히 트래픽이 높은 애플리케이션에서 성능을 극대화할 수 있습니다.

추가적으로, 데이터베이스와의 비동기 작업을 위한 다양한 라이브러리와 기술도 존재하므로, 각각의 요구사항에 맞게 커스터마이징하여 사용하시기 바랍니다. 이 글이 여러분의 FastAPI 개발에 도움이 되기를 바랍니다.