본문으로 건너뛰기
김신건의 로그

[Django] View: 함수 기반 vs 클래스 기반

· 수정 · 📖 약 1분 · 402자/단어 #django #view #cbv #fbv
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 기반)

가능한 한 일관성. 팀이 한 쪽 선호하면 따름.

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기