Django, DRF FBV(Function Based Views) vs CBV(Class Based Views)

정현우·2022년 5월 31일
3

Django Basic to Advanced

목록 보기
21/39
post-thumbnail

FBV vs CBV

Django, 특히 DRF로 작업하다 보면 여러가지 mixin, DRF에서는 from rest_framework import generics 로 부터 class를 상속 받아서 CBV로 작업을 많이한다. 하지만 CBV 작업은 주로 CRUD에만 초점이 맞춰져 있다. 어떨때 어떤 형태를 사용하면 좋을까?

FBV, CBV에 대해 자세히 알아보기

FBV

  • FBV는 django의 시발점이다. CBV형태의 API는 태초의 django에서 없었다. Function으로 API를 정의하는 형태이다. 기능을 템플릿화하는 방법으로 CBV를 추가하여 보일러 플레이트(즉, 동일한 코드) 코드를 반복해서 작성할 필요가 없게 만들었다. 시리즈에 존재하는 순수 django에서 view를 먼저 살펴보자.

  • 위 글에서는 함수 형태로 선언하고 from django.http import HttpResponse로 부터 HTTP 응답을 주고, 해당 함수를 URL에 매핑해 주는 형태였다. (FBV, CBV는 DRF에서 두드러지게 나타난다) 아래 예제를 보자.

# urls.py
urlpatterns= [
	path('', views.index, name='index'),
]

# views.py
@api_view(['GET', 'POST'])
def index(request):
	if request.method == 'POST':
    		return HttpResponse("Post method")
    	else:
    		return HttpResponse("Get method")

장점

  • 데코레이터 하나, api_view에서 어떤 HTTP request method를 받는지 알 수 있다. 그리고 직관적이다. 직관적이기 때문에 읽기 쉽고, 해석하기 쉽다.

  • 작성하기 간단하다. flask 형식의 API를 찍어내는 느낌이 강하다.

  • 단발성, 재사용 가능성이 낮은 특별한 비즈니스 로직의 API를 작성할 때 유리하다.

단점

  • 하지만 1회성 성격이기때문에 재사용성이 낮고, 상속의 개념이 아니라 확장하기 힘들다. 그렇기 때문에 비즈니스 로직에, 익숙한 OOP 디자인패턴을 적용하기 힘들다.

  • CRUD를 만들때, 조건문 분기처리를 해서 다 따로 하나하나 처리를 해야한다. 또는 method를 분리하면 같은 모델 대상으로 단순 CRUD만 하더라도 4개의 함수를 작성해야 한다.

CBV

  • CBV는 위에서 언급 하였듯이 초기에는 없었고, Class Based Views 로 확장을 위해서 만들어 졌다.

  • CBV는 이미 Django core에서 기본적인 로직은 다 짜여져 있고, 오버라이딩을 하여 사용을 하면 된다. 그리고 해당 class를 as_view()라는 함수 내부의 dispatch() 를 통하여 request method를 CBV내부에 함수 정의 여부에 따라 알아서 적절한 함수를 호출해 준다.

# urls.py
urlpatterns= [
	path('', views.IndexView.as_view(), name='index'),
]

# views.py
from django.views import View

class ContactView(View):
	def post(self, request):
    		return HttpResponse("Post method")
	def get(self, request):
    		return HttpResponse("Get method")

장점

  • 단순하게 Django의 View CBV를 상속받아서 어떤 메소드인지 체크하는 예제이다.

  • HTTP method명을 메소드 (class 의 함수)를 선언하는 것으로 request를 받아서 모두 처리할 수 있다. 그래서 코드 중복을 줄일 수 있다. (DRY하다)

  • 해당 class를 다시 상속 받아 ( class GetPostModelView(ContactView) ... )와 같이 확장하고, 재사용 가능하다. 그렇기 때문에 익숙한 OOP 디자인 패턴을 상상해 볼 수 있다.

