DRF3 튜토리얼 6 - 뷰셋과 라우터
원문 - Viewsets and Routers
번역을 허락해 준 Tom Christie에게 고마움을 전합니다.
튜토리얼 6: 뷰셋 & 라우터
REST 프레임워크는 ViewSets
이라는 추상 클래스를 제공합니다. 이를 통해 개발자는 API의 상호작용이나 상태별 모델링에 집중할 수 있고, URL 구조는 기본 관례에 따라 자동으로 설정됩니다.
ViewSet
클래스는 View
클래스와 거의 비슷하지만, get
과 put
메서드는 지원하지 않고 read
와 update
메서드를 지원합니다.
ViewSet
클래스는 따지고 보면, 앞 장에서 만든 핸들러 메서드가 실제 뷰로 구체화될 때 이를 연결해주기만 합니다. 이때 보통은 Router
클래스를 사용하여 복잡한 URL 설정을 처리합니다.
뷰셋을 사용하여 리팩터링하기
지금까지 만든 뷰들을 살펴보면서 뷰셋을 사용해서 리팩터링을 해봅시다.
가장 먼저 리팩터링할 뷰는 UserList
와 UserDetail
뷰입니다. UserViewSet
하나로 모아보죠. 두 뷰의 코드를 삭제한 다음 아래의 클래스 하나를 입력합니다.
from rest_framework import viewsets
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
이 뷰셋은 `list`와 `detail` 기능을 자동으로 지원합니다
"""
queryset = User.objects.all()
serializer_class = UserSerializer
여기서 사용한 ReadOnlyModelViewSet
클래스는 '읽기 전용' 기능을 자동으로 지원합니다. queryset
과 serializer_class
속성은 여전히 설정을 해야 하지만, 두 개의 클래스에 중복으로 설정할 필요는 없어졌습니다.
다음으로는 SnippetList
와 SnippetDetail
, SnippetHighlight
뷰를 리팩터링해보죠. 이 뷰들을 삭제하고, 아래의 클래스를 입력합니다.
from rest_framework.decorators import detail_route
class SnippetViewSet(viewsets.ModelViewSet):
"""
이 뷰셋은 `list`와 `create`, `retrieve`, `update`, 'destroy` 기능을 자동으로 지원합니다
여기에 `highlight` 기능의 코드만 추가로 작성했습니다
"""
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
@detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
이번에는 읽기 기능과 쓰기 기능을 모두 지원하기 위해 ModelViewSet
클래스를 사용했습니다.
추가한 highlight
기능에는 여전히 @detail_route
데코레이터를 사용했습니다. 이 데코레이터는 create
나 update
, delete
에 해당하지 않는 기능에 대해 사용하면 됩니다.
@detail_route
데코레이터를 사용한 기능은 기본적으로 GET
요청에 응답합니다. methods
인자를 설정하면 POST
요청에도 응답할 수 있습니다.
추가 기능의 URL은 기본적으로 메서드 이름과 같습니다. 이를 변경하고 싶다면 데코레이터에 url_path
인자를 설정하면 됩니다.
뷰셋과 주소를 명시적으로 연결하기
핸들러 메서드는 단지 URL 설정과 연결하는 기능만 담당합니다. 물 밑에서 어떤 일들이 벌어지는지 알아보고자, 여기서는 먼저 뷰셋의 뷰들을 명시적으로 적어보겠습니다.
urls.py
파일에서 ViewSet
클래스를 실제 뷰(concrete view)와 연결합니다.
from snippets.views import SnippetViewSet, UserViewSet, api_root
from rest_framework import renderers
snippet_list = SnippetViewSet.as_view({
'get': 'list',
'post': 'create'
})
snippet_detail = SnippetViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})
snippet_highlight = SnippetViewSet.as_view({
'get': 'highlight'
}, renderer_classes=[renderers.StaticHTMLRenderer])
user_list = UserViewSet.as_view({
'get': 'list'
})
user_detail = UserViewSet.as_view({
'get': 'retrieve'
})
ViewSet
클래스의 뷰들을 HTTP 메서드에 따라 어떻게 실제 뷰와 연결했는지 살펴보세요.
이제 실제 뷰와 URL을 연결해보겠습니다.
urlpatterns = format_suffix_patterns([
url(r'^$', api_root),
url(r'^snippets/$', snippet_list, name='snippet-list'),
url(r'^snippets/(?P<pk>[0-9]+)/$', snippet_detail, name='snippet-detail'),
url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'),
url(r'^users/$', user_list, name='user-list'),
url(r'^users/(?P<pk>[0-9]+)/$', user_detail, name='user-detail')
])
라우터 사용하기
View
클래스 대신 ViewSet
클래스를 사용했기 때문에, 이제는 URL도 설정할 필요가 없습니다. Router
클래스를 사용하면 뷰 코드와 뷰, URL이 관례적으로 자동 연결됩니다. 단지 뷰를 라우터에 적절히 등록해주기만 하면 됩니다. 그러면 REST 프레임워크가 알아서 다 합니다.
urls.py
파일을 다음과 같이 고쳐보죠.
from django.conf.urls import url, include
from snippets import views
from rest_framework.routers import DefaultRouter
# 라우터를 생성하고 뷰셋을 등록합니다
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)
router.register(r'users', views.UserViewSet)
# 이제 API URL을 라우터가 자동으로 인식합니다
# 추가로 탐색 가능한 API를 구현하기 위해 로그인에 사용할 URL은 직접 설정을 했습니다
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
라우터에 뷰셋을 등록하는 일은 url 패턴 설정하기와 비슷합니다. 여기서는 두 개를 등록했는데요. 뷰들에 사용할 URL의 접두어와 뷰셋입니다.
DefaultRouter
클래스는 API의 최상단 뷰를 자동으로 생성해주므로, views
모듈에 있는 api_root
메서드와 연결했던 URL도 삭제하였습니다.
뷰? 뷰셋? 장단점 비교하기
뷰셋은 유용한 추상화입니다. API 전반에 걸쳐 일관적인 URL 관례를 구현할 수 있고 작성할 코드 양은 최소한으로 유지할 수 있어서, URL 설정에 낭비될 정성을 API의 상호작용과 표현 자체에 쏟을 수 있습니다.
하지만 이것이 항상 옳다는 뜻은 아닙니다. 클래스 기반 뷰와 함수 기반 뷰에 각각 장단점이 있듯이 말이죠. 뷰셋을 사용하면 명확함이 좀 약해집니다.
회고
정말 적은 양의 코드만으로 pastebin과 같은 웹 API를 구현했습니다. 이 API는 웹 브라우저를 완벽히 지원하고, 인증 기능도 있고, 오브젝트별로 권한도 설정되며 다양한 형태로 렌더링됩니다.
지금까지 기본 Django 뷰에서 시작하여 기능들을 점진적으로 만드는 설계 과정을 차근차근 살펴보았습니다.
최종 코드는 GitHub에서 볼 수 있고, 라이브 데모에서 작동 모습을 확인할 수도 있습니다.
더 많은 것들
이제 튜토리얼이 끝났습니다. REST 프레임워크에 대해 더 알고 싶다면 다음 링크들로 시작하면 좋을 겁니다.
- GitHub에서 이슈를 등록하거나 리뷰하고, pull requests를 작성해 보세요.
- REST 프레임워크 사용자 모임에 가입해서 활동해 보세요.
- 창시자의 트위터를 팔로우하고 인사를 나누세요.
이제 멋진 무언가를 만들어 보세요.
번역에 도움을 주신 목돌님과 조청님께 감사합니다.
COMMENTS