Django FBV와 CBV
함수 기반 뷰와 클래스 기반 뷰의 동작 차이, as_view·dispatch 흐름, 제네릭 뷰까지의 선택 기준
Contents
같은 화면을 함수로 쓸지 클래스로 쓸지는 재사용성과 가독성 사이의 선택입니다.
뷰가 받는 것과 돌려주는 것
Django에서 뷰는 HTTP 요청 객체를 받아 응답 객체를 돌려주는 호출 가능한 객체입니다. 같은 뷰를 파이썬 함수로 작성하면 함수 기반 뷰(Function-Based View, FBV)입니다. 클래스로 작성하면 클래스 기반 뷰(Class-Based View, CBV)라고 부릅니다. 두 방식은 응답 결과가 같고, 차이는 코드를 어떻게 구조화하느냐에 있습니다.
선택의 축은 재사용성과 가독성의 트레이드오프입니다. 단순한 화면은 함수 한 개가 더 읽기 쉽고, 비슷한 화면이 반복되면 클래스 상속이 중복을 줄입니다.
함수 기반 뷰
FBV는 요청을 인자로 받는 파이썬 함수입니다. 요청 메서드 분기를 if request.method 조건문으로 직접 작성합니다.
from django.http import HttpResponse
def my_view(request):
if request.method == 'GET':
return HttpResponse('GET 요청 처리')
elif request.method == 'POST':
return HttpResponse('POST 요청 처리')위 함수는 한 곳에서 모든 분기를 보여 줍니다. 어떤 메서드가 어떤 처리로 이어지는지 위에서 아래로 따라 읽으면 됩니다. 이 명시성이 FBV의 가장 큰 장점이며, 로직이 짧을수록 효과가 큽니다. 대신 비슷한 함수가 여러 개 생기면 공통 부분을 함수 바깥으로 빼내야 하고, 그 작업이 손이 많이 갑니다.
클래스 기반 뷰
CBV는 클래스로 뷰를 정의하고, HTTP 메서드 이름을 그대로 메서드 이름으로 씁니다. get, post 같은 메서드가 각 요청 메서드를 담당합니다.
from django.views import View
from django.http import HttpResponse
class MyView(View):
def get(self, request):
return HttpResponse('GET 요청 처리')
def post(self, request):
return HttpResponse('POST 요청 처리')분기를 조건문이 아니라 메서드 정의로 나눈다는 점이 핵심입니다. 처리할 메서드만 정의하면 되고, 정의하지 않은 메서드는 Django가 자동으로 거절합니다. 상속과 믹스인으로 공통 동작을 여러 뷰가 나눠 쓸 수 있어 재사용에 유리합니다. 대신 흐름이 메서드와 상위 클래스로 흩어져, 작은 화면에는 과한 구조가 됩니다.
URLconf 등록과 as_view
URLconf(URL 설정)는 각 경로에 호출 가능한 객체를 연결합니다. FBV는 이미 함수라서 그대로 연결하지만, CBV는 클래스라서 바로 연결할 수 없습니다. 이 간극을 메우는 것이 as_view()입니다.
from django.urls import path
from . import views
urlpatterns = [
path('posts/', views.my_view, name='posts'), # FBV: 함수 그대로
path('about/', views.MyView.as_view(), name='about'), # CBV: as_view()로 함수화
]as_view()는 View의 클래스 메서드이며, 호출하면 요청을 처리할 뷰 함수를 만들어 돌려줍니다. 즉 URLconf 입장에서는 FBV와 CBV가 똑같이 함수로 보입니다. 클래스라는 구조는 그 함수 안쪽에 숨어 있습니다.
as_view와 dispatch의 동작
요청이 들어오면 as_view()가 만든 함수가 클래스 인스턴스를 생성하고 dispatch()를 호출합니다. dispatch()는 요청 메서드를 소문자로 바꿔 같은 이름의 메서드를 찾아 실행합니다. 정의되지 않은 메서드면 http_method_not_allowed로 보내 HTTP 405 응답을 돌려줍니다. 이 흐름은 Django 공식 문서의 클래스 기반 뷰 설명과 django.views.generic.base.View 구현에 정의되어 있습니다.
여기서 FBV와 CBV의 분기 방식 차이가 드러납니다. FBV는 개발자가 if로 분기를 쓰고, CBV는 dispatch()가 메서드 이름으로 분기를 대신합니다. 허용 메서드 목록은 http_method_names 속성이 들고 있어, 목록에 없는 메서드는 핸들러를 찾기 전에 막힙니다. 분기 코드를 직접 쓰지 않아도 되는 대신, 흐름을 보려면 상위 클래스 구현을 알아야 합니다.
제네릭 뷰와 믹스인
CBV의 실질적 이점은 Django가 제공하는 제네릭 뷰에서 나옵니다. 목록·상세·생성·수정·삭제처럼 자주 쓰는 화면 패턴을 클래스로 미리 만들어 두었습니다. 모델과 템플릿만 지정하면 조회와 페이지네이션 같은 반복 코드를 직접 쓰지 않아도 됩니다.
from django.views.generic import ListView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
paginate_by = 10ListView는 Post 목록 조회, 템플릿 렌더링, 페이지 나누기를 상속받은 동작으로 처리합니다. paginate_by만 지정해도 페이지네이션이 붙는 식입니다. 같은 화면을 FBV로 쓰면 쿼리·페이징·렌더링을 직접 반복해야 합니다.
공통 동작은 믹스인(mixin)으로 끼워 넣습니다. 믹스인은 단독 뷰가 아니라 다른 뷰에 기능을 더하는 작은 클래스입니다. 예를 들어 로그인 검사는 LoginRequiredMixin을 앞에 두어 처리합니다.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
from .models import Post
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'body']
login_url = '/login/'믹스인은 상속 순서가 중요합니다. LoginRequiredMixin을 CreateView보다 앞에 둬야 합니다. 그래야 메서드 결정 순서(Method Resolution Order, MRO)에서 로그인 검사가 먼저 걸립니다. 순서를 바꾸면 검사가 동작하지 않을 수 있으므로, 권한 믹스인은 항상 왼쪽에 둡니다.
무엇을 언제 고르나
두 방식은 같은 결과를 내지만 구조와 비용이 다릅니다.
| 구분 | FBV | CBV |
|---|---|---|
| 구조 | 함수 | 클래스 |
| 재사용성 | 어려움 | 상속·믹스인으로 용이 |
| 가독성 | 단순하고 직관적 | 구조적이지만 흐름이 분산 |
| 확장성 | 기능 추가 시 복잡도 증가 | 상속으로 확장 용이 |
| 제네릭 뷰 | 직접 구현 필요 | Django 제공 제네릭 뷰 사용 |
FBV는 로직이 단순하고 복잡한 처리가 필요 없을 때 적합합니다. 작은 규모나 프로토타입을 빠르게 만들 때, 또는 함수형 스타일을 선호할 때 손에 잘 맞습니다.
CBV는 로직이 복잡하고 여러 요청 메서드를 다룰 때 유리합니다. 여러 뷰에서 공통 기능을 공유해야 하거나, 프로젝트 규모가 커 확장성이 중요할 때 효과가 큽니다. 목록·상세 같은 제네릭 뷰를 그대로 쓰는 경우도 CBV가 자연스럽습니다.
정리
FBV와 CBV는 동작 결과가 같고, 코드를 함수로 구조화하느냐 클래스로 구조화하느냐가 다릅니다. FBV는 분기를 조건문으로 한곳에 모아 읽기 쉽고, CBV는 dispatch()가 메서드 이름으로 분기를 대신해 상속과 믹스인으로 재사용을 키웁니다. 둘 사이를 가르는 기준은 화면의 복잡도와 반복 정도이며, 단순하면 FBV, 패턴이 반복되거나 제네릭 뷰가 맞으면 CBV를 고릅니다. 한 프로젝트 안에서 두 방식을 섞어 써도 무방하니, 화면마다 더 단순해지는 쪽을 고르면 됩니다.