[FastAPI] 미들웨어(Middleware) - 개념부터 커스텀 구현까지

2025. 12. 12. 16:48·ServerDev/FastAPI
 

 

1. 왜 미들웨어가 필요할까?

 

 

FastAPI로 서버를 개발하다 보면 모든 API 엔드포인트에 공통적으로 적용해야 하는 기능들이 생깁니다. 예를 들어볼까요?

  • "모든 API 요청에 대해 응답 속도를 측정하고 싶다."
  • "들어오는 모든 요청(Request)의 헤더를 검사해서 보안을 강화하고 싶다."
  • "서버에서 나가는 모든 응답(Response)에 특정 헤더를 추가하고 싶다."

이런 기능을 구현하기 위해 수십, 수백 개의 API 함수마다 똑같은 코드를 반복해서 적는 것은 비효율적이며 유지보수를 지옥으로 만드는 지름길입니다. 이때 필요한 것이 바로 미들웨어(Middleware)입니다.

오늘은 FastAPI의 기능 중 하나인 미들웨어의 개념과 작동 원리, 그리고 제가 사용하는 다양한 구현 패턴을 기록해보려고 합니당.


2. 미들웨어의 작동 원리 (The Onion Architecture)

 

클라이언트가 보낸 요청(Request)은 실제 로직(Path Operation)에 도달하기 전에 여러 겹의 미들웨어 계층을 통과해야 합니다. 그리고 로직 처리가 끝난 후 응답(Response)이 나갈 때도 다시 이 계층을 거꾸로 통과해서 나갑니다.

흐름 정리

  1. 전처리 (Pre-processing): 요청이 들어오면 애플리케이션에 전달하기 전에 특정 코드를 실행
  2. 전달 (Passing): call_next를 통해 요청을 실제 앱(또는 다음 미들웨어)으로 넘김
  3. 후처리 (Post-processing): 앱이 작업을 마치고 응답을 반환하면, 그 응답을 가로채서 수정하거나 로그를 남긴 후 클라이언트에게 최종 반환

 

 

 

 


 

 

3. 기본 구현: 함수형 데코레이터 (@app.middleware)

가장 간단하고? 직관적인 방법은 제가 생각했을 때는  @app.middleware("http") 데코레이터를 사용하는 것입니다.

여기서 가장 많이 쓰이는 예제인 '처리 시간(Process Time) 측정' 코드를 뜯어보겠습니다.

import time
from fastapi import FastAPI, Request

app = FastAPI()

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    # [1] 요청 전처리 구간
    start_time = time.time()

    # [2] 실제 애플리케이션 로직(다음 단계)으로 요청 전달
    # await call_next(request)는 라우터 함수가 실행되고 응답이 올 때까지 기다립니다.
    response = await call_next(request)

    # [3] 응답 후처리 구간
    process_time = time.time() - start_time

    # 응답 헤더에 처리 시간 정보를 추가 (단위: 초)
    response.headers["X-Process-Time"] = str(process_time)

    return response

@app.get("/")
async def read_root():
    # 시뮬레이션을 위해 잠시 대기
    time.sleep(1) 
    return {"message": "Hello World"}

코드 분석 

  • request: 클라이언트로부터 들어온 요청 객체입니다. 헤더, 바디, 쿼리 파라미터 등을 조회할 수 있음!
  • call_next: 요청을 다음 단계(실제 경로 함수)로 넘겨주는 함수입, 이 함수는 await와 함께 호출되며, 결과값으로 response를 받는다.
  • response: 경로 함수가 리턴한 응답 객체. 여기서 헤더를 수정하거나 쿠키를 심을 수 있음.


4. 내장 미들웨어 활용 (CORS, TrustedHost)

FastAPI(그리고 기반이 되는 Starlette)는 보안과 편의를 위해 이미 만들어진 미들웨어를 제공하는데, 이걸 활용해도 좋습니다.

 

(1) CORSMiddleware (교차 출처 리소스 공유)

프론트엔드(예: React, Vue)와 백엔드 서버의 도메인/포트가 다를 때 발생하는 브라우저 보안 이슈를 해결함.

 

