지난 글에서는 Django의 클래스 기반 뷰(CBV)가 왜 필요한지, 함수 기반 뷰(FBV)와 비교했을 때 어떤 장점을 갖고 있는지를 살펴봤습니다. 이번 포스트에서는 모든 CBV의 근본이라고 할 수 있는 Django의 기본 View 클래스에 대해 자세히 알아보겠습니다.

“CBV를 제대로 이해하려면 django.views.View가 어떻게 동작하고 어떤 구조를 갖는지부터 파악하는 것이 중요합니다.”


1. Django View 클래스란?

Django의 모든 클래스 기반 뷰는 다음과 같은 형태로 django.views.View 클래스를 상속받아 사용합니다.

from django.views import View

class MyView(View):
    def get(self, request, *args, **kwargs):
        # GET 요청이 들어왔을 때 처리할 로직
        ...

    def post(self, request, *args, **kwargs):
        # POST 요청이 들어왔을 때 처리할 로직
        ...

View 클래스의 특징

  1. HTTP 메서드별 메서드 분리
    요청이 들어오면 Django는 내부적으로 dispatch() 메서드를 통해 get(), post(), put(), delete() 등 요청 메서드에 해당하는 함수를 자동으로 호출합니다.

    • FBV처럼 “if request.method == 'GET': … else: …” 라는 조건문을 쓰지 않아도 되므로 코드 구조가 깔끔합니다.
  2. 상속과 확장성
    View 클래스를 상속받아 여러 자식 뷰를 생성할 수 있으며, 공통 로직은 부모 클래스에, 개별 로직은 자식 클래스에 넣음으로써 코드 재사용을 극대화할 수 있습니다.


2. View 클래스의 기본 구조와 원리

Django View 구조 LEGO 스타일

가장 단순한 예시

# views.py
from django.http import HttpResponse
from django.views import View

