DRF3 튜토리얼 5 - 관계 & 하이퍼링크 API

원문 - Relationships and Hyperlinked APIs

번역을 허락해 준 Tom Christie에게 고마움을 전합니다.


튜토리얼 5: 관계 & 하이퍼링크 API

지금까지 우리가 만든 API에서 '관계'는 주 키(primary key)로 나타나고 있었습니다. 이번 튜토리얼에서는 API의 발견성(discoverability)과 응집력(cohesion)을 향상시키고자 관계를 하이퍼링크로 나타내 보겠습니다.

API의 최상단에 대한 엔드 포인트 만들기

지금까지 '코드 조각'과 '사용자'에 대한 엔드 포인트를 만들었지만, 아직까지 이렇다 할 API의 시작점은 없었습니다. 이를 만들기 위해 평범한 함수 기반 뷰와 @api_view 데코레이터(2부 참고)를 사용해 보겠습니다. snippets/views.py 파일을 여세요.

from rest_framework.decorators import api_view  
from rest_framework.response import Response  
from rest_framework.reverse import reverse


@api_view(('GET',))
def api_root(request, format=None):  
    return Response({
        'users': reverse('user-list', request=request, format=format),
        'snippets': reverse('snippet-list', request=request, format=format)
    })

여기서 URL을 만드는 데 reverse 함수를 사용한 점을 주목하세요.

코드 조각의 하이라이트 버전에 대한 엔드 포인트 만들기

우리 API에서 아직까지 만들지 않은 부분은 바로, 코드 조각의 하이라이트 버전을 볼 수 있는 방법입니다.

API의 다른 부분과는 달리 이번에는 JSON 대신 HTML 형태로 나타내겠습니다. REST 프레임워크에서 HTML로 렌더링하는 방식은 두 가지 정도가 있는데, 하나는 템플릿을 사용하는 것이고, 다른 하나는 미리 렌더링된 HTML을 사용하는 것입니다. 이번에는 후자가 더 어울려 보입니다.

하이라이트된 코드 조각을 보여주려고 할 때 주의해야 할 점 하나는, 우리가 사용할 만한 제네릭 뷰가 없다는 것입니다. 오브젝트 자체가 아니라, 오브젝트의 속성 하나를 반환할 것이기 때문입니다.

제네릭 뷰 대신 평범한 클래스를 사용하고, .get() 메서드를 구현하겠습니다. snippets/views.py 파일에 다음 내용을 추가하세요.

from rest_framework import renderers  
from rest_framework.response import Response

class SnippetHighlight(generics.GenericAPIView):  
    queryset = Snippet.objects.all()
    renderer_classes = (renderers.StaticHTMLRenderer,)

    def get(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

지금까지 그래왔듯이, 새 뷰는 URL 설정에서도 연결해야 합니다.

snippets/urls.py 파일에서 최상단 url을 방금 만든 뷰로 연결합시다.

url(r'^$', views.api_root),  

하이라이트된 코드 조각을 볼 수 있는 url에 대한 패턴도 추가합니다.

url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', views.SnippetHighlight.as_view()),  

하이퍼링크로 API 연결하기

요소들 사이의 관계를 다루는 일은 웹 API 설계에서 또 하나의 도전 과제입니다. 관계를 표현하는 방법은 꽤 다양합니다.

  • 주 키(primary key)
  • 하이퍼링크
  • 관계 요소의 식별 가능한 슬러그(slug) 필드
  • 관계 요소의 기본 문자열 표현
  • 포함된 관계 요소에 대한 표현
  • 이 외에도 사용자화된 표현

REST 프레임워크에서는 모든 방법을 지원합니다. 관계 혹은 역관계에 적용하거나, 제네릭 외부 키(foreign key)처럼 사용자화된 manager에 적용할 수도 있습니다.

여기서는 하이퍼링크 방식을 채택하겠습니다. 이러게 하려면 기존에 사용했던 ModelSerializerHyperlinkedModelSerializer로 변경해야 합니다.

HyperlinkedModelSerializer는 다음과 같은 점들이 다릅니다.

  • pk 필드는 기본 요소가 아닙니다.
  • HyperlinkedIdentityField를 사용하는 url 필드가 포함되어 있습니다.
  • 관계는 PrimaryKeyRelatedField 대신 HyperlinkedRelatedField를 사용하여 나타냅니다.

기존 시리얼라이저에 하이퍼링크를 추가하기는 쉽습니다. snippets/serializers.py 파일에 다음 내용을 추가합시다.

class SnippetSerializer(serializers.HyperlinkedModelSerializer):  
    owner = serializers.ReadOnlyField(source='owner.username')
    highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')

    class Meta:
        model = Snippet
        fields = ('url', 'highlight', 'owner',
                  'title', 'code', 'linenos', 'language', 'style')


class UserSerializer(serializers.HyperlinkedModelSerializer):  
    snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)

    class Meta:
        model = User
        fields = ('url', 'username', 'snippets')

