Django REST Framework/DRF 일반

[DRF] 공식 문서 - Versioning 정리

bluebamus 2024. 1. 22. 17:57

 1. 버전 관리(Versioning)

   - API 버전 관리를 통해 다양한 클라이언트 간의 동작을 변경할 수 있다. REST 프레임워크는 여러 가지 버전 관리 체계를 제공한다.

   - 버전 관리는 들어오는 클라이언트 요청에 따라 결정되며, 요청 URL 또는 요청 헤더를 기반으로 할 수 있다.

   - 버전 관리에 접근하는 데는 여러 가지 유효한 방법이 있다. 특히 제어할 수 없는 여러 클라이언트를 가진 장기적인 시스템을 위한 엔지니어링을 하는 경우, 버전이 지정되지 않은 시스템도 적합할 수 있다.

 

   1) REST 프레임워크와 함께하는 버전 관리(Versioning with REST framework)

      - API 버전 관리가 활성화되면, request.version 속성은 들어오는 클라이언트 요청에서 요청된 버전에 해당하는 문자열을 포함한다.

      - 기본적으로 버전 관리는 활성화되지 않고, request.version은 항상 None을 반환한다.

 

      1. 버전에 따른 동작 변경(Varying behavior based on the version)

         - API 동작을 어떻게 변화시킬지는 전적으로 당신에게 달려있지만, 일반적으로 새로운 버전에서 다른 직렬화 스타일로 전환하려는 예를 들 수 있다.

def get_serializer_class(self):
    if self.request.version == 'v1':
        return AccountSerializerVersion1
    return AccountSerializer

 

      2. 버전화된 API에 대한 URL 반전(Reversing URLs for versioned APIs)

         - REST 프레임워크에 포함된 reverse 함수는 버전 관리 체계와 연계된다. 키워드 인자로 현재 요청을 반드시 포함해야 한다. 

from rest_framework.reverse import reverse

reverse('bookings-list', request=request)

 

         - 위의 함수는 요청 버전에 적절한 URL 변환을 적용한다. 

 

         - 예를 들면:
            - NamespaceVersioning이 사용되고 API 버전이 'v1'인 경우, 사용되는 URL 조회는 'v1:bookings-list'가 되며, 이는 http://example.org/v1/bookings/와 같은 URL로 해석될 수 있다.

            - QueryParameterVersioning이 사용되고 API 버전이 1.0인 경우, 반환된 URL은 http://example.org/bookings/?version=1.0와 같은 것일 수 있습니다.

 

      3. 버전화된 API와 하이퍼링크 직렬화기(Versioned APIs and hyperlinked serializers)

         - URL 기반 버전 관리 체계와 함께 하이퍼링크 serialization 스타일을 사용할 때는 serializer의 컨텍스트로 request를 전달시키는 것이 중요하다.

def get(self, request):
    queryset = Booking.objects.all()
    serializer = BookingsSerializer(queryset, many=True, context={'request': request})
    return Response({'all_bookings': serializer.data})

 

         - 이렇게 하면 반환된 URL에 적절한 버전 정보가 포함될 수 있습니다.

 

   2) 버전 관리 체계 설정(Configuring the versioning scheme)

      - 버전 관리 체계는 DEFAULT_VERSIONING_CLASS 설정 키에 의해 정의됩니다.

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'
}

 

         - 명시적으로 설정하지 않는 한, DEFAULT_VERSIONING_CLASS의 값은 None이 된다. 이 경우 request.version 속성은 항상 None을 반환한다.

         - 개별 뷰에서 버전 관리 체계를 설정할 수도 있다. 일반적으로 이것을 할 필요는 없다. 왜냐하면 전역적으로 사용되는 단일 버전 관리 체계를 가지는 것이 더 합리적이기 때문이다. 그렇게 해야 하는 경우, versioning_class 속성을 사용하면 된다.

