클래스형 뷰

jurin·2021년 7월 15일
0

뷰는 요청을 받아서 응답을 반환해주는 호출 가능한 객체이다. 장고에서는 뷰를 함수로도 작성할 수 있고 클래스로도 작성할 수 있다.

사실 함수형 뷰보다 클래스형 뷰가 장점이 많다. 클래스형 뷰를 사용하면 상속과 믹스인 기능을 사용해서 코드를 재사용할 수 있고, 뷰를 체계적으로 구성할 수 있다. 또한 제네릭 뷰 역시 클래스형 뷰로 작성되어 있다.

클래스 뷰의 시작

클래스형 뷰를 사용하기 위해서는 먼저 URLconf에서 함수형 뷰가 아니라 클래스형 뷰를 사용한다는 점을 표시해줘야 한다.

# urls.py
from django.urls import path
from myapp.views import MyView

urlpatterns = [
    path('about/', MyView.as_view())
]

as_view() 메소드에 주목해보면 장고의 URL 해석기는 요청과 관련된 파라미터들을 클래스가 아니라 함수에 전달하기 때문에 클래스형 뷰는 클래스로 진입하기 위한 진입 메소드인 as_view() 클래스 메소드를 제공한다.

as_view() 진입 메소드의 역할은 클래스의 인스턴스를 생성하고, 그 인스턴스의 dispatch() 메소드를 호출한다. dispatch() 메소드는 요청을 검사해서 GET, POST 등의 어떤 HTTP 메소드로 요청되었는지를 알아낸 다음, 인스턴스 내에서 해당 이름을 갖는 메소드로 요청을 중계 해준다. 만일 해당 메소드가 정의되어 있지 않으면 HttpResponseNotAllowed 익셉션을 발생시킨다.

Myveiw 클래스 views.py 파일에 코딩

from django.http import HttpResponse
from django.views.generic import View

class Myview(View):
    def get(self, request):
        # 뷰 로직 작성
        return HttpResponse('result')

Myview 클래스는 View 클래스를 상속받고 있으며, View 클래스에는 as_view() 메소드와 dispatch() 메소드가 정의되어 있다.

클래스형 뷰 장점

  1. GET, POST 등의 HTTP 메소드에 따른 처리 기능을 코딩할 때, IF 함수를 사용하지 않고 메소드명으로 구분할 수 있어서 코드의 구조가 깔끔해진다.
  2. 다중 상속과 같은 객체 지향 기술이 가능하므로 클래스형 제네릭 뷰 및 믹스인 클래스 등을 사용할 수 있어서 코드의 재사용성이나 개발 생산성을 높여준다.

1. 효율적인 메소드 구분

HTTP 메소드, 즉 GET, POST, HEAD 등의 요청을 수신한 후 클래스형 뷰에서는 어떻게 처리하는지, 그리고 함수형 뷰와 어떻게 다른지 비교해보자.

함수형 뷰 HTTP GET 메소드 코딩

from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # 뷰 로직 생성
        return HttpResponse('result')

요청 request 객체의 method 속성을 체크하는 로직이 필요하므로 항상 if 조건의 체크 문장이 생긴다.

클래스형 뷰 HTTP GET 메소드 코딩

# views.py
from django.http import HttpResponse
from django.views.generic import View

class MyView(View):
    def get(self, request):
        # 뷰 로직 생성
        return HttpResponse('result')

클래스형 뷰에서는 HTTP 메소드 이름으로 클래스 내에 메소드를 정의하면 된다(get(), post(), head()). 내부적으로 있는 dispatch() 메소드가 GET, POST 등의 어떤 HTTP 메소드로 요청되었는지를 알아낸 다음 해당 이름을 갖는 메소드로 요청을 중계해주기 때문에 가능한 일이다.

HEAD 메소드 예제
HEAD HTTP 메소드는 서점에 방문한 직후에 새롭게 출간된 책이 있는지를 서버에게 문의하는 용도이닫. 최근 발간된 책이 없는데도 책 리스트를 서버로부터 받아오면 네트워크 대역폭이 낭비되므로 이를 방지하기 위해 HEAD 메소드를 사용한 것이다. HTTP 프로토콜 규격에 따르면 HEAD 요청에 대한 응답은 바디 없이 헤더만 보내주면 된다.

HEAD 요청을 처리하는 뷰 로직은 간단하게 클래스 내에 head() 메소드를 정의하면 된다.

# views.py
from django.http import HttpResponse
from django.views.generic import  ListView

class BookListView(ListView):
    model = Book

    def head(self, *args, **kwargs):
        last_book = self.get_queryset().latest('publication_date')
        response = HttpResponse('')
        # RFC 1123 date 포맷
        response['List-Modified'] = last_book.publication_date.strftime('&a, &d &b &Y GMT')
        return response

2. 상속 가능 기능

상속 기능을 이해하려면 제네릭 뷰에 대해 알고 있어야 한다. 클래스형 뷰의 대부분은 장고가 제공해주는 제네릭 뷰를 상속받아 작성하기 때문이다.

제네릭 뷰는 뷰 개발 과정에서 개발자의 단순 반복 작업을 덜어주는 기능이다. 즉, 제네릭 뷰는 뷰 개발 과정에서 공통적으로 사용할 수 있는 기능들을 추상화하고, 이를 장고에서 미리 만들어 기본적으로 제공해주는 클래스 뷰를 말한다.
뷰가 클래스이기 때문에 제네릭 뷰도 상속받아서 작성할 수 있는 것이다.