from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# 허용할 출처 목록
origins = [
    "http://localhost.front-end:3000",
    "https://my-service.com",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,       # 허용할 도메인
    allow_credentials=True,      # 쿠키 포함 여부
    allow_methods=["*"],         # 허용할 HTTP 메서드 (GET, POST 등)
    allow_headers=["*"],         # 허용할 HTTP 헤더
)

 

(2) TrustedHostMiddleware

HTTP Host 헤더 공격을 방지하기 위해, 허용된 도메인으로 들어온 요청만 처리

 

from fastapi.middleware.trustedhost import TrustedHostMiddleware

app = FastAPI()

app.add_middleware(
    TrustedHostMiddleware, 
    allowed_hosts=["example.com", "*.example.com"] # 와일드카드 사용 가능
)

5. 클래스 기반 미들웨어 (BaseHTTPMiddleware)

미들웨어 로직이 복잡해지거나 상태를 관리해야 한다면 함수형보다는 클래스형으로 작성하는 것이 관리하기 좋았던 거 같습니다.  starlette.middleware.base.BaseHTTPMiddleware를 상속받아 구현합니다.

from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import FastAPI, Request

class MyCustomMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, some_config: str):
        super().__init__(app)
        self.some_config = some_config # 초기 설정값 저장 가능

    async def dispatch(self, request: Request, call_next):
        # [전처리]
        print(f"Config Value: {self.some_config}")
        print(f"Request Path: {request.url.path}")

        # [다음 단계로 전달]
        response = await call_next(request)

        # [후처리]
        response.headers["X-Custom-Header"] = "FastAPI-Is-Great"
        return response

app = FastAPI()

# 미들웨어 등록 시 인자 전달 가능
app.add_middleware(MyCustomMiddleware, some_config="dev_mode")

 

이렇게 클래스로 분리하면 코드를 모듈화하여 별도의 파일(middlewares.py)로 관리하기가 훨씬 수월하더라구요,


6. 주의할 점 

미들웨어는 좋지만, 주의할 점이 있습니다.

  1. 성능 이슈: 미들웨어는 모든 요청마다 실행됨.... 여기서 무거운 연산(복잡한 DB 조회, 외부 API 호출 등)을 수행하면 서버 전체의 성능이 급격히 느려지기때문에 분리해야됩니다 ㅜㅜ
  2. 예외 처리: 미들웨어 내부에서 에러가 발생하면 전체 애플리케이션이 멈출 수 있습니다. try-except 블록을 적절히 사용하여 에러 예외처리는 필수
  3. 순서의 중요성: add_middleware를 여러 개 사용할 경우, 나중에 추가한 미들웨어가 먼저 실행됩니다 (스택 구조). 의존성이 있는 미들웨어라면 등록 순서에 주의해야 함..(Spring같은 경우 order 이라 생각하고 있긴함) 
    다만, Spring은 숫자로 우선순위를 명시하는 경우가 많지만, FastAPI는 코드가 작성된 순서(엄밀히 말하면 add_middleware가 실행된 순서)에 따라 감싸지는 구조입니다...

 

앞서 소개한 3가지 방식(데코레이터, 내장, 클래스)은 각각의 용도가 다릅니다. 실무에서는 다음과 같은 기준으로 선택했던 거 같습니다.

3번 방식 (@app.middleware)을 써야 할 때

"빠르고 간단하게 특정 기능을 추가하고 싶을 때"

  • 프로젝트 규모가 작거나, main.py 파일 하나에서 빠르게 개발할 때.
  • 복잡한 상태 관리나 설정값 없이, 단순히 요청 전후에 로그를 찍거나 헤더를 추가하는 정도의 가벼운 로직일 때.
  • 프로토타입을 만들며 아이디어를 빠르게 검증하고 싶을 때.

4번 방식 (내장 미들웨어)을 써야 할 때

"웹 표준, 보안, 성능과 관련된 검증된 기능이 필요할 때"

  • CORS, GZip 압축, HTTPS 리다이렉트 등 웹 개발에서 공통적으로 요구되는 표준 기능을 구현할 때.
  • 직접 구현하는 것보다 FastAPI/Starlette 팀이 최적화해 놓은 코드를 쓰는 것이 보안상 훨씬 안전하다 생각해요

5번 방식 (클래스 기반)을 써야 할 때

