[Django] View: 함수 기반 vs 클래스 기반
django view, FBV, CBV, class-based view, function-based view, generic view
정의
Django View는 HTTP 요청을 받아 응답을 반환하는 callable. 두 스타일이 공존.
- FBV (Function-Based View): 일반 함수. 간단하고 명시적.
- CBV (Class-Based View): 클래스. 상속·믹스인으로 재사용성.
신규 코드에 둘 다 일반적. 단순한 건 FBV, 패턴 반복(CRUD)은 CBV.
FBV
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from .models import Post
def post_list(request):
posts = Post.objects.filter(is_published=True).order_by("-created_at")
return render(request, "blog/post_list.html", {"posts": posts})
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug, is_published=True)
return render(request, "blog/post_detail.html", {"post": post})
@login_required
def post_create(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect(post.get_absolute_url())
else:
form = PostForm()
return render(request, "blog/post_form.html", {"form": form})
request.method로 GET/POST 분기, request.POST/request.GET/request.FILES로 데이터 접근.
응답 헬퍼
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect, Http404
from django.shortcuts import render, redirect, get_object_or_404, get_list_or_404
return HttpResponse("Hello") # 텍스트
return HttpResponse("...", content_type="text/xml")
return JsonResponse({"id": 1, "name": "Alice"})
return render(request, "template.html", context)
return redirect("post-list") # name으로
return redirect("/blog/") # URL
return redirect(post) # get_absolute_url
return HttpResponseRedirect("/")
CBV: View 기본
from django.views import View
class PostListView(View):
def get(self, request):
posts = Post.objects.all()
return render(request, "blog/post_list.html", {"posts": posts})
def post(self, request):
...
# urls.py
path("posts/", PostListView.as_view(), name="post-list"),
as_view()로 클래스를 callable로 변환. 각 HTTP 메서드를 메서드로 정의.
Generic CBV: ListView, DetailView
CRUD 패턴이 사전 정의된 클래스들.
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
class PostListView(ListView):
model = Post
template_name = "blog/post_list.html" # 기본: blog/post_list.html (관례)
context_object_name = "posts" # 기본: object_list
paginate_by = 20
def get_queryset(self):
return Post.objects.filter(is_published=True).select_related("author")
class PostDetailView(DetailView):
model = Post
template_name = "blog/post_detail.html" # 기본: blog/post_detail.html
context_object_name = "post" # 기본: object 또는 model명 소문자
slug_url_kwarg = "slug" # URL의 slug 파라미터
# urls.py
path("posts/", PostListView.as_view(), name="post-list"),
path("posts/<slug:slug>/", PostDetailView.as_view(), name="post-detail"),
CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ["title", "body"]
success_url = reverse_lazy("post-list")
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UpdateView):
model = Post
fields = ["title", "body"]
template_name = "blog/post_form.html" # 기본은 model_form.html
class PostDeleteView(LoginRequiredMixin, DeleteView):
model = Post
success_url = reverse_lazy("post-list")
template_name = "blog/post_confirm_delete.html"
폼 검증·저장·리다이렉트 등 모두 처리. fields 또는 form_class 명시.
Mixin
여러 CBV에 공통 기능 주입.
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
def test_func(self):
return self.get_object().author == self.request.user
MRO 순서 중요: Mixin → 본 클래스 순. Mixin이 왼쪽.
자주 쓰는 mixin:
LoginRequiredMixin: 로그인 강제PermissionRequiredMixin: 권한 체크UserPassesTestMixin: 임의 조건FormView: 폼 처리 베이스
get_context_data
템플릿에 추가 컨텍스트.
class PostDetailView(DetailView):
model = Post
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["comments"] = self.object.comments.filter(approved=True)
context["related_posts"] = Post.objects.filter(tags__in=self.object.tags.all()).exclude(id=self.object.id)[:5]
return context
get_queryset
목록 조회 동적 필터.
class PostListView(ListView):
def get_queryset(self):
qs = super().get_queryset()
if q := self.request.GET.get("q"):
qs = qs.filter(title__icontains=q)
return qs.order_by("-created_at")
데코레이터
FBV에 자주 쓰는 데코레이터.
from django.contrib.auth.decorators import login_required, permission_required
from django.views.decorators.http import require_http_methods, require_POST
from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import csrf_exempt
@login_required
@require_POST
def vote(request, post_id):
...
@require_http_methods(["GET", "POST"])
def my_view(request):
...
@cache_page(60 * 15) # 15분 캐시
def expensive_view(request):
...
@csrf_exempt # CSRF 검증 생략 (API 등, JWT 인증 시)
def webhook(request):
...
CBV에 적용 시 @method_decorator:
from django.utils.decorators import method_decorator
@method_decorator(login_required, name="dispatch")
class MyView(View):
...
HTTP 메서드 분기
CBV는 자동, FBV는 수동.
def my_view(request):
if request.method == "GET":
return ...
elif request.method == "POST":
return ...
return HttpResponseNotAllowed(["GET", "POST"])
request 객체
request.method # "GET", "POST"
request.GET # QueryDict (?key=value)
request.POST # POST body (form-encoded)
request.body # raw bytes (JSON 등은 직접 파싱)
request.FILES # 업로드 파일
request.user # 인증된 사용자 (AnonymousUser if not)
request.session
request.COOKIES
request.META # WSGI 환경 (헤더 등)
request.headers # 더 깔끔한 헤더 접근 (2.2+)
request.path
request.build_absolute_uri()
JSON body:
import json
data = json.loads(request.body)
함정
1. CBV 메서드 이름 vs HTTP 메서드
get, post, put, delete, patch, head, options. 그 외 메서드는 처리 안 됨.
2. as_view() 의 인자
path("posts/<int:year>/", PostListView.as_view(year=None)), # 클래스 변수 오버라이드 가능
3. self.request, self.kwargs
CBV에서 메서드 내 접근.
class MyView(DetailView):
def get_context_data(self, **kwargs):
slug = self.kwargs["slug"] # URL 파라미터
user = self.request.user
...
4. FBV vs CBV 선택
- 한 가지 일만 → FBV
- CRUD 표준 패턴 → CBV (생산성)
- 비표준 / 복잡 흐름 → FBV가 더 명시적
- API → DRF의 ViewSet (CBV 기반)
가능한 한 일관성. 팀이 한 쪽 선호하면 따름.
💬 댓글