RESTful API에서 버저닝이 필요한 이유는 주로 호환성 유지와 유연성을 확보하기 위해서입니다. API는 시간이 지남에 따라 업데이트가 필요하며, 이를 사용자에게 적절히 제공하면서 기존 사용자가 중단 없이 API를 사용할 수 있도록 하기 위해 버저닝이 필수적입니다. 주요 이유를 다음과 같이 정리할 수 있습니다:
URI 버저닝
/api/v1/resource
/api/v2/resource
쿼리 파라미터
/api/resource?version=1
/api/resource?version=2
헤더 버저닝
GET /api/resource
Header: Accept: application/vnd.api+json; version=1
FastAPI는 경로(Path) 기반의 버전 관리를 간단히 구현할 수 있습니다.
/v1/items
와 /v2/items
경로에서 각각 다른 버전의 API를 제공합니다.from fastapi import FastAPI
app = FastAPI()
@app.get("/v1/items")
def read_items_v1():
return {"version": "v1", "items": ["item1", "item2"]}
@app.get("/v2/items")
def read_items_v2():
return {"version": "v2", "items": ["item1", "item2"], "additional_info": "v2 specific data"}
from fastapi import APIRouter, FastAPI
app = FastAPI()
v1_router = APIRouter()
@v1_router.get("/items")
def read_items():
return {"version": "v1", "items": ["item1", "item2"]}
v2_router = APIRouter()
@v2_router.get("/items")
def read_items():
return {"version": "v2", "items": ["item1", "item2"], "additional_info": "v2 specific data"}
app.include_router(v1_router, prefix="/v1")
app.include_router(v2_router, prefix="/v2")
위와 같이 APIRouter
와 prefix
를 사용하면 코드 구조를 더 명확하고 관리하기 쉽게 만들 수 있습니다.
버전 정보를 쿼리 매개변수로 전달받는 방식도 있습니다. 이는 직관적이지만 경로나 헤더 기반 방식만큼 일반적이진 않습니다.
from fastapi import FastAPI, HTTPException, Query
app = FastAPI()
@app.get("/items")
def read_items(version: str = Query(...)):
if version == "1.0":
return {"version": "v1", "items": ["item1", "item2"]}
elif version == "2.0":
return {"version": "v2", "items": ["item1", "item2"], "additional_info": "v2 specific data"}
else:
raise HTTPException(status_code=400, detail="Unsupported API version")
클라이언트는 요청 시 ?version=1.0
형식으로 버전을 지정할 수 있습니다.
버전을 경로 대신 헤더를 통해 전달받는 방식도 가능합니다. 이를 구현하려면 헤더 값을 확인하는 미들웨어를 추가하거나 엔드포인트에서 헤더를 직접 확인하면 됩니다.
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
@app.get("/items")
def read_items(x_api_version: str = Header(...)):
if x_api_version == "1.0":
return {"version": "v1", "items": ["item1", "item2"]}
elif x_api_version == "2.0":
return {"version": "v2", "items": ["item1", "item2"], "additional_info": "v2 specific data"}
else:
raise HTTPException(status_code=400, detail="Unsupported API version")
클라이언트는 x-api-version
헤더를 통해 원하는 버전을 지정할 수 있습니다.