"복잡한 로직을 모듈화하거나, 재사용성을 높여야 할 때"

  • 미들웨어 실행 시 초기 설정값(__init__)을 받아야 할 때 (예: app.add_middleware(MyMiddleware, config="dev")).
  • 로직이 너무 길어져서 별도의 파일(예: middlewares.py)로 분리하여 깔끔하게 관리하고 싶을 때.
  • 만든 미들웨어를 라이브러리처럼 패키징 하여 여러 프로젝트에서 재사용하고 싶을 때.

 


 

8. 마치며

 

FastAPI의 미들웨어는 Gateway) 역할을 수행합니다. 간단한 로깅은 @app.middleware로 빠르게 구현하고, 보안 설정은 내장 미들웨어에 맡기며, 비즈니스 로직이 섞인 복잡한 제어는 클래스 기반으로 설계하는 것이 가장 이상적인 패턴인 거 같습니다! 

 

'ServerDev > FastAPI' 카테고리의 다른 글

[FastAPI/AI] OCR, LLM(Ollama), RAG 구현 시 절대 BackgroundTasks만 믿으면 안 되는 이유  (0) 2025.12.13
[FastAPI] 미들웨어 - 작동 원리부터 ELK 연동을 위한 JSON 구조화 로깅까지  (0) 2025.12.13
[FastAPI] BackgroundTasks 심층 분석: 0.1초 응답의 비밀과 치명적 한계 (vs Celery, Custom Loop)  (0) 2025.12.12
[FastAPI - CORS 마스터 3부] 개발(Local) vs 운영(Prod) 환경 분리와 실전 트러블 슈팅  (0) 2025.12.12
[FastAPI - CORS 마스터 2부] 쿠키 인증(Credential) 이슈 해결과 보안을 위한 정교한 허용 전략  (0) 2025.12.11
'ServerDev/FastAPI' 카테고리의 다른 글
  • [FastAPI/AI] OCR, LLM(Ollama), RAG 구현 시 절대 BackgroundTasks만 믿으면 안 되는 이유
  • [FastAPI] 미들웨어 - 작동 원리부터 ELK 연동을 위한 JSON 구조화 로깅까지
  • [FastAPI] BackgroundTasks 심층 분석: 0.1초 응답의 비밀과 치명적 한계 (vs Celery, Custom Loop)
  • [FastAPI - CORS 마스터 3부] 개발(Local) vs 운영(Prod) 환경 분리와 실전 트러블 슈팅
yeseul-kim01
yeseul-kim01
  • yeseul-kim01
    슬 개발일지
    yeseul-kim01
  • 전체
    오늘
    어제
    • 분류 전체보기 (79)
      • 자격증 (1)
        • 정보보안기사 (0)
      • DevOps (17)
        • Docker (6)
        • Kubernetes (1)
        • GitHub Actions (0)
        • AWS (4)
        • Monitoring (1)
        • Nginx (1)
        • GCP (3)
      • ServerDev (34)
        • SpringBoot (13)
        • DJango (5)
        • FastAPI (14)
        • Next (0)
        • Flask (0)
        • Database (2)
      • Algorithm (2)
        • BFS (0)
        • DFS (1)
        • 다익스트라 (0)
      • CS (8)
      • Data Engineering (1)
      • AI&MLOps (2)
      • Architecture (6)
      • Software Engineering (0)
        • Library Packaging (0)
      • Project (5)
        • docx-generator (0)
        • speak-note (2)
        • ms-serving (1)
        • keyshield (2)
      • ProgrammingLanguages (3)
        • Python (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    NLP부트캠프
    아키텍처
    depends
    KeyShield
    Kubernetes
    STT
    docker
    실시간시스템
    multipartfile
    프로젝트기록-KeyShield
    실무일기-인프라편
    멀티모듈
    프로젝트기록-speaknote
    rag
    하이브리드아키텍처
    SpeakNote
    grpc
    FastAPI
    KServe
    동시성제어
    SpringBoot
    아키텍처설계
    MLops
    트러블슈팅
    비동기처리
    di
    Django
    백엔드
    실무일기-백엔드편
    FastAPI - CORS 마스터
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
yeseul-kim01
[FastAPI] 미들웨어(Middleware) - 개념부터 커스텀 구현까지
상단으로

티스토리툴바