~/csslab.dev/notes $ ls

// lab

FastAPI lifespan — 앱 수명과 리소스 정리를 한 자리에

  • 백엔드를 FastAPI로 다루면서 정리한 앱 시작·종료 시점 처리 방식
  • DB 커넥션 풀이나 모델 로딩처럼 한 번 만들고 끝까지 재사용해야 하는 자원을 어디에 두는가의 문제
  • 정리 목적: lifespan이 컨텍스트 매니저와 같은 구조라는 걸 짚어두기

lifespan — yield 기준으로 앞뒤가 나뉜다

@asynccontextmanager
async def lifespan(app: FastAPI):
    pool = await create_pool()      # 앱 시작 시 1회
    app.state.pool = pool
    yield                           # ── 이 지점에서 앱이 요청을 받는다
    await pool.close()              # 앱 종료 직전 1회

app = FastAPI(lifespan=lifespan)
  • yield 이전 — 앱이 뜨기 전에 실행. 자원 확보
  • yield 이후 — 종료 직전에 실행. 자원 정리
  • 요청마다가 아니라 프로세스 수명당 한 번 실행됨

컨텍스트 매니저와 같은 모양

lifespan이 특별한 문법처럼 보이지만 구조는 컨텍스트 매니저 그대로.

# 클래스형 — 진입/이탈을 메서드로
class Resource:
    def __enter__(self): ...        # 블록 진입 시 초기화
    def __exit__(self, *exc): ...   # 블록 이탈 시 정리 (예외가 나도 실행)

with Resource() as r:
    use(r)

# 데코레이터형 — yield 하나로 진입/이탈을 표현
@contextmanager
def resource():
    r = acquire()
    yield r
    release(r)
  • 공통점: 정리 코드가 실행되는 것을 언어가 보장 — 중간에 예외가 나도 이탈 경로를 탄다
  • 수동으로 try/finally를 흩어 놓는 것보다 누락 위험이 낮음

비동기 버전

  • 비동기 환경에서는 __aenter__ / __aexit__ 를 구현하거나 @asynccontextmanager 사용
  • FastAPI lifespan이 비동기 컨텍스트 매니저를 그대로 받는 이유가 여기 있음

정리

  • 자원의 수명 범위를 코드 구조로 표현 — 언제 만들고 언제 정리하는지가 한 함수 안에 보임
  • 앱 수명이면 lifespan, 블록 수명이면 with. 같은 발상의 다른 축척