Post

FastAPI에서 app.state 활용하기



FastAPI에서 app.state 활용하기

FastAPI로 서비스를 구축하다 보면 애플리케이션 기동 시 한 번 불러온 결과를 전역 변수처럼 재사용하고 싶을 때가 많습니다. 이때 일반적인 전역 변수보다 안전하고, FastAPI의 의존성 주입과도 잘 어울리는 방법이 바로 app.state입니다. 이 포스트에서는 app.state의 개념과 구현 패턴, 그리고 예시를 정리합니다.

app.state란 무엇인가

FastAPIStarlette 의 서브클래스로 구현되어 있습니다. [출처] 따라서 Starlette가 제공하는 app.state(임의의 속성을 자유롭게 저장, 공유할 수 있는 네임스페이스)를 그대로 이용할 수 있습니다.

  • 저장 위치: FastAPI 인스턴스 자체
  • 생애 주기: 프로세스가 살아 있는 동안 유지
  • 주요 용도: 설정 값, 싱글톤 서비스 객체, 캐시 등

스타트업 이벤트에서 값 로드하기

app.state에 값을 넣는 가장 일반적인 시점은 애플리케이션 기동 직전입니다. FastAPI는 startup 이벤트를 제공하며, 이 이벤트에서 비동기/동기 코드 모두 실행할 수 있습니다. startup 이벤트는 간편하게 데코레이터를 이용하여 생성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
# main.py
from fastapi import FastAPI
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

app = FastAPI()

@app.on_event("startup")
async def load_configs():
    async with AsyncSession(engine) as session:
        rows = await session.execute(select(settings.c.config_value))
        app.state.config_values = rows.scalars().all()

기동 시점에만 한 번 호출되므로 DB 부하를 최소화하면서 전역 설정을 안전하게 확보할 수 있습니다.

구현 패턴

패턴 1: 엔드포인트에서 직접 주입

가장 직관적인 방식은 엔드포인트 내부에서 request.app.state 값을 꺼내 로직 클래스에 전달하는 것입니다.

1
2
3
4
5
6
7
8
9
10
# routers/hello.py
from fastapi import APIRouter, Request
from services.greeter import Greeter

router = APIRouter()

@router.get("/hello")
async def hello(name: str, request: Request):
    greeter = Greeter(request.app.state.config_values)
    return {"msg": greeter.greet(name)}
  • 장점: 구현이 단순
  • 단점: 테스트 시 request 객체를 직접 만들어야 하므로 약간 번거로움

패턴 2: Depends로 서비스 인스턴스 주입

의존성 주입을 활용하면 라우터 코드가 더 깔끔해집니다.

1
2
3
4
5
6
# deps.py
from fastapi import Request, Depends
from services.greeter import Greeter

def get_greeter(request: Request) -> Greeter:
    return Greeter(request.app.state.config_values)
1
2
3
4
5
6
7
8
9
10
# routers/hello.py
from fastapi import APIRouter, Depends
from deps import get_greeter
from services.greeter import Greeter

router = APIRouter()

@router.get("/hello")
async def hello(name: str, greeter: Greeter = Depends(get_greeter)):
    return {"msg": greeter.greet(name)}

테스트에서는 get_greeter를 손쉽게 모킹해 다른 설정을 주입할 수 있습니다.

패턴 3: 싱글톤 서비스 자체를 app.state에 저장

설정 값이 아니라 서비스 인스턴스를 통째로 저장해도 됩니다.

1
2
3
4
5
6
# main.py (계속)
from services.greeter import Greeter

@app.on_event("startup")
async def init_services():
    app.state.greeter = Greeter(app.state.config_values)
1
2
3
4
5
6
7
8
# routers/hello.py
from fastapi import APIRouter, Request

router = APIRouter()

@router.get("/hello")
async def hello(name: str, request: Request):
    return {"msg": request.app.state.greeter.greet(name)}
  • 장점: 애플리케이션 전역에서 동일 인스턴스 재사용
  • 단점: 싱글톤이므로 동시성에 주의(내부에서 mutable 상태를 변경하면 안 됨)

더 알아볼 내용

여러 스타트업 핸들러를 둘 때의 순서

@app.on_event("startup")는 여러 번 선언해도 문제없습니다. FastAPI는 핸들러가 등록된 순서대로 실행합니다. 모듈 임포트 순서가 곧 실행 순서이므로 의존 관계가 있다면 파일 구조와 import 순서를 신경 써야 합니다.

마무리

app.state는 전역 변수보다 안전하고, 의존성 주입과 유연하게 결합하는 편리한 공유 저장소입니다. 스타트업 이벤트와 함께 사용하면 데이터베이스나 파일 시스템에서 값을 한 번만 읽어 들인 뒤, 애플리케이션 전역에서 손쉽게 활용할 수 있습니다.



This post is licensed under CC BY 4.0 by the author.