class HelloCBV(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse("Hello, CBV!")
  • get() 메서드: 브라우저에서 GET 요청(주소창에 URL 입력, 링크 클릭 등)을 보냈을 때 실행됩니다.

  • HttpResponse("Hello, CBV!"): 텍스트 형태의 응답을 직접 만들어 반환합니다.

urls.py 연결하기

# urls.py
from django.urls import path
from .views import HelloCBV

urlpatterns = [
    path('hello/', HelloCBV.as_view(), name='hello_cbv'),
]
  • .as_view() 메서드는 CBV를 Django가 이해할 수 있는 형태로 변환해 줍니다.

  • URL 패턴과 연결해 주면, http://example.com/hello/로 GET 요청이 들어올 때 HelloCBVget() 메서드가 호출됩니다.


3. View 클래스 사용법과 실전 예시

위 예시처럼 단순히 “문자열 하나를 반환하는” 경우는 흔치 않을 것입니다. 보통 실제 프로젝트에서는 JSON 응답을 하거나, 템플릿 렌더링을 하거나, 데이터베이스 쿼리를 수행하는 경우가 많죠. 여기서는 JSON 응답템플릿 렌더링을 예시로 살펴보겠습니다.

3.1 JSON 응답 처리 예시

# views.py
from django.http import JsonResponse
from django.views import View

class UserDataView(View):
    def get(self, request, *args, **kwargs):
        # 예: GET 파라미터로 user_id를 받는다고 가정
        user_id = request.GET.get('user_id', None)
        if not user_id:
            return JsonResponse({'error': 'No user_id provided'}, status=400)

        # 실제로는 DB 조회 등을 수행
        user_data = {
            'id': user_id,
            'name': f'User_{user_id}',
            'role': 'admin'
        }
        return JsonResponse(user_data, status=200)

    def post(self, request, *args, **kwargs):
        # POST 요청 바디에서 JSON 혹은 form 데이터를 파싱할 수 있습니다.
        # request.POST, request.body, request.FILES 등을 활용 가능
        # 실제 서비스에서는 JSON 파싱 후 DB 저장 로직 등을 작성
        new_user_data = {
            'id': 123,
            'name': 'NewUser',
            'role': 'member'
        }
        return JsonResponse(new_user_data, status=201)
  • JsonResponse: Django에서 JSON 응답을 편리하게 생성할 수 있는 도구입니다.

  • request.GET, request.POST: Django의 HttpRequest 객체에서 GET/POST 파라미터를 가져올 수 있습니다.

  • 실전에서는 DB 모델과 연동하거나 Serializer를 사용하여 더욱 구조화된 방식으로 데이터를 처리합니다.

3.2 템플릿 렌더링 예시

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

class GreetingView(View):
    def get(self, request, *args, **kwargs):
        context = {
            'title': 'Welcome to My Site',
            'greeting': 'Hello, this is a GreetingView!'
        }
        return render(request, 'greeting.html', context)
<!-- templates/greeting.html -->
<!DOCTYPE html>
<html>
<head>
  <title>{{ title }}</title>
</head>
<body>
  <h1>{{ greeting }}</h1>
</body>
</html>
  • render() 함수는 템플릿 파일(greeting.html)을 찾아서 렌더링한 HTML을 HttpResponse 형태로 반환합니다.

  • 이렇게 CBV 내부에서 템플릿 렌더링도 간단하게 할 수 있습니다.


4. View 클래스의 확장과 재사용성

View 클래스가 유용한 가장 큰 이유 중 하나는 “코드 재사용”입니다. 프로젝트가 커질수록, 여러 뷰 간에 공통적으로 필요한 로직(예: 로깅, 인증 체크, 공통 데이터 처리 등)을 부모 클래스에 넣고 자식 클래스에서 상속받아 쓰면 훨씬 효율적입니다.

4.1 공통 로직을 부모 클래스에 두기

# views.py
from django.http import JsonResponse
from django.views import View

class BaseLoggingView(View):
    def dispatch(self, request, *args, **kwargs):
        # 모든 요청(GET/POST 등)에 대해 공통 로직 실행
        print(f"[Log] {request.method} request at {request.path}")
        return super().dispatch(request, *args, **kwargs)

class ChildLoggingView(BaseLoggingView):
    def get(self, request, *args, **kwargs):
        data = {'message': 'GET response from child'}
        return JsonResponse(data)

    def post(self, request, *args, **kwargs):
        data = {'message': 'POST response from child'}
        return JsonResponse(data)
  • dispatch() 메서드: Django가 뷰 로직을 실행하기 전·후를 제어할 수 있는 핵심 메서드입니다.

  • 이 예시에서는 모든 요청에 대해 로그를 찍은 뒤, super().dispatch()를 통해 실제 get(), post() 등을 호출합니다.

4.2 Mixin을 통한 기능 추가

Django에서는 종종 Mixin 기법을 사용해 “특정 기능만 섞어넣는” 방식을 택합니다. 예를 들어, LoginRequiredMixin(로그인 여부 체크), PermissionRequiredMixin(권한 체크) 등을 CBV에 섞어 쓰는 식이죠.

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View
from django.http import JsonResponse

class SecuredView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        return JsonResponse({'message': 'You are authenticated.'})
  • Mixin을 다루는 건 뒤에서 제네릭 뷰나 권한 관리 등을 설명할 때 더 자세히 다룰 계획이니, 여기서는 “이런 식으로 기능을 조합할 수 있다” 정도만 알고 넘어가면 충분합니다.

5. FBV와의 간단한 비교

구분 FBV (함수 기반 뷰) CBV (클래스 기반 뷰)
작성 형태 함수 형태로 작성 클래스 & 메서드 형태로 작성
HTTP 메서드 처리 if request.method == 'GET': ... else: ... get(), post() 등 메서드별로 분리
코드 중복 가능성 로직이 복잡해지면 중복 코드가 발생하기 쉬움 상속, Mixin을 통해 공통 로직 재사용 가능
확장성 함수 내부에 공통 로직이 섞여 있는 경우 많음 클래스 상속 구조로 복잡한 요구사항 대응에 적합
가독성 간단한 프로토타입에는 유리 규모가 커질수록 명확한 구조를 유지하기에 더 적합
  • FBV는 소규모 혹은 단순한 기능에는 빠르게 작성하기 좋고, CBV는 확장성과 유지보수에 유리합니다.

  • 특히 팀 프로젝트나 중·대형 프로젝트에서는 CBV 활용이 효율적입니다.


6. 조금 더 들어가 보기: dispatch()와 URL 파라미터

Django View 요청 흐름도

6.1 dispatch()를 직접 오버라이딩하는 이유

  • 공통 전·후처리: 요청이 들어올 때마다 DB 연결을 체크하거나, 권한 검사를 하거나, 로그를 남기는 등의 로직을 여기서 처리할 수 있습니다.

  • 메서드 매핑: 내부적으로 request.method.lower()를 사용해 get(), post(), put() 등을 찾아 호출합니다. 만약 추가적으로 PATCH 같은 메서드를 커스텀 처리하고 싶다면 dispatch()를 오버라이드해서 처리하기도 합니다.

class CustomDispatchView(View):
    def dispatch(self, request, *args, **kwargs):
        print("Custom pre-processing logic here.")
        response = super().dispatch(request, *args, **kwargs)
        print("Custom post-processing logic here.")
        return response

6.2 URL 파라미터 받기

urls.py에서 <int:user_id>/처럼 경로 파라미터를 지정했다면, CBV의 get(), post() 메서드에서 kwargs를 통해 값을 받을 수 있습니다.

![Django View 구조 LEGO 스타일](/media/whitedec/blog_img/view_class_lego_structure.webp)# urls.py
urlpatterns = [
    path('user/<int:user_id>/', UserDetailView.as_view(), name='user_detail'),
]

# views.py
class UserDetailView(View):
    def get(self, request, *args, **kwargs):
        user_id = kwargs.get('user_id')
        # user_id로 DB 조회
        return HttpResponse(f"User ID: {user_id}")
  • FBV와 마찬가지로 kwargs에서 URL 파라미터를 꺼내 쓸 수 있습니다.

7. 더 풍부한 실전 사례: 간단 게시판 예시

“View 클래스를 조금 더 실전적으로 써보고 싶다!”는 분들을 위해, 간단하게 게시판 목록과 상세 페이지를 만드는 예시 코드를 들어보겠습니다. (다음 글에서 제네릭 뷰를 다루면 더 간편하게 CRUD를 구현할 수 있지만, 여기서는 기본 View 클래스로도 가능하다는 점을 보여드립니다.)

# models.py - 예시 모델
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title
# views.py
from django.views import View
from django.shortcuts import render, get_object_or_404
from .models import Post

# 게시글 목록 보기
class PostListView(View):
    def get(self, request, *args, **kwargs):
        posts = Post.objects.order_by('-created_at')
        context = {'posts': posts}
        return render(request, 'post_list.html', context)

# 게시글 상세 보기
class PostDetailView(View):
    def get(self, request, *args, **kwargs):
        post_id = kwargs.get('pk')
        post = get_object_or_404(Post, pk=post_id)
        context = {'post': post}
        return render(request, 'post_detail.html', context)
<!-- post_list.html -->
<!DOCTYPE html>
<html>
<head>
    <title>게시글 목록</title>
</head>
<body>
    <h1>게시글 목록</h1>
    <ul>
        {% for post in posts %}
            <li>
                <a href="{% url 'post_detail' post.id %}">{{ post.title }}</a>
            </li>
        {% endfor %}
    </ul>
</body>
</html>
<!-- post_detail.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{{ post.title }}</title>
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
    <p>작성일: {{ post.created_at }}</p>
</body>
</html>
# urls.py
from django.urls import path
from .views import PostListView, PostDetailView

urlpatterns = [
    path('posts/', PostListView.as_view(), name='post_list'),
    path('posts/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
]
  • PostListView: GET 요청 시 모든 게시글 목록을 조회하여 템플릿(post_list.html)에 전달합니다.

  • PostDetailView: URL 파라미터 pk로 특정 게시글을 찾아서 템플릿(post_detail.html)에 전달합니다.

  • 이렇게 기본 View 클래스로도 손쉽게 목록, 상세 페이지를 구성할 수 있습니다.

다만, CRUD 전체를 구성하려면 Create/Update/Delete 로직이 필요합니다. 이를 더 쉽게 해주는 것이 Django가 제공하는 제네릭 뷰(Generic Views) 인데, 그 내용은 다음 글에서 자세히 다뤄볼 예정입니다!


8. 결론 및 다음 포스트 예고

여기까지가 Django의 기본 View 클래스가 어떻게 동작하고, 실전에서 어떻게 활용할 수 있는지에 대한 예시입니다. 핵심 포인트를 정리해보자면:

  1. django.views.View를 상속받고, get() / post() / dispatch() 등 메서드를 오버라이드해 HTTP 요청에 대응한다.

  2. 공통 로직을 상위 클래스에 작성하고, 자식 뷰에서 필요한 부분만 재정의하면 코드 재사용을 극대화할 수 있다.

  3. 템플릿 렌더링, JSON 응답, DB 조회 등 다양한 시나리오에서 간단히 뷰 로직을 작성할 수 있다.

  4. 실전 예시(간단한 게시판)처럼, 기본 View 클래스만으로도 충분히 프로젝트를 만들 수 있다.
    단, CRUD 작업을 간편하게 처리하려면 Django가 제공하는 제네릭 뷰(Generic Views)를 배우는 것이 훨씬 편리하다.

다음 포스트에서는 폼 처리를 좀 더 쉽게 만들어 주는 FormView 클래스를 집중적으로 다뤄보겠습니다. 이를 통해 “사용자 입력을 받아 저장하고, 검증에 실패하면 에러 메시지를 보여주는” 과정을 자동화하는 방법을 터득할 수 있을 것입니다.

CBV 시리즈는 계속됩니다!

  • 3편: “FormView로 폼 처리 쉽게 하기”
  • 4편: “ListView & DetailView 활용법”
  • 5편: “CreateView, UpdateView, DeleteView로 CRUD 구현하기”
  • … (이후 계속)

지난 글 보기


추가로 참고할 자료

이번 글을 통해 기본 View 클래스의 구조와 활용 방법에 대한 궁금증이 어느 정도 해소되었길 바랍니다. 다음 편에서는 FormView를 통해 “폼 작성→검증→에러 처리→성공 시 리다이렉트” 같은 과정을 어떻게 간편하게 처리하는지 배워볼 예정이니, 많은 관심 부탁드립니다!


“Django의 View 클래스는 단순히 HTTP 메서드별로 코드 분리를 해주는 것을 넘어,
대규모 프로젝트에서의 확장성과 유지 보수를 획기적으로 개선할 수 있는 강력한 도구입니다.”

앞으로도 CBV를 깊이 탐구하며 더욱 생산적인 Django 개발을 해 보아요!