반응형
소개
웹 애플리케이션을 개발할 때 미들웨어와 CORS 설정은 매우 중요한 부분입니다. 이번 글에서는 FastAPI에서 미들웨어를 활용하는 방법과 CORS 설정 방법을 실제 예제와 함께 상세히 알아보겠습니다.
미들웨어란?
미들웨어는 요청이 들어오고 응답이 나가는 중간에 실행되는 코드입니다. 주로 다음과 같은 용도로 사용됩니다:
- 요청/응답 로깅
- 인증/인가 처리
- 요청 처리 시간 측정
- CORS 처리
- 에러 핸들링
기본 미들웨어 구현
# main.py
from fastapi import FastAPI, Request
import time
import logging
# 로깅 설정
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
@app.middleware("http")
async def log_requests(request: Request, call_next):
"""
모든 HTTP 요청을 로깅하는 미들웨어
작동 방식:
1. 요청이 들어올 때 시간 기록
2. 다음 미들웨어/라우터 호출
3. 응답이 나갈 때 소요 시간 계산 및 로깅
"""
# 요청 시작 시간
start_time = time.time()
# 요청 정보 로깅
logger.info(f"Request started: {request.method} {request.url}")
# 실제 요청 처리
response = await call_next(request)
# 처리 시간 계산
process_time = time.time() - start_time
# 응답 헤더에 처리 시간 추가
response.headers["X-Process-Time"] = str(process_time)
# 응답 정보 로깅
logger.info(f"Request completed in {process_time:.3f}s")
return response
사용자 정의 미들웨어 예제
1. 요청 제한 미들웨어
# middleware/rate_limiter.py
from fastapi import HTTPException, Request
from collections import defaultdict
import time
class RateLimiter:
def __init__(self, requests_per_minute: int = 60):
self.requests_per_minute = requests_per_minute
self.requests = defaultdict(list) # IP별 요청 시간 저장
async def __call__(self, request: Request, call_next):
"""
IP 기반 요청 제한 미들웨어
사용 예시:
app.middleware("http")(RateLimiter(requests_per_minute=60))
"""
# 클라이언트 IP 주소
ip = request.client.host
# 현재 시간
current_time = time.time()
# 1분 이내의 요청만 유지
self.requests[ip] = [
req_time for req_time in self.requests[ip]
if current_time - req_time < 60
]
# 요청 수 확인
if len(self.requests[ip]) >= self.requests_per_minute:
raise HTTPException(
status_code=429,
detail="Too many requests"
)
# 현재 요청 추가
self.requests[ip].append(current_time)
return await call_next(request)
# 미들웨어 등록
app.middleware("http")(RateLimiter(requests_per_minute=60))
2. 에러 핸들링 미들웨어
# middleware/error_handler.py
from fastapi import Request
from fastapi.responses import JSONResponse
import logging
logger = logging.getLogger(__name__)
@app.middleware("http")
async def catch_exceptions_middleware(request: Request, call_next):
"""
모든 예외를 잡아서 일관된 에러 응답을 반환하는 미들웨어
에러 응답 형식:
{
"error": "에러 메시지",
"path": "요청 경로",
"method": "HTTP 메서드"
}
"""
try:
return await call_next(request)
except Exception as e:
# 에러 로깅
logger.error(f"Error processing request: {str(e)}")
# 에러 응답 반환
return JSONResponse(
status_code=500,
content={
"error": str(e),
"path": request.url.path,
"method": request.method
}
)
3. 인증 미들웨어
# middleware/auth.py
from fastapi import Request, HTTPException
from fastapi.security import HTTPBearer
from .security import decode_jwt
security = HTTPBearer()
@app.middleware("http")
async def authenticate_request(request: Request, call_next):
"""
JWT 토큰 검증 미들웨어
작동 방식:
1. Authorization 헤더에서 토큰 추출
2. 토큰 검증
3. 검증된 사용자 정보를 request.state에 저장
"""
if request.url.path in ["/login", "/docs", "/openapi.json"]:
# 인증이 필요 없는 경로
return await call_next(request)
try:
# Authorization 헤더 검증
authorization = request.headers.get("Authorization")
if not authorization:
raise HTTPException(status_code=401, detail="No authorization header")
scheme, token = authorization.split()
if scheme.lower() != "bearer":
raise HTTPException(
status_code=401,
detail="Invalid authentication scheme"
)
# 토큰 검증
payload = decode_jwt(token)
# 검증된 사용자 정보 저장
request.state.user = payload
except Exception as e:
raise HTTPException(status_code=401, detail=str(e))
return await call_next(request)
CORS 설정
CORS(Cross-Origin Resource Sharing)는 다른 도메인에서의 리소스 요청을 제어하는 메커니즘입니다.
기본 CORS 설정
# main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# CORS 미들웨어 추가
app.add_middleware(
CORSMiddleware,
# 허용할 출처 목록
allow_origins=[
"http://localhost:3000", # React 개발 서버
"https://yourdomain.com" # 프로덕션 도메인
],
# 자격 증명(쿠키 등) 포함 여부
allow_credentials=True,
# 허용할 HTTP 메서드
allow_methods=["*"], # 모든 메서드 허용
# 허용할 HTTP 헤더
allow_headers=["*"], # 모든 헤더 허용
)
환경별 CORS 설정
# config.py
from pydantic import BaseSettings
class Settings(BaseSettings):
# 개발/운영 환경 구분
ENVIRONMENT: str = "development"
# CORS 설정
CORS_ORIGINS: list = [
"http://localhost:3000",
"http://localhost:8080"
]
# 운영 환경일 경우 실제 도메인만 허용
@property
def cors_origins(self):
if self.ENVIRONMENT == "production":
return ["https://yourdomain.com"]
return self.CORS_ORIGINS
settings = Settings()
# main.py
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)
동적 CORS 설정
# middleware/dynamic_cors.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import re
app = FastAPI()
def get_allowed_origins():
"""
허용할 출처 목록을 동적으로 생성
예: 모든 subdomain 허용
"""
base_domains = [
r"^https://.*\.yourdomain\.com$", # 모든 subdomain
r"^http://localhost:[0-9]+$" # localhost의 모든 포트
]
return base_domains
class DynamicCORS(CORSMiddleware):
async def is_origin_allowed(self, origin: str) -> bool:
"""
출처가 허용되는지 동적으로 확인
"""
if not origin:
return False
# 허용된 패턴과 매칭
return any(
re.match(pattern, origin)
for pattern in get_allowed_origins()
)
app.add_middleware(
DynamicCORS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)
CORS 테스트
// 프론트엔드 테스트 코드
// CORS 설정 테스트
async function testCORS() {
try {
const response = await fetch('http://localhost:8000/api/test', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include' // 쿠키 포함
});
const data = await response.json();
console.log('Response:', data);
} catch (error) {
console.error('CORS Error:', error);
}
}
미들웨어 순서 관리
미들웨어는 등록된 순서대로 실행되므로, 순서가 중요합니다:
# main.py
# 1. CORS 미들웨어 (항상 최상위에 위치)
app.add_middleware(CORSMiddleware, ...)
# 2. 로깅 미들웨어
app.middleware("http")(log_requests)
# 3. 인증 미들웨어
app.middleware("http")(authenticate_request)
# 4. 요청 제한 미들웨어
app.middleware("http")(RateLimiter(requests_per_minute=60))
# 5. 에러 핸들링 미들웨어 (항상 최하위에 위치)
app.middleware("http")(catch_exceptions_middleware)
마치며
미들웨어와 CORS 설정은 웹 애플리케이션의 보안과 기능성을 높이는 중요한 요소입니다. FastAPI는 이러한 기능들을 쉽게 구현하고 관리할 수 있는 강력한 도구를 제공합니다. 실제 프로젝트에서는 보안 요구사항과 클라이언트의 필요에 맞게 적절히 설정하시기 바랍니다.
'파이썬 > Fast API' 카테고리의 다른 글
FastAPI 애플리케이션 Docker로 배포하기 (2) | 2024.11.27 |
---|---|
JWT를 이용한 FastAPI 사용자 인증 구현하기 (0) | 2024.11.24 |
FastAPI의 데이터베이스 연동 쉽게 이해하기 (2) | 2024.11.23 |
FastAPI 시작하기 - 설치부터 첫 API 만들기까지 (2) | 2024.11.21 |