제네릭 뷰를 상속받아서 클래스형 뷰를 작성하는 예제
/about/이라는 URL로 웹 요청이 들어오면 단순하게 about.html 템플릿을 보여준다고 가정한다.

# some_app/urls.py
from django.urls import path
from some_app.views import AboutView

urlpatterns = [
    path('/about/', AboutView.as_view()),
]
# some_app/views.py
from django.views.generic import TemplateView

class AboutView(TemplateView):
    template_name = "about.html"

요청 request 객체를 분석하고, 템플릿 시스템에 넘겨줄 컨텍스트 변수를 구성하는 것은 모두 TemplateView 제네릭 뷰에서 처리한다. 우린 간단하게 about.html 템플릿 파일을 사용하라고만 알려주면 된다.

더 간단하게 TemplateView 제네릭 뷰를 사용할 수도 있다.

from django.urls import path
from django.views.generic import TemplateView

urlpatterns = [
    path('/about/', TemplateView.as_view(template_name="about.html")),
]

상속 기능과 더불어 중요한 기능인 오버라이딩 기능이 포함되어 있다는 것도 기억해야 한다. template_name은 TemplateView 클래스에 정의되어 있는 클래스 속성인데 필요에 따라 오버라이딩하여 사용한다. 하위 클래스인 AboutView나 as_view() 메소드를 통해서도 오버라이딩할 수 있다.

클래스형 제네릭 뷰

장고에서는 URL 패턴으로부터 파라미터를 추출하고 그 파라미터로 데이터베이스를 검색하여 해당 데이터를 템플릿 시스템에서 렌더링하는 기능처럼 웹 프로그램 개발 시 공통적으로 사용할 수 있는 로직을 이미 개발해 놓고 기본 클래스로 제공하고 있기 때문에 이를 상속받아 사용하면 된다.

공통된 로직을 미리 개발해 놓고 제공하는 클래스형 뷰를 제네릭 뷰라고 한다.

제네릭 뷰의 4가지 분류

  • Base View : 뷰 클래스를 생성하고, 다른 제네릭 뷰의 부모 클래스를 제공하는 기본 제네릭 뷰
  • Generic Display View : 객체의 리스트를 보여주거나 특정 객체의 상세 정보를 보여준다.
  • Generic Edit View : 폼을 통해 객체를 생성, 수정, 삭제하는 기능을 제공한다.
  • Generic Date View : 날짜 기반 객체의 연/월/일 페이지로 구분해서 보여준다.

클래스형 뷰에서 폼 처리

  • 최초의 GET : 사용자에게 처음으로 폼(빈 폼 또는 초기 데이터로 채워진 폼)을 보여줌
  • 유효한 데이터를 가진 POST : 데이터를 처리함. 주로 리다이렉트 처리됨
  • 유효하지 않은 데이터를 가진 POST : 보통은 에러 메시지와 함께 폼이 다시 출력됨

함수형 뷰와 클래스형 뷰의 폼 처리 과정을 비교해보자

함수형 뷰로 폼을 처리

from django.http import HttpResponseRedirect
from django.shortcuts import render

from .forms import MyForm

def myview(request):
    if request.method == 'POST':
        form = MyForm(request.POST)
        if form.is_valid():
            # cleaned_data로 관련 로직 처리
            return HttpResponseRedirect('/success/')
    
    else:
        form = MyForm(initial={'key': 'value'})
        
    return render(request, 'form_template.html', {'form': form})

IF 문장으로 HTTP의 GET 방식과 POST 방식을 구분하여 뷰가 GET 방식으로 요청 받은 경우 사용자에게 처음으로 폼을 보여주도록 하고 뷰가 POST 방식으로 요청을 받은 경우 데이터가 담긴 제출된 폼으로 간주하여 처리한다.

클래스형 뷰로 폼을 처리

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views.generic import View

from .forms import MyForm

class MyFormView(View):
    form_class = MyForm
    initial = {'key': 'value'}
    template_name = 'form_template.html'

    # 최초의 GET
    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {'form': form})
    
    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)

        # 유효한 데이터를 가진 POST
        if form.is_valid():
            # cleaned_data로 관련 로직 처리
            return HttpResponseRedirect('/success/')
        # 유효하지 않은 데이터를 가진 POST
        return render(request, self.template_name, {'form': form})

위에서는 View 제네릭 뷰를 상속하여 작성하였지만, 폼 처리용 제네릭 뷰인 FormView를 상속받아 처리하면 코드가 훨씬 간결해진다.

from django.views.generic import FormView
from .forms import MyForm

class MyFormView(FormView):
    form_class = MyForm
    initial = {'key': 'value'}
    template_name = 'form_template.html'

    def form_valid(self, form):
        # cleaned_data로 관련 로직 처리
        return super(MyFormView, self).form_valid(form)

FormView 제네릭 뷰를 사용하면 FormView 클래스에 이미 정의되어 있기 때문에 클래스 내에 get(), post() 메소드 정의도 필요 없게 된다. 다음 4가지를 유의해서 코딩하면 된다.

  • form_class : 사용자에 보여줄 폼을 정의한 forms.py 파일 내의 클래스명
  • template_name : 폼을 포함하여 렌더링할 템플릿 파일 이름
  • success_url : MyFormView 처리가 정상적으로 완료되었을 때 리다이렉트시킬 URL
  • form_valid() 함수 : 유효한 폼 데이터로 처리할 로직 코딩. super() 함수를 사용하면, success_url로 지정된 URL로 리다이렉션 처리됨




출처: Django로 배우는 파이썬 웹 프로그래밍(기초) - 김석훈님

profile
anaooauc1236@naver.com

0개의 댓글