이 코드에서는 새롭게 'highlight' 필드가 추가되었습니다. 이 필드는 url 필드와 같은 타입이며, 'snippet-detail' url 패턴 대신 'snippet-highlight' url 패턴을 가리킵니다.

앞에서 URL의 format 접미어로 '.json'을 붙였듯이, highlight 필드에는 format 접미어로 '.html'을 붙였습니다.

URL 패턴에 이름 붙이기

하이퍼링크 API를 만들고 싶다면, URL 패턴에 이름을 붙여야 합니다. 어떤 패턴들인지 살펴봅시다.

  • API의 최상단은 'user-list''snippet-list'를 가리킵니다.
  • 코드 조각 시리얼라이저에는 'snippet-highlight'를 가리키는 필드가 존재합니다.
  • 사용자 시리얼라이저에는 'snippet-detail'을 가리키는 필드가 존재합니다.
  • 코드 조각 시리얼라이저와 사용자 시리얼라이저에는 'url' 필드가 존재합니다. 이 필드는 기본적으로 '{모델_이름}-detail'을 가리키며 따라서 'snippet-detail''user-detail'을 가리킵니다.

이 이름들을 URL 설정에 넣었다면 snippets/urls.py 파일은 다음과 같을 겁니다.

from django.conf.urls import url, include

# API endpoints
urlpatterns = format_suffix_patterns([  
    url(r'^$', views.api_root),
    url(r'^snippets/$',
        views.SnippetList.as_view(),
        name='snippet-list'),
    url(r'^snippets/(?P<pk>[0-9]+)/$',
        views.SnippetDetail.as_view(),
        name='snippet-detail'),
    url(r'^snippets/(?P<pk>[0-9]+)/highlight/$',
        views.SnippetHighlight.as_view(),
        name='snippet-highlight'),
    url(r'^users/$',
        views.UserList.as_view(),
        name='user-list'),
    url(r'^users/(?P<pk>[0-9]+)/$',
        views.UserDetail.as_view(),
        name='user-detail')
])

# 탐색 가능한 API를 위한 로그인/로그아웃 뷰
urlpatterns += [  
    url(r'^api-auth/', include('rest_framework.urls',
                               namespace='rest_framework')),
]

페이징 기능 추가하기

사용자나 코드 조각의 목록이 꽤 긴 경우가 있을 겁니다. 따라서 결과물을 여러 페이지로 나누어, API 클라이언트 측에서 각 페이지를 하나씩 차례대로 읽어가도록 만들면 좋겠습니다.

tutorial/settings.py 파일을 열어 페이징 설정의 기본 값을 바꿔 봅시다. 다음을 추가합니다.

REST_FRAMEWORK = {  
    'PAGE_SIZE': 10
}

REST 프레임워크의 모든 설정은 'REST_FRAMEWORK'라는 딕셔너리에 넣어야 합니다. 이렇게 해야 프로젝트의 다른 설정들과 분리할 수 있습니다.

필요에 따라 페이징 스타일을 바꿀 수도 있지만 여기서는 기본 스타일을 따르겠습니다.

탐색 가능한 API

탐색 가능한 API를 브라우저에서 열어서 링크들을 이리 저리 눌러보면, API의 구석구석을 둘러볼 수 있을 겁니다.

또한 코드 조각 인스턴스의 '하이라이트 버전'을 살펴볼 수도 있겠습니다. (HTML 형태입니다.)

튜토리얼 6부에서는 뷰셋과 라우터를 통해 코드 양을 줄이는 방법을 배우겠습니다.

번역에 도움을 주신 파이님께 감사합니다.

Show Comments