반응형

Django의 Class-Based Views(CBV)는 객체 지향적인 방식으로 뷰를 구현할 수 있게 해주며, 코드의 재사용성과 확장성을 크게 향상시켜주는 강력한 기능이다. CBV의 내부 동작 방식과 주요 메서드들의 실행 흐름을 자세히 살펴보려고 한다.



CBV의 기본 구조

사용자가 웹 브라우저에서 요청을 보내면, Django는 URL 패턴을 통해 해당 요청을 적절한 CBV로 라우팅한다.
Django CBV는 View 클래스as_view() 클래스 메서드를 통해 URL 패턴에 연결되며, 이는 실제로 뷰 함수로 변환된다. 관련해서는 아래 포스팅을 참고하면 된다.

https://comgu.tistory.com/entry/Django-View-클래스의-asview-메서드

 

[Django] View 클래스의 as_view() 메서드

Django를 사용시 as_view() 메서드를 자주 마주치게 된다. URL 설정에서 클래스 기반 뷰(Class-based View)를 연결할 때 항상 이 메서드를 호출한다. 이 메서드가 어떤 일을 하는지, 그리고 왜 필요한지 알

comgu.tistory.com

뷰 함수가 실행되면, CBV 내부에서는 일련의 메서드들이 순차적으로 실행되면서 요청을 처리한다.

초기 설정 단계: setup()

모든 요청은 먼저 setup() 메서드를 통과한다. setup()은 가장 먼저 실행되며, 뷰 인스턴스에 필요한 기본 속성들을 설정한다. 여기서 request 객체와 URL로부터 전달받은 인자들이 인스턴스 변수로 저장된다.

def setup(self, request, *args, **kwargs):
    """
    모든 요청 처리의 시작점
    request 객체와 URL 파라미터들을 클래스 속성으로 설정
    """
    self.request = request
    self.args = args
    self.kwargs = kwargs

Dispatch 단계: dispatch()

그 다음으로 dispatch() 메서드가 호출된다. 이 메서드는 트래픽 관리자와 같은 역할을 하여, HTTP 메서드에 따라 적절한 핸들러 메서드를 호출한다. 예를 들어, GET 요청이 들어오면 get() 메서드를, POST 요청이 들어오면 post() 메서드를 실행한다.

def dispatch(self, request, *args, **kwargs):
    """
    요청의 HTTP 메서드를 확인하고 적절한 핸들러로 라우팅
    """
    if request.method.lower() in self.http_method_names:
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)

이후 단계들은 View 클래스의 용도에 따른 구현이 매우 다양하다. 먼저 generic view들부터 살펴본다.

 

Generic CBV

Django의 Generic CBV들은 웹 애플리케이션 개발에서 자주 사용되는 일반적인 패턴들을 추상화하여 제공하며, 일반적인 CRUD 작업을 쉽게 처리할 수 있도록 추가적인 메서드들을 제공한다. 대표적인 generic view들을 살펴보자.

View

최상위 CBV 클래스인 View도 generic view에 해당된다. 모든 View는 이 View 클래스를 상속한다. as_view() 클래스 메서드로 URL 패턴에 연결되며, dispatch() 메서드를 통해 요청을 적절한 핸들러로 라우팅한다.

TemplateView

TemplateView는 주로 정적인 콘텐츠를 보여주는 데 사용되며, 템플릿을 렌더링하는 것이 주요 목적이다.

  • get_context_data() 메소드를 통해 템플릿에 데이터를 전달할 수 있다.
  • GET 요청을 자동으로 처리한다(get() 메소드)

RedirectView

RedirectView는 들어오는 요청을 다른 URL로 리다이렉트하는 기능을 수행한다. URL 변경, 레거시 URL 처리, 조건부 리다이렉션 등 다양한 시나리오에서 활용된다.

주요 속성:

  • url: 리다이렉트할 URL
  • pattern_name: 리다이렉트할 URL 패턴
  • query_string: URL 파라미터 유지 여부

ListView

ListView는 모델의 객체 목록을 표시하는데 사용된다. (ex. 블로그 포스트 목록이나 상품 카탈로그 등을 구현)

  • 객체 목록을 표시한다.
  • get_queryset()이 핵심 메서드이다.
  • paginate_by 설정으로 페이지네이션이 가능하다.

DetailView

DetailView는 단일 객체의 상세 정보를 표시하는데 사용된다.

  • 단일 객체의 상세 정보를 표시한다.
  • get_object()가 핵심 메서드이다.
  • 객체가 없을 경우 404 에러로 처리한다.

FormView

