이 포스트는 인프런의 "Django REST Framework 핵심사항"강의를 듣고 제작하였습니다.
시작하기 전에 앞서 일반적으로 장고를 활용할 때 아래의 순서를 따르는 것이 좋다.
settings.py -> models.py -> urls.py -> views.py -> templates
라우팅
에 대한 설명을 찾아보면 다음과 같다.
라우팅
(routing)은 어떤 네트워크 안에서 통신 데이터를 보낼 때 최적의 경로를 선택하는 과정이다.
-위키백과
위에서 말하는 큰 의미에서의 라우팅
과 우리가 장고에서 말하는 라우팅
은 조금 다른 부분이 있다. 장고에서 말하는 라우팅
은 다음과 같이 이해하면 된다.
접속한 주소에 따라서 적절한 처리 로직을 연결해주는 작업을
라우팅
이라고 하고 이러한 일을 도와주는 도구를라우터
라고 한다.
위의 설명을 듣고 딱 생각나는 형태가 있다.
바로 우리가 urls.py
에서 urlpatterns
안의 path
에서 하나의url
에 대해 view
를 연결해주는 작업을 바로 라우팅
이라고 할 수 있을 것이다. 이에 대한 자세한 설명은
이 영상을 보면 아주 잘 이해될 것이다.
그럼 이제 DRF의 router
를 얘기해보자.
공식문서의 튜토리얼을 따라가면서 router
를 사용할 때와 사용하지 않을 때는 비교해가면서 알아보도록 하자.
우리가 Router
를 사용하지 않을 경우 각각의 URL
와 ViewSet
을 연결시켜주어야 한다.
#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.
-공식문서
즉, 우리가 ViewSet
과 path
의 URI
를 연결하려면 ViewSet
에서 사용될 HTTP Method
를 명시적으로 작성해주어야 한다는 말이다.
사실 지금과 같이 하나의 테이블
에 대한 CRUD operation
만 구현한다면 어렵진 않겠지만 binding
해야되는 URL
의 갯수가 많아진다면 간단한 구조라도 코드가 복잡하고 길어질 것이다.
이에 대한 과정을 간략하게 줄여주는 것이 DRF
의 router
이다.
가상환경 안에 설치된 drf
의 router
들에 대해서 정의해놓은 파일을 살펴보면 다음과 같은 글을 찾아볼 수 있다.
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 내부 문서
해석하자면 우리의 API
와 URL conf
를 자동적으로 결정해주며 라우터 클래스
를
인스턴스화
(객체화) 시키고 ViewSets
을 등록
(register)하라는 말이다.
drf
의 DefaultRouter
를 사용하는 경우 위의 코드는 다음과 같이 간단하게 된다.
#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
클래스에 대해서 간단하게 알아보려고 한다.
우리가 만든 가상환경
에 들어가면 우리가 설치한 라이브러리
나 프레임워크
의 파일들에 대해서 찾아볼 수 있다.
#가상환경 > Lib > site-packages > rest_framework > routers.py
"생략"
class BaseRouter:
(...)
class SimpleRouter(BaseRouter):
(...)
class APIrootView(views.APIVew):
(...)
class DefaultRouter(SimpleRouter):
(...)
위와 같이 Router
클래스가 4개가 있는 것을 확인할 수 있다.
또한 SimpleRouter
는 BaseRouter
를 상속받아 만들어졌으며 DefaultRouter
가 SimpleRouter
를 상속받아 만들어졌음을 알 수 있다.
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
에 대한 설명은 나타나있지 않다.
이제 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가지 요청
DefaultRouter
는 위에서 찾아봤다시피 SimpleRouter
에서 상속받아 만들어진 클래스이다. 여기서 다시 Router
클래스들의 상속관계를 살펴보면 다음과 같다.
BaseRouter
>SimpleRouter
>DefaultRouter
즉, DefaultRouter
는 SimpleRouter
의 기능에서 추가적인 기능을 제공하는 클래스라고 볼 수 있다.
아래의 표는 DefaultRouter
가 기본적으로 제공하는 URL
들이다. DefaultRouter
가 SimpleRouter
에서 추가로 제공하는 기능은 다음과 같다.
api-root
의URL
: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_suffixes
와 APIRootView
에 관한 내용이 SimpleRouter
에서 추가된 것을 확인할 수 있다.
include_format_suffixes
가 응답형식에 관한 부분이며 APIRootView
가 기본 제공되는 api-root
페이지를 구현한 class
형 View
이다. 실제로 코드를 열어보면 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
)
Router
는ViewSet
에 대응되는URL
을 자동적으로 생성해준다.DefaultRouter
가 제공하는 기능이 제일 많다.
이유를 잘 짚어 설명해주셔서 술술 이해됐어요. 감사합니다!