class ProfileList(APIView):
    versioning_class = versioning.QueryParameterVersioning

 

      1. Other versioning settings

         - 다음 설정 키도 버전 관리를 제어하는 데 사용된다.
            - DEFAULT_VERSION. 버전 관리 정보가 없는 경우 request.version에 사용해야 하는 값이다. 기본값은 None이다.
            - ALLOWED_VERSIONS. 설정되면, 이 값은 버전 관리 체계에 의해 반환될 수 있는 버전 집합을 제한하고, 제공된 버전이 이 집합에 없는 경우 오류를 발생시킨다. DEFAULT_VERSION 설정에 사용된 값은 항상 ALLOWED_VERSIONS 집합의 일부로 간주된다(단, None인 경우 제외). 기본값은 None이다.

            - VERSION_PARAM. 미디어 타입이나 URL 쿼리 매개변수 등의 버전 관리 매개변수에 사용해야 하는 문자열이다. 기본값은 'version'이다.

 

         - 또한 자체 버전 관리 체계를 정의하고 default_version, allowed_versions 및 version_param 클래스 변수를 사용하여 뷰 또는 뷰셋별로 버전 관리 클래스와 이 세 가지 값을 설정할 수도 있다.

         - 예를 들어, URLPathVersioning을 사용하려는 경우,

from rest_framework.versioning import URLPathVersioning
from rest_framework.views import APIView

class ExampleVersioning(URLPathVersioning):
    default_version = ...
    allowed_versions = ...
    version_param = ...

class ExampleView(APIVIew):
    versioning_class = ExampleVersioning

 

 2. API Reference

   1) AcceptHeaderVersioning

      - 이 체계는 클라이언트가 Accept 헤더의 미디어 타입의 일부로 버전을 지정하도록 요구한다. 버전은 주 미디어 타입을 보완하는 미디어 타입 매개변수로 포함된다.

      - 다음은 accept 헤더 버전 관리 스타일을 사용하는 HTTP 요청의 예시이다.

GET /bookings/ HTTP/1.1
Host: example.com
Accept: application/json; version=1.0

 

      - 위의 예제 요청에서 request.version 속성은 '1.0'이라는 문자열을 반환한다.

      - Accept 헤더를 기반으로 한 버전 관리는 일반적으로 베스트 프랙티스로 간주되지만, 클라이언트 요구 사항에 따라 다른 스타일이 적합할 수 있다.
         - generally considered : https://steveklabnik.com/writing/nobody-understands-rest-or-http#i_want_my_api_to_be_versioned
         - best practice : https://github.com/interagent/http-api-design/blob/master/en/foundations/require-versioning-in-the-accepts-header.md

      3. 벤더 미디어 타입과 함께 accept 헤더 사용하기(Using accept headers with vendor media types)

         - 엄밀히 말하면, json 미디어 타입은 추가 매개변수를 포함하도록 지정되어 있지 않다. 잘 정의된 공개 API를 구축하고 있다면 벤더 미디어 타입을 사용할 수 있다. 이를 위해 사용자 정의 미디어 타입을 가진 JSON 기반 렌더러를 사용하여 렌더러를 설정한다.

            - including additional parameters : https://datatracker.ietf.org/doc/html/rfc4627#section-6

            - vendor media type : https://en.wikipedia.org/wiki/Internet_media_type#Vendor_tree

class BookingsAPIRenderer(JSONRenderer):
    media_type = 'application/vnd.megacorp.bookings+json'

 

         - 이제 클라이언트 요청은 다음과 같이 보일 것이다.

GET /bookings/ HTTP/1.1
Host: example.com
Accept: application/vnd.megacorp.bookings+json; version=1.0

 

   2) URLPathVersioning

      - 이 체계는 클라이언트가 URL 경로의 일부로 버전을 지정하도록 요구한다.

GET /v1/bookings/ HTTP/1.1
Host: example.com
Accept: application/json

 

      - URL conf는 'version' 키워드 인자와 일치하는 버전 패턴을 포함해야 하므로, 이 정보가 버전 관리 체계에서 사용할 수 있다.