FormView는 폼 처리를 위한 generic view로, GET 요청과 POST 요청에 따라 다른 흐름을 가진다.

  • 폼 처리 전용 뷰이다.
  • form_valid(), form_invalid()가 핵심 메서드이다.
  • success_url 처리가 중요하다.

CreateView

CreateView는 모델 객체 생성을 담당한다.

  • 자동으로 모델 폼 생성한다.
  • GET 요청시 빈 폼을 표시하며, POST 요청시 폼 데이터를 처리 및 저장한다.
  • 유효성 검사 통과 시 실행되는 form_valid()로 객체 저장 전 추가 처리를 할 수 있다.
  • 성공 시 success_url로 리다이렉트된다.
  • get_context_data()로 템플릿에 추가 컨텍스트 데이터 전달이 가능하다.

UpdateView

UpdateView는 모델 객체 수정을 담당한다.

  • 자동으로 객체를 조회하여 폼에 데이터를 채운다.
  • GET 요청시 기존 데이터가 채워진 폼을 표시하며, POST 요청시 수정된 데이터를 처리 및 저장한다.
  • pkslug로 객체를 식별한다.
  • CreateView와 거의 동일한 인터페이스를 제공한다.

DeleteView

DeleteView는 모델 객체 삭제를 담당한다.

  • GET 요청시 삭제 확인 페이지를 표시하며, POST 요청시 실제 객체의 삭제를 수행한다.
  • success_url로 삭제 후 리다이렉트된다.
  • pk 또는 slug로 삭제할 객체를 식별한다.

 

 

CBV 주요 메서드

CBV에서 중요한 메서드이거나, CBV를 커스터마이징할 때 가장 자주 오버라이드되는 메서드들의 용도를 살펴보자:

dispatch()

모든 요청에 대한 선처리가 필요할 때 사용한다.

class BookView(View):
    def dispatch(self, request, *args, **kwargs):
        # 모든 요청 처리 전에 실행
        if not request.user.is_authenticated:
            return redirect('login')
        return super().dispatch(request, *args, **kwargs)

get_queryset()

데이터베이스에서 객체를 조회하는 방식을 커스터마이징할 때 사용한다.

class BookListView(ListView):
    model = Book
    
    def get_queryset(self):
        # 출판된 책만 필터링
        return Book.objects.filter(is_published=True)

get_context_data()

템플릿에 전달할 컨텍스트 데이터를 추가하거나 수정할 때 사용한다.

class BookDetailView(DetailView):
    model = Book
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 추가 컨텍스트 데이터 전달
        context['related_books'] = Book.objects.filter(
            category=self.object.category
        ).exclude(id=self.object.id)[:3]
        return context

get_object()

단일 객체를 조회하는 방식을 커스터마이징할 때 사용한다.

class BookDetailView(DetailView):
    model = Book
    
    def get_object(self, queryset=None):
        obj = super().get_object(queryset)
        # 조회수 증가
        obj.views += 1
        obj.save()
        return obj

form_valid() / form_invalid()

form_valid(): 폼 데이터가 유효할 때 실행된다. 이 함수는 post() 함수의 내부에서 호출된다.

form_invalid(): 폼이 유효하지 않을 때 실행된다. 이 함수 역시도 post() 함수의 내부에서 호출된다.

class BookCreateView(CreateView):
    model = Book
    form_class = BookForm
    success_url = reverse_lazy('book-list')
    
    def form_valid(self, form):
        # 폼이 유효할 때 실행
        form.instance.author = self.request.user
        return super().form_valid(form)
    
    def form_invalid(self, form):
        # 폼이 유효하지 않을 때 실행
        messages.error(self.request, "입력 정보를 확인해주세요.")
        return super().form_invalid(form)

get() / post() 등 HTTP 메소드 별 핸들러

HTTP 메서드별 처리 로직을 직접 정의할 때 사용한다.

class BookView(View):
    def get(self, request, *args, **kwargs):
        # GET 요청 처리
        return render(request, 'book_detail.html', {'book': self.get_object()})
    
    def post(self, request, *args, **kwargs):
        # POST 요청 처리
        book = self.get_object()
        book.likes += 1
        book.save()
        return redirect('book-detail', pk=book.pk)

get_form_class()

동적으로 폼 클래스를 결정할 때 사용한다. 오버라이드 되지 않는다면, 기본적으로는 뷰의 form_class 속성에 할당된 폼 클래스가 반환된다.

class BookCreateView(CreateView):
    model = Book
    
    def get_form_class(self):
        # 사용자 권한에 따라 다른 폼 반환
        if self.request.user.is_staff:
            return AdminBookForm
        return UserBookForm