단점

  • 단순한 요청, 단발성 1회성, 특수한 비즈니스 로직 처리에 상속으로 인해 버려지는 코드가 있다. 그렇기 때문에 필요없는 기능까지 모두 상속받아서 만들어 무거워질 수 있다.

  • 해석하기 어렵다. 만약 위 처럼 core view를 상속받아 자체적으로 다양한 형태의 mixin을 만들어서 사용하면, 처음 접하는 사람에게는 러닝커브가 상당할 수 밖에 없다. 다 따라가서 찾아봐야 하기 때문이다. 추상화의 단점과 일맥상통하다.

중요한건 Mixin

  • Django는 아주 다양한 형태의 Mixin을 제공한다. 대표적으로 generic이 있고, 또 그것이 DRF에서의 generic으로도 존재한다. 이를 활용한 DRF를 제대로 이해하기 위해서는 사전에 'serializer' 에 대한 지식이 필요하다.

  • DRF의 generic은 시리즈에 어떤 것들이 있고, 단순 CRUD CBV를 어떻게 찍어내는지 보여주는 글을 참조 바란다.

    • 추가로 붙이자면, 해당 글에서는 method에 따른 generic 상속 class와 CBV만 설명하고 있지만, 제대로 사용하려면 해당 method를 정의하는 class 함수에 create, save, update, perform_create 등의 함수를 모두 살펴보는 것을 추천한다.
  • 아래는 FBV vs CBV에 대한 선택지를 던져주는 다이어 그램이다. 한 번 참조 해보면 좋을 것 같다.


참조 : https://medium.com/@ksarthak4ever/django-class-based-views-vs-function-based-view-e74b47b2e41b


결론

CBV는 FBV를 '대체'하기 위해 등장한 것이 아니다.

  • CBV는 확장성과 django 철학에 맞는 'less code'에 초점이 맞춰져서 등장한 것이다. 덜 작성하고, 빠르게 확장하기 위해 CBV가 등장했다!

  • 적절한 상황에서 CBV와 FBV를 적절하게 섞어야 한다. 필자의 경우 CBV가 너무 편했던 경험은, 특정 모델(Model)에 통계적 정보를 전달하기 위해서 작성했다.

class ModelDailyCountAPIView(generics.ListAPIView):
    permission_classes = [IsAdminUser]
    target_model = None

    # 데일리 - 공통 통계 함수
    # from start_date to end_date 까지 "일 단위로 개수 카운팅"
    def obj_daily_counts(self, start_date, end_date):
        start_date = datetime.date(int(start_date[0]), int(start_date[1]), int(start_date[2]))
        end_date = datetime.date(int(end_date[0]), int(end_date[1]), int(end_date[2]))
        
		obj_daily_counts = self.target_model.objects\
        	.filter(created_at__range=[start_date, end_date])\
            .annotate(year=ExtractYear('created_at'))\
            .annotate(month=ExtractMonth('created_at'))\
            .annotate(day=ExtractDay('created_at'))\
            .values('year', 'month', 'day')\
            .annotate(count=Count('pk'))
		return obj_daily_counts          
  • Django offical Docs에서는 If you find you’re struggling to implement your view as a subclass of a generic view, then you may find it more effective to write just the code you need, using your own class-based or functional views. 라고 첨언이 되어 있다.

  • 필자의 경우에는 우선적으로 CBV로 작성한다. 그리고 상속할 필요가 없는 하나의 비즈니스 로직만 계속 길어지고, 재사용 가능성이 없게, 특이해지는 경우에는 FBV로 분리하려고 한다. 특히 특정 단순 모델의 bulk-delete경우 FBV가 훨씬 편하게 느껴졌다.

  • 그리고 CBV로 규칙을 만드니 인수인계와 공통 모듈 작업에도 더 편하게 느껴졌다.

    • ex) 특정 generic을 상속받은, 모듈화된 CBV mixin 자체 제작 후 해당 CBV 상속에 대한 가이드 라인.
profile
도메인 중심의 개발, 깊이의 가치를 이해하고 “문제 해결” 에 몰두하는 개발자가 되고싶습니다. 그러기 위해 항상 새로운 것에 도전하고 노력하는 개발자가 되고 싶습니다!

0개의 댓글