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 요청시 수정된 데이터를 처리 및 저장한다.
- pk나 slug로 객체를 식별한다.
- 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 |