get_success_url()

요청 처리를 성공힌 후 리다이렉트할 URL을 동적으로 생성할 때 사용한다.

class BookUpdateView(UpdateView):
    model = Book
    form_class = BookForm
    
    def get_success_url(self):
        # 수정 완료 후 리다이렉트할 URL 동적 생성
        messages.success(self.request, "책 정보가 수정되었습니다.")
        return reverse('book-detail', kwargs={'pk': self.object.pk})

get_template_names()

조건에 따라 다른 템플릿을 사용해야 할 때 사용한다.

class BookDetailView(DetailView):
    model = Book
    
    def get_template_names(self):
        # 조건에 따라 다른 템플릿 사용
        if self.request.user.is_mobile:
            return ['books/mobile/book_detail.html']
        return ['books/book_detail.html']

get_initial()

폼의 초기값을 동적으로 설정할 때 사용한다.

class BookCreateView(CreateView):
    model = Book
    form_class = BookForm
    
    def get_initial(self):
        # 폼의 초기값 설정
        initial = super().get_initial()
        initial['category'] = self.request.GET.get('category')
        initial['author'] = self.request.user
        return initial

get_form_kwargs()

폼 인스턴스 생성 시 추가 인자를 전달할 때 사용한다. 

class BookCreateView(CreateView):
    model = Book
    form_class = BookForm
    
    def get_form_kwargs(self):
        # 폼 인스턴스 생성 시 추가 인자 전달
        kwargs = super().get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

get_form()

get_form_class()으로부터 얻은 폼 클래스의 인스턴스를 생성한다. 인스턴스 생성시, get_form_kwargs()에서 얻은 kwargs 딕셔너리가 사용된다.

get_context_object_name()

템플릿에서 사용할 객체나 리스트의 변수명을 커스터마이징할 때 사용한다.

class BookListView(ListView):
    model = Book
    
    def get_context_object_name(self, object_list):
        # 템플릿에서 사용할 컨텍스트 변수명 설정
        return 'book_list_custom'

 

사용자 정의 뷰

실제 프로젝트에서는 제네릭 뷰만으로 해결할 수 없는 복잡한 요구사항들이 있다. 이러한 경우 사용자 정의 뷰를 만들어 사용한다.

사용자 정의 뷰의 특징

  • HTTP 메서드별로 별도의 처리 로직을 구현할 수 있다.
  • Mixin을 통해 공통 기능을 재사용할 수 있다.
  • 요청과 응답을 세밀하게 제어할 수 있다.

1. 기본적인 사용자 정의 뷰 예시

from django.views import View
from django.shortcuts import render

class SimpleView(View):
    def get(self, request, *args, **kwargs):
        context = {'message': 'Hello, World!'}
        return render(request, 'simple.html', context)
        
    def post(self, request, *args, **kwargs):
        data = request.POST.get('data')
        # 데이터 처리 로직
        return redirect('success-page')

2. Mixin을 활용한 뷰 예시

from django.contrib.auth.mixins import LoginRequiredMixin

class LogMixin:
    def dispatch(self, request, *args, **kwargs):
        print(f"접근 시간: {timezone.now()}")
        return super().dispatch(request, *args, **kwargs)

class DashboardView(LoginRequiredMixin, LogMixin, View):
    def get(self, request, *args, **kwargs):
        user_data = {
            'username': request.user.username,
            'last_login': request.user.last_login
        }
        return render(request, 'dashboard.html', user_data)

3. API 응답을 위한 뷰 예시

from django.http import JsonResponse

class UserAPIView(View):
    def get(self, request, *args, **kwargs):
        user = request.user
        data = {
            'id': user.id,
            'username': user.username,
            'email': user.email
        }
        return JsonResponse(data)

 

결론

Django CBV는 처음 배울 때는 복잡하지만, 각 메서드의 역할과 실행 순서를 이해하면 유용한 도구가 된다. Generic View는 일반적인 웹 개발 패턴을 쉽게 구현할 수 있게 해주고, 사용자 정의 뷰는 통해 복잡한 비즈니스 요구사항을 처리할 수 있게 해준다.

반응형

'Django' 카테고리의 다른 글

[Django] CreateView  (0) 2025.02.04
[Django] PostgreSQL 연동하기  (1) 2025.02.04
[Django] 템플릿에 Bootstrap 적용하기  (0) 2025.01.24
[Django] Messages Framework  (0) 2025.01.23
[Django] 사용자 모델 구현 방법 비교  (0) 2025.01.22

+ Recent posts