FastAPI Versioning

Dev Smile·2024년 12월 31일
0

FastAPI

목록 보기
7/10
post-thumbnail

1. API Versioning의 필요성 및 방식

RESTful API에서 버저닝이 필요한 이유는 주로 호환성 유지유연성을 확보하기 위해서입니다. API는 시간이 지남에 따라 업데이트가 필요하며, 이를 사용자에게 적절히 제공하면서 기존 사용자가 중단 없이 API를 사용할 수 있도록 하기 위해 버저닝이 필수적입니다. 주요 이유를 다음과 같이 정리할 수 있습니다:

1-1. 호환성 유지

  • 기존 API 사용자(클라이언트)는 새로운 버전의 API가 출시되어도 영향을 받지 않아야 합니다.
  • 새로운 기능이나 수정사항이 추가되더라도 기존 API를 사용하는 시스템은 그대로 작동할 수 있도록 보장합니다.

1-2. 점진적인 업데이트

  • API를 사용하는 다양한 클라이언트가 동시에 새 버전으로 업데이트될 수 없는 경우가 많습니다.
  • 버저닝을 통해 클라이언트가 새로운 버전으로 전환할 시간을 제공합니다.

1-3. 변경사항 관리

  • API의 구조, 요청/응답 포맷, 인증 방식, 엔드포인트 등이 변경될 때, 버전을 통해 변경사항을 명확히 구분할 수 있습니다.
  • 예를 들어, 데이터 모델 변경, 새로운 필드 추가, 혹은 기존 필드 삭제 등의 작업을 수행할 수 있습니다.

1-4. 기능 테스트 및 롤백

  • 새로운 기능을 포함한 API를 테스트하거나 문제가 발생했을 때 롤백이 쉽습니다.
  • 서로 다른 버전의 API를 별도로 유지함으로써 안정성을 확보할 수 있습니다.

1-5. 다양한 클라이언트 요구 지원

  • 클라이언트가 요구하는 기능이나 데이터가 서로 다를 수 있습니다. 버전별로 이를 맞춤 지원할 수 있습니다.
  • 예를 들어, 모바일 클라이언트와 데스크톱 클라이언트의 요구사항이 다른 경우에 유용합니다.

1-6. API 생명주기 관리

  • 오래된 버전을 지원 중단(EOL)할 때, 명확하게 알릴 수 있습니다.
  • 버저닝을 통해 API의 수명과 사용자 전환 계획을 체계적으로 관리할 수 있습니다.

1-7. 버저닝 방식

  • URI 버저닝

    /api/v1/resource
    /api/v2/resource
    • 직관적이고 널리 사용됨.
    • URL에 명시적으로 포함되므로 변경 관리가 쉬움.
  • 쿼리 파라미터

    /api/resource?version=1
    /api/resource?version=2
    • RESTful하지 않다는 비판을 받을 수 있음.
  • 헤더 버저닝

    GET /api/resource
    Header: Accept: application/vnd.api+json; version=1
    • URI가 깨끗하지만 사용하기 복잡할 수 있음.

2. FastAPI에서의 Versioning 구현

FastAPI는 경로(Path) 기반의 버전 관리를 간단히 구현할 수 있습니다.

2-1. URI 버저닝

  • 경로를 활용한 Versioning
    • 경로에 버전 정보를 포함하는 방식은 가장 간단한 방법입니다.
    • 아래 코드는 /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"}
  • APIRouter를 사용한 모듈화
    • 규모가 큰 애플리케이션에서는 APIRouter를 사용해 버전별 엔드포인트를 모듈화하는 것이 효율적입니다.
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")

위와 같이 APIRouterprefix를 사용하면 코드 구조를 더 명확하고 관리하기 쉽게 만들 수 있습니다.


2-2. Query Parameter 기반 Versioning

버전 정보를 쿼리 매개변수로 전달받는 방식도 있습니다. 이는 직관적이지만 경로나 헤더 기반 방식만큼 일반적이진 않습니다.

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 형식으로 버전을 지정할 수 있습니다.


2-3. Header 기반 Versioning

버전을 경로 대신 헤더를 통해 전달받는 방식도 가능합니다. 이를 구현하려면 헤더 값을 확인하는 미들웨어를 추가하거나 엔드포인트에서 헤더를 직접 확인하면 됩니다.

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 헤더를 통해 원하는 버전을 지정할 수 있습니다.


0개의 댓글

관련 채용 정보