urlpatterns = [
    re_path(
        r'^(?P<version>(v1|v2))/bookings/$',
        bookings_list,
        name='bookings-list'
    ),
    re_path(
        r'^(?P<version>(v1|v2))/bookings/(?P<pk>[0-9]+)/$',
        bookings_detail,
        name='bookings-detail'
    )
]

 

   3) NamespaceVersioning

      - 클라이언트에게 이 체계는 URLPathVersioning과 동일하다. 차이점은 URL 키워드 인자 대신 URL 네임스페이싱을 사용하여 Django 애플리케이션에서 어떻게 구성되는지에 있다.

GET /v1/something/ HTTP/1.1
Host: example.com
Accept: application/json

 

      - 이 체계에서 request.version 속성은 들어오는 요청 경로와 일치하는 네임스페이스에 따라 결정된다.

      - 다음 예제에서는 서로 다른 네임스페이스 아래에 있는 두 가지 다른 가능한 URL 접두사를 뷰 세트에 제공합니다:

# bookings/urls.py
urlpatterns = [
    re_path(r'^$', bookings_list, name='bookings-list'),
    re_path(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')
]

# urls.py
urlpatterns = [
    re_path(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
    re_path(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
]

 

      - URLPathVersioning과 NamespaceVersioning 모두 간단한 버전 관리 체계가 필요한 경우 적절하다.       

      - URLPathVersioning 접근 방식은 소규모의 ad-hoc 프로젝트에 더 적합할 수 있으며, NamespaceVersioning은 대규모 프로젝트를 관리하는 데 아마도 더 쉬울 것이다.

 

   4) HostNameVersioning

      - 호스트 이름 버전 관리 체계는 클라이언트가 URL의 호스트 이름 부분에 요청된 버전을 지정하도록 요구한다.

      - 예를 들어, 다음은 http://v1.example.com/bookings/ URL에 대한 HTTP 요청이다.

GET /bookings/ HTTP/1.1
Host: v1.example.com
Accept: application/json


      - 기본적으로 이 구현은 호스트 이름이 다음 간단한 정규 표현식과 일치하도록 예상한다.

^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$


      - 첫 번째 그룹이 괄호로 묶여 있음을 주의해야 한다. 이는 호스트 이름의 일치하는 부분을 나타낸다.

      - HostNameVersioning 체계는 디버그 모드에서 사용하기 어렵다. 왜냐하면 일반적으로 127.0.0.1과 같은 원시 IP 주소에 접근하게 되기 때문이다. 이 경우 사용자 정의 서브도메인으로 localhost에 접근하는 방법에 대한 다양한 온라인 튜토리얼이 있으므로 도움이 될 수 있다.

         -  access localhost with a custom subdomain : https://reinteractive.net/posts/199-developing-and-testing-rails-applications-with-subdomains

      - 버전을 기반으로 들어오는 요청을 다른 서버로 라우팅하는 요구사항이 있는 경우, 호스트 이름 기반 버전 관리는 특히 유용할 수 있다. 왜냐하면 다른 API 버전에 대해 다른 DNS 레코드를 구성할 수 있기 때문이다.

 

   5) QueryParameterVersioning

      - 이 체계는 URL의 쿼리 매개변수로 버전을 포함하는 간단한 스타일이다.

GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: application/json

 

 3. 사용자 정의 버전 관리 체계(Custom versioning schemes)

   - 사용자 정의 버전 관리 체계를 구현하려면 BaseVersioning을 상속받아 .determine_version 메서드를 오버라이드한다.

 

   1) Example

      - 다음 예제는 사용자 정의 X-API-Version 헤더를 사용하여 요청된 버전을 결정한다.

class XAPIVersionScheme(versioning.BaseVersioning):
    def determine_version(self, request, *args, **kwargs):
        return request.META.get('HTTP_X_API_VERSION', None)

 

      - 버전 관리 체계가 요청 URL에 기반을 두고 있다면, 버전화된 URL이 어떻게 결정되는지 변경하려고 할 것이다. 이를 위해서는 클래스의 .reverse() 메서드를 오버라이드해야 한다. 예제를 보려면 소스 코드를 참조하면 된다.

 

 - 공식 사이트 문서 : https://www.django-rest-framework.org/api-guide/versioning/

 

Versioning - Django REST framework

 

www.django-rest-framework.org