
FastAPI์์ Middleware๋ ์์ฒญ(Request)๊ณผ ์๋ต(Response)์ ํ๋ฆ์ ํต์ ํ๋ ์ค๊ณ ์ง์ ์ด๋ค.
์ธ์ฆ, ๋ก๊น
, ์ฑ๋ฅ ์ธก์ , ๋ณด์, ์์ฒญ ์ฐจ๋จ์ฒ๋ผ
๋ชจ๋ ์์ฒญ์ ๊ณตํต์ผ๋ก ์ ์ฉ๋๋ ๊ท์น์ ์ ๋ถ ๋ฏธ๋ค์จ์ด ๊ณ์ธต์์ ์ฒ๋ฆฌ๋๋ค.
Middleware๋ ์์ฒญ(Request)๊ณผ ์๋ต(Response) ์ฌ์ด์ ์์นํ ์ฝ๋๋ค.
ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ์ ๋ณด๋ด๋ฉด
๐ ์์ฒญ ์ ํ ๋ฒ, ์๋ต ์ ํ ๋ฒ
๐ ํญ์ ๋ ๋ฒ ์คํ๋๋ค
Middleware๋ ๊ฑด๋ฌผ ์ ๊ตฌ์ ๋ณด์ ๊ฒ์๋์ ๊ฐ๋ค.
๋๊ฐ ๋ค์ด์๋์ง,๋ค์ด์๋ ๋๋์ง, ์ผ๋ง๋ ์ค๋ ๋จธ๋ฌผ๋ ๋์ง๋ฅผ
๋น์ฆ๋์ค ๋ก์ง์ ๋ค์ด๊ฐ๊ธฐ ์ ์ ํ๋จํ๋ค.
์์ฒญ / ์๋ต ํ๋ฆ ์์ฝ
Client โ Middleware (์์ฒญ ์ ์ฒ๋ฆฌ) โ call_next(request) โ Path Operation (๋น์ฆ๋์ค ๋ก์ง) โ Middleware (์๋ต ํ ์ฒ๋ฆฌ) โ Client
Middleware๋ ์์ฒญ์ ๊ฐ๋ก์ฑ๊ณ , ๋ค์ ๋๋๋ ค ๋ฐ๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ค.
๋ชจ๋ Middleware๋ ์๋์ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ค.
@app.middleware("http")
async def middleware(request: Request, call_next):
# ์์ฒญ ์ ์ฒ๋ฆฌ (Request ๋จ๊ณ)
response = await call_next(request)
# ์๋ต ํ ์ฒ๋ฆฌ (Response ๋จ๊ณ)
return response
โ Middleware๋ ๋จ์ํ ํฌํผ๊ฐ ์๋๋ค
โ์ด ์์ฒญ์ ์๋ฒ ์์ผ๋ก ๋ค์ผ์ง ๋ง์งโ๋ฅผ ๊ฒฐ์ ํ๋ ๊ณ์ธต์ด๋ค
Middleware๋ ์คํ(Stack) ๊ตฌ์กฐ๋ก ์์ธ๋ค.
๋์ค์ ๋ฑ๋กํ Middleware๊ฐ ๊ฐ์ฅ ๋ฐ๊นฅ์ชฝ์ ์์นํ๋ค.
์์ฒญ ํ๋ฆ Client โ Middleware C (์ธ์ฆ) โ Middleware B (์๊ฐ ์ธก์ ) โ Middleware A (๋ก๊น ) โ Route
์๋ต ํ๋ฆ Route โ Middleware A โ Middleware B โ Middleware C โ Client
๐ ์ ์ง๋ฌธ์ โ์โ๋ผ๋ฉด
Middleware๋ก ๋ถ๋ฆฌํ๋ ๊ฒ์ด ๋ง๋ค
์ด์ Middleware์ ๊ฐ๋
๊ณผ ํ๋ฆ์ ์ดํดํ์ผ๋,
์ค์ ๋ก ์ฌ๋ฌ ๊ฐ์ ๋ฏธ๋ค์จ์ด๊ฐ ์ด๋ป๊ฒ ์์ด๊ณ ์คํ๋๋์ง๋ฅผ ํ์ธํด๋ณด์.
์๋ ์์ ์์๋ ์ญํ ์ด ์๋ก ๋ค๋ฅธ ๋ฏธ๋ค์จ์ด 3๊ฐ๋ฅผ ๋ฑ๋กํ๋ค.
๋ค์ ํ ๋ฒ ๊ฐ์กฐ
๋์ค์ ๋ฑ๋กํ ๋ฏธ๋ค์จ์ด๊ฐ ๊ฐ์ฅ ๋ฐ๊นฅ์ชฝ์ ์์นํ๋ค.
import time
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
# 1๏ธโฃ ๊ฐ์ฅ ์์ชฝ (๊ฐ์ฅ ๋ง์ง๋ง์ ์คํ)
@app.middleware("http")
async def middleware_a(request: Request, call_next):
print("[A] ์์ฒญ ๋ค์ด์ด - ๋ก๊น
์์")
print(f"[A] ์์ฒญ ์ ๋ณด: {request.method} {request.url.path}")
response = await call_next(request)
print("[A] ์๋ต ๋๊ฐ - ๋ก๊น
์๋ฃ")
return response
# 2๏ธโฃ ์ค๊ฐ
@app.middleware("http")
async def middleware_b(request: Request, call_next):
print("[B] ์์ฒญ ๋ค์ด์ด - ์ฒ๋ฆฌ ์๊ฐ ์ธก์ ์์")
start_time = time.perf_counter()
response = await call_next(request)
process_time = time.perf_counter() - start_time
response.headers["X-Process-Time"] = f"{process_time:.4f}"
print(f"[B] ์๋ต ๋๊ฐ - ์ฒ๋ฆฌ ์๊ฐ: {process_time:.4f}์ด")
return response
# 3๏ธโฃ ๊ฐ์ฅ ๋ฐ๊นฅ์ชฝ (๊ฐ์ฅ ๋จผ์ ์คํ)
@app.middleware("http")
async def middleware_c(request: Request, call_next):
print("[C] ์์ฒญ ๋ค์ด์ด - ์ธ์ฆ ๊ฒ์ฌ ์์")
# ๋ฌธ์ ๊ฒฝ๋ก๋ ์ธ์ฆ ์ ์ธ
if request.url.path in ["/docs", "/openapi.json", "/redoc"]:
print("[C] ๋ฌธ์ ๊ฒฝ๋ก๋ ์ธ์ฆ ๊ฑด๋๋")
response = await call_next(request)
print("[C] ์๋ต ๋๊ฐ")
return response
auth_token = request.headers.get("X-Auth-Token")
if request.url.path == "/secret" and not auth_token:
print("[C] ์ธ์ฆ ์คํจ! ์์ฒญ ๊ฑฐ๋ถ")
return JSONResponse(
status_code=403,
content={"detail": "X-Auth-Token ํค๋๊ฐ ํ์ํด์"}
)
print("[C] ์ธ์ฆ ํต๊ณผ")
response = await call_next(request)
print("[C] ์๋ต ๋๊ฐ")
return response
@app.get("/")
def root():
print("--- [Route] / ์คํ ---")
return {"message": "Hello, jeff!"}
@app.get("/secret")
async def secret():
print("--- [Route] /secret ์คํ ---")
return {"message": "๋น๋ฐ ๋ฐ์ดํฐ์์"}
์๋ฒ๋ฅผ ์คํํ๊ณ http://localhost:8000์ ์ ์ํ๋ฉด
ํฐ๋ฏธ๋์ ์๋์ ๊ฐ์ ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋๋ค.
[C] ์์ฒญ ๋ค์ด์ด - ์ธ์ฆ ๊ฒ์ฌ ์์ [C] ์ธ์ฆ ํต๊ณผ [B] ์์ฒญ ๋ค์ด์ด - ์ฒ๋ฆฌ ์๊ฐ ์ธก์ ์์ [A] ์์ฒญ ๋ค์ด์ด - ๋ก๊น ์์ [A] ์์ฒญ ์ ๋ณด: GET / --- [Route] / ์คํ --- [A] ์๋ต ๋๊ฐ - ๋ก๊น ์๋ฃ [B] ์๋ต ๋๊ฐ - ์ฒ๋ฆฌ ์๊ฐ: 0.0028์ด [C] ์๋ต ๋๊ฐ
๐ ์์ฒญ ์: C โ B โ A โ Route
๐ ์๋ต ์: Route โ A โ B โ C
์์ฒญ ํ๋ฆ Client โ Middleware C (์ธ์ฆ) โ Middleware B (์๊ฐ ์ธก์ ) โ Middleware A (๋ก๊น ) โ Route ์๋ต ํ๋ฆ Route โ Middleware A โ Middleware B โ Middleware C โ Client
ํน์ ํค๋๊ฐ ์์ผ๋ฉด ์์ฒญ์ ์ฐจ๋จํ๋ ์์
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.middleware("http")
async def check_auth_header(request: Request, call_next):
# ๋ฌธ์ ๊ด๋ จ ๊ฒฝ๋ก๋ ์ธ์ฆ ์ ์ธ
if request.url.path in ["/docs", "/openapi.json", "/redoc"]:
return await call_next(request)
auth_token = request.headers.get("X-Auth-Token")
if not auth_token:
return JSONResponse(
status_code=403,
content={"detail": "X-Auth-Token ํค๋๊ฐ ํ์ํด์"}
)
return await call_next(request)
call_next๋ฅผ ํธ์ถํ์ง ์๋๋ค