DRF Router

guswls·2022년 8월 6일
1

DRF 시리즈

목록 보기
3/5
post-thumbnail

이 포스트는 인프런의 "Django REST Framework 핵심사항"강의를 듣고 제작하였습니다.


시작하기 전에 앞서 일반적으로 장고를 활용할 때 아래의 순서를 따르는 것이 좋다.

settings.py -> models.py -> urls.py -> views.py -> templates


1. 라우팅이란?


라우팅에 대한 설명을 찾아보면 다음과 같다.

라우팅(routing)은 어떤 네트워크 안에서 통신 데이터를 보낼 때 최적의 경로를 선택하는 과정이다.
                                                                                                   -위키백과

위에서 말하는 큰 의미에서의 라우팅과 우리가 장고에서 말하는 라우팅은 조금 다른 부분이 있다. 장고에서 말하는 라우팅은 다음과 같이 이해하면 된다.

접속한 주소에 따라서 적절한 처리 로직을 연결해주는 작업을 라우팅이라고 하고 이러한 일을 도와주는 도구를 라우터라고 한다.

위의 설명을 듣고 딱 생각나는 형태가 있다.

바로 우리가 urls.py에서 urlpatterns안의 path에서 하나의url에 대해 view를 연결해주는 작업을 바로 라우팅이라고 할 수 있을 것이다. 이에 대한 자세한 설명은
이 영상을 보면 아주 잘 이해될 것이다.

그럼 이제 DRF의 router를 얘기해보자.


2. DRF Router tutorial

공식문서의 튜토리얼을 따라가면서 router를 사용할 때와 사용하지 않을 때는 비교해가면서 알아보도록 하자.

2-1. Router를 사용하지 않는 경우

우리가 Router를 사용하지 않을 경우 각각의 URLViewSet을 연결시켜주어야 한다.

#api2 > urls.py

from django.urls import path
from rest_framework import routers
from api2.views import UserViewSet
from rest_framework.urlpatterns import format_suffix_patterns

user_list = UserViewSet.as_view({
    'post':'create',
    'get': 'list'
})
user_detail = UserViewSet.as_view({
    'get': 'retrieve',
    'put': 'partial_update',
    'patch': 'partial_update',
    'delete': 'destroy'
})

urlpatterns = format_suffix_patterns([
    path('users/', user_list, name='user-list'),
    path('users/<int:pk>/', user_detail, name='user-detail')
])

format_suffix_patterns에 대해서는 이 글을 참고하자.

우리가 router를 사용하지 않고 example에 있는 api를 사용하려면 위와 같이 구현을 해주어야 한다.

장고의 공식문서를 읽어보자.

The handler methods only get bound to the actions when we define the URLConf. To see what's going on under the hood let's first explicitly create a set of views from our ViewSets.
                                                                                                   -공식문서

즉, 우리가 ViewSetpathURI를 연결하려면 ViewSet에서 사용될 HTTP Method를 명시적으로 작성해주어야 한다는 말이다.

사실 지금과 같이 하나의 테이블에 대한 CRUD operation만 구현한다면 어렵진 않겠지만 binding해야되는 URL의 갯수가 많아진다면 간단한 구조라도 코드가 복잡하고 길어질 것이다.

이에 대한 과정을 간략하게 줄여주는 것이 DRFrouter이다.

2-2. Router를 사용하는 경우

가상환경 안에 설치된 drfrouter들에 대해서 정의해놓은 파일을 살펴보면 다음과 같은 글을 찾아볼 수 있다.

Routers provide a convenient and consistent way of automatically
determining the URL conf for your API
.

They are used by simply instantiating a Router class, and then registering
all the required ViewSets with that router.
                                                                                    -routers.py 내부 문서

해석하자면 우리의 APIURL conf자동적으로 결정해주며 라우터 클래스
인스턴스화(객체화) 시키고 ViewSets등록(register)하라는 말이다.

drfDefaultRouter를 사용하는 경우 위의 코드는 다음과 같이 간단하게 된다.

#api2 > urls.py

from django.urls import path, include
from rest_framework import routers
from api2.views import UserViewSet

router = routers.DefaultRouter()
router.register(r'users', UserViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

위에서 설명한 것과 DefaultRouter()객체화 시킨 router에 우리가 사용할 ViewSet을 등록하기만 해도 우리가 위에서 URL을 바인딩하는 작업이 완료된 것이다.

register( )의 필수 인자

  • prefix : 라우터를 사용할 url들의 접두사
    ex) users

  • ViewSet : 라우터로 연결시켜줄 ViewSet클래스
    ex) UserViewSet

이제 Router클래스에 대해서 간단하게 알아보려고 한다.


3. router.py 분석


우리가 만든 가상환경에 들어가면 우리가 설치한 라이브러리프레임워크의 파일들에 대해서 찾아볼 수 있다.

#가상환경 > Lib > site-packages > rest_framework > routers.py

"생략"

class BaseRouter:
(...)

class SimpleRouter(BaseRouter):
(...)

class APIrootView(views.APIVew):
(...)

class DefaultRouter(SimpleRouter):
(...)

위와 같이 Router 클래스가 4개가 있는 것을 확인할 수 있다.

또한 SimpleRouterBaseRouter를 상속받아 만들어졌으며 DefaultRouterSimpleRouter를 상속받아 만들어졌음을 알 수 있다.

3-1. BaseRouter

BaseRouter를 열어보면 다음과 같다.

#가상환경 > Lib > site-packages > rest_framework > routers.py

"생략"

class BaseRouter:
	def __init__(self):
    (...)
    def register(self, prefix, viewset, basename=None):
    (...)
    def get_default_basename(self, viewset):
    (...)
    def get_urls(self):
    (...)
    
    @property
    def urls(self)
    (...)

우리가 DefaultRouter활용할 때 사용한 register도 이 안에 정의되어있다.

인자로 받는 값중에 basename이라는 값도 경우에 따라 받을 수 있도록 지정되어있다. basename은 라우팅을 할 때 사용할 URLName에 관한 인자이다. 일반적으로는 우리가 지정한 ViewSet에서 자동적으로 지정된다.

기능을 제공하기 위해 만들었다기 보단 Base클래스로써 만들어진 느낌이 강하고 실제로 공식문서에서도 BaseRouter에 대한 설명은 나타나있지 않다.

3-2. SimpleRouter

이제 SimpleRouter에 대해서 알아보자.

#가상환경 > Lib > site-packages > rest_framework > routers.py

"생략"

class SimpleRouter:
	routes = [
    	Route(...)
        DynamicRoute(...)
        Route(...)
        DynamicRoute(...)
    ]
    
    def __init__(self, trailing_slash=True):
    	(...)
    def get_default_basename(self,viewset):
    	(...)
    def get_routes(self, viewset):
    	(...)
    def _get_dynamic_route(self, route, action):
    	(...)
	def get_method_map(self, viewset, method_map):
    	(...)
    def get_lookup_regex(self, viewset, lookup_prefix=''):
    	(...)
    def get_urls(self):
    	(...)

위 클래스에서 routes로 정의된 리스트에는 자동적으로 생성되는 URL에 관한 내용이 담겨있다. 그리고 BaseRouter에 비해 많은 메소드들이 들어있는 것이 확인 가능하다.

아래의 표는 SimpleRouter가 기본으로 제공하는 페이지의 URL이다. 이 표를 토대로 SimpleRouter가 자동적으로 지정해주는 URL은 다음과 같이 볼 수 있을 것이다.

{prefix}/ : GET, POST 2가지 요청
{prefix}/{pk} : GET, PUT, PATCH, DELETE 4가지 요청


3-3. DefaultRouter

DefaultRouter는 위에서 찾아봤다시피 SimpleRouter에서 상속받아 만들어진 클래스이다. 여기서 다시 Router클래스들의 상속관계를 살펴보면 다음과 같다.

BaseRouter > SimpleRouter > DefaultRouter

즉, DefaultRouterSimpleRouter의 기능에서 추가적인 기능을 제공하는 클래스라고 볼 수 있다.

아래의 표는 DefaultRouter가 기본적으로 제공하는 URL들이다. DefaultRouterSimpleRouter에서 추가로 제공하는 기능은 다음과 같다.

  • api-rootURL : api-root페이지를 기본적으로 제공한다. 이 페이지에서 모든 list에 대한 api의 하이퍼링크를 제공한다. ex) /api2/users

  • format-suffixes : 응답형식(json/api)에 대해서 URL로 지정할 수 있다.
    ex) /api2/users/1.json로 하면 json형식으로 GET요청의 응답을 해준다.

DefaultRouter클래스의 코드를 분석해보면 다음과 같다.

#가상환경 > Lib > site-packages > rest_framework > routers.py

class DefaultRouter(SimpleRouter):
	include_root_view = True
    include_format_suffixes = True
    root_view_name = 'api-root'
    default_schema_renderers = None
    APIRootView = APIRootView
    APISchemaView = SchemaView
    SchemaGenerator = SchamaGenerator
    
    def __init__(self, *args, **kwargs):
    	(...)
	def get_api_root_view(self, api_urls=None):
    	(...)
	def get_urls(self):
    	(...)

fomat_suffixesAPIRootView에 관한 내용이 SimpleRouter에서 추가된 것을 확인할 수 있다.

include_format_suffixes가 응답형식에 관한 부분이며 APIRootView가 기본 제공되는 api-root페이지를 구현한 classView이다. 실제로 코드를 열어보면 GET요청에 대한 내용을 다루고 있음을 확인할 수 있다.

DefaultRouter가 기본 제공하는 URL를 정리하면 다음과 같다.

{prefix}/ : GET, POST 2가지 요청
{prefix}/{pk} : GET, PUT, PATCH, DELETE 4가지 요청


/ : api-root페이지
{prefix}.json : GET 요청의 응답을 JSON형식으로 해줌. ( list )
{prefix}/{pk}.json : GET 요청의 응답을 JSON형식으로 해줌. ( retrieve )


4. 총정리


  1. RouterViewSet에 대응되는 URL을 자동적으로 생성해준다.
  2. DefaultRouter가 제공하는 기능이 제일 많다.
profile
안녕하세요

2개의 댓글

comment-user-thumbnail
2023년 1월 5일

이유를 잘 짚어 설명해주셔서 술술 이해됐어요. 감사합니다!

1개의 답글