티스토리 뷰

Django

Django #5 장고의 Views

joyHong 2022. 3. 15. 23:32

지금까지의 글은 주로 장고의 뷰 계층을 다루었다. 엄밀히 이야기하자면 함수형 뷰 라는 것을 이용하여 간단한 웹페이지를 구성해보았다. 이번 글에서는 지금까지 언급하지 않았던 뷰 계층에 대한 조금 심층적인 이야기를 하려고 한다.

 

장고의 뷰는 크게 함수형 뷰와 클래스형 뷰로 나뉜다.

처음에 장고는 함수형 뷰만 제공했는데 동일한 코드를 사용하지 않도록 템플릿화하여 클래스형 뷰를 추가하였다. 공식 문서에서 밝힌 바와 같이 클래스형 뷰는 기존의 함수형 뷰를 대체하는 것이 아니다. 주로 사용하는 코드를 간결하게 만들어 놓은 것이라 보면 좋을 것 같다. 그렇기 때문에 사용은 편하지만 사용을 위해서는 추가적인 학습이 필요하다. 당연한 trade-off 라고 보여진다.

참조: https://medium.com/@ksarthak4ever/django-class-based-views-vs-function-based-view-e74b47b2e41b

위의 그림은 어떤 뷰를 써야하는지에 대해 판단할 수 있는 글에서 참조한 내용이다.  핵심은  뻔한 이야기이지만 내가 사용하고자 하는 기능에 잘 맞는 뷰를 선택해야 사용하면 된다는 것이다. 그리고 더 뻔한 이야기겠지만 공식 문서와 실제 사용 예제를 보면서 어떠한 종류가 있는지 알아야 내가 원하는 기능에 맞는 뷰를 선택할 수 있기 때문에 조금의 노력(?) 혹은 많은 노력이 필요하다.

그래서 이 글에서는 간단한 차이에 대해서 조금 언급하려고 한다.

클래스형 뷰는 이름에서 밝힌 바와 같이 파이썬 클래스이다. 지금까지 사용했던 함수형 뷰는 함수를 생성하여 사용하였던 점을 기억해보면 클래스를 만들 것인가 함수를 만들 것인가에서부터 차이가 있다. 클래스를 사용한다는 것은 재사용, 확장이 가능하다는 이점을 가지고 있다.  또한 클래스형 뷰는 미리 구성된 기능이 있는 다양한 템플릿들이 제공된다.  장고를 통해 웹 애플리케이션을 생성함에 있어 일반적인 요구 사항들에 대하여 미리 구성된 기능을 제공하기 때문에 이를 “제네릭 뷰”라고 부르는 경우도 많다.

 

시작에 앞서 v4 애플리케이션을 생성하고, v1과 v2에서 만들었던 코드를 가져와 비교하면서 v4 코드를 만들어보겠다.

혹시나 애플리케이션 생성하는 방법이 아직 서투르고 기억이 나지 않는다면 이전 글을 참조해보면 도움이 될 것이다.

실제 코드를 작성할 때 체감하는 함수형 뷰와 클래스형 뷰의 차이는 urls.py와 views.py를 생성할 때 차이가 난다.

함수형 뷰와 클래스형 뷰 둘 다 urls.py에서 호출하는 대상이 views.py에 있는 함수라는 것은 일단 동일하다. 단지 함수형 뷰는 지금까지 views.py에 바로 생성하였던 함수를 호출 하는 것이고, 클래스형 뷰는 views.py에 클래스를 생성하고 그 클래스에 포함되어 있는 as_view() 함수를 호출한다. as_view()는 사용자가 만들지 않아도 된다. 왜냐하면 앞에서 설명했듯이 템플릿화하여 제공하기 때문이다.

예제를 보면서 차이를 살펴 보면 더 확실히 구분이 된다.

views.py

#v1에서 작성한 함수형 뷰
def index(request):
    response = HttpResponse()
    response.write("<h1>Welcome</h1>")
    response.write("<p>This is my first Django. </p>")
    return response

#v4에서 작성할 클래스형 뷰
class BaseView(View):
    def get(self, request, *args, **kwargs):
        response = HttpResponse()
        response.write("<h1>Welcome</h1>")
        response.write("<p>This is my first Django. </p>")
        return response

v4 애플리케이션을 생성하였다면 views.py 위의 코드와 같이 생성하도록 한다.

BaseView라는 클래스를 생성하였는데 이 클래스는 View 라는 클래스를 상속받도록 하였다.  그리고 def get() 함수를 하나 생성하고 결과로 보여줄 내용을 작성하였다.

 

이 다음에는 urls.py에 아래와 같이 작성하도록 한다.

urls.py

app_name = 'v4'
urlpatterns = [
    # path('', index, name='index'),
    path('', BaseView.as_view()),
]
 

주석으로 처리한 부분은 v1에서 생성했던 것이다. 우선 차이를 보면 http://127.0.0.1:8000/v4 로 요청이 들어오면 이를 처리하는 view를 지정하는 것은 같으나 v1에서는 index 라는 함수를 지정했다면, v4에서는 BaseView라는 클래스의 as_view() 함수를 지정하고 있다.

우리는 views.py를 만들 때 as_view() 함수를 생성하지 않았지만 클래스형 뷰에 미리 구성된 기능으로 제공하고 있기 때문에 특별히 작성하지 않아도 되는 것이다.

 

그럼 실제로 동작이 되는지 확인을 해보기 위해 서버 구동 후 http://127.0.0.1:8000/v4/ 로 접속하면 아래와 같이 보이게 된다.

아주 아주 간단한 클래스형 뷰를 구성해 보았다.

참고로 함수형 뷰를 만들 때 간단한 예제를 사용했었는데 우리는 HTTP 요청 메소드를 신경쓰지 않고 코드를 작성했었다.

그러나 실제로는 GET 방식이냐 POST 방식이냐에 따라 다른 처리를 할 필요가 생기게 된다.

그럴 때는 request.method 를 통해 HTTP 요청 메소드를 구별하여 코드를 작성해야 한다.

아래는 그러한 예시이다.

def index(request):
    if request.method == 'GET':
        response = HttpResponse()
        response.write("<h1>Welcome</h1>")
        response.write("<p>This is my first Django. </p>")
        return response

 

다시 클래스형 뷰로 돌아와 BaseView라는 클래스가 View라는 클래스를 상속받았다고 언급했었다. 그리고 일반적인 요구사항을 처리하는 기능을 제공하기 때문에 “제네릭 뷰”라고도 한다고 위에서 설명하였다.

클래스형 뷰와 제네릭 뷰에 대해서 약간의 혼동이 있을 수 있을 것 같아 이 부분을 좀 더 언급하자면

Django의  제네릭 뷰는 웹 애플리케이션에서 매우 일반적인 작업을 수행하는 뷰 기능이다. 제네릭 뷰 역시 계속 언급하고 있는 클래스형 뷰이다.

제네릭 뷰는 클래스형 뷰이기 때문에 미리 생성되어 있는 여러가지 제네릭 뷰들 중에서 적절한 제네릭 뷰를 상속 받아 사용을 하면 된다. 따라서 이렇게 사용하는 뷰가 제네릭 뷰를 상속받은 클래스형 뷰인 것이다.

그럼 위에서 생성한 BaseView는 View라는 제네릭 뷰를 상속받은 클래스형 뷰라고 할 수 있다.

 

그렇다면 제네릭 뷰는 어떠한 것이 있는가? 라는 질문이 당연히 뒤 따르게 된다.

크게는 Base views, Generic display views, Generic editing views, Generic date views 4가지로 나뉜디고 한다.

참조 : https://docs.djangoproject.com/en/4.0/ref/class-based-views/

 

그 중에서 Base Views는 장고의 View를 생성하는데 필요한 다양한 기능들을 제공한다. 총 3가지로  View, TemplateView, RedirectView가 존재한다.

이 중에서도 View는 가장 기본이 되는 최상위 뷰가 되고, TemplateView는 주어진 템플릿명을 렌더링하는 뷰이다.

그럼 이 두가지를 사용하여 간단히 구성을 해보겠다.

 

View는 처음 예시에서 사용해 보았는데 이번에는 template_name을 통해 템플릿을 지정해 보도록 하겠다.

 

views.py

class IndexView(View):
    template_name = "v2/index.html"

    def get(self, request, *args, **kwargs):
        return render(request, self.template_name)

 

urls.py

app_name = 'v4'
urlpatterns = [
    # path('', index, name='index'),
    path('', BaseView.as_view()),
    path('index', IndexView.as_view(), name='index'),
]

 

http://127.0.0.1:8000/v4/index 로 요청이 들어오면 v2 애플리케이션에서 생성하였던 index.html을 렌더링 하도록 구성하였다.

 

이번에는 TemplateView를 사용해 보겠다. 클래스를 생성하고 TemplateView 제네릭 뷰를 상속 받으면 간단하게 사용이 가능하다.

 

views.py

class HtmlView(TemplateView):
    template_name = "v3/index.html"

 

urls.py

urlpatterns = [
    # path('', index, name='index'),
    path('', BaseView.as_view()),
    path('index', IndexView.as_view(), name='index'),
    path('html', HtmlView.as_view(), name='html'),
]

 

여기서는 HtmlView 클래스를 생성 후 template_name으로 v3 앱에서 만들었던 index.html 을 지정하였다. 다른 코드 필요없이 요청이 들어오면 template_name 에 매칭되는 html 을 렌더링해준다.

http://127.0.0.1:8000/v4/html 로 접근하염 아래와 같이 v3에서 만들었던 내용이 나오게 된다.

이와 같이 미리 구성한 기능을 제공하기 때문에 간단한 예제이긴 하지만 코드의 양이 줄어드는 것을 확인할 수 있다.

 

마지막으로 HTTP 메소드 방식에 따라 다른 동작을 하도록 하려고 한다. TemplateView 클래스를 상속받는 NameView를 생성하겠다.

POST를 사용하기 위해 CSRF를 사용하지 않도록 추가도 하겠다.

 

views.py

@method_decorator(csrf_exempt, name='dispatch')
class NameView(TemplateView):
    template_name = "v2/name.html"

    def get_context_data(self, **kwargs):
        context = super(NameView, self).get_context_data(**kwargs)
        return context

    def get(self, request, *args, **kwargs):
        """
        GET 방식으로 요청이 들어오면 URL에 입력된 값을 context에 담아 template_name에 해당되는 html을 렌더링하도록 처리한다.
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)

    def post(self, request, **kwargs):
        """
        POST 방식으로 들어오면 body의 내용을 context에 담아 template_name에 해당되는 html을 렌더링하도록 처리한다.
        :param request:
        :param kwargs:
        :return:
        """
        context = self.get_context_data(**kwargs)
        body = json.loads(request.body.decode('utf8'))
        context['name'] = body['keyword']
        return self.render_to_response(context)

 

urls.py

app_name = 'v4'
urlpatterns = [
    # path('', index, name='index'),
    path('', BaseView.as_view()),
    path('index', IndexView.as_view(), name='index'),
    path('html', HtmlView.as_view(), name='html'),
    path('name/<str:name>', NameView.as_view(), name='name'),
]

 

다른 것들에 비해 코드가 좀 긴 편이지만 차근히 살펴보면 복잡한 것이 없다.

먼저 urls.py에서는 URL패턴이 name/문자열 인 경우 NameView 에서 처리하도록 지정하였다.

다음으로 views.py에서 NameView 클래스를 생성하고 이 클래스는 TemplateView 제네릭 뷰를 상속받도록 하였다.

template_name은 v2 앱에서 생성하였던 name.html으로 지정하였다.

그리고 HTTP 메소드 방식에 따라 get() 과 post()를 각각 다르게 처리하도록 하였다.

get() 방식으로 요청이 들어오면 단순히 URL 패턴에 입력된 값을 컨텍스트에 담아 지정한 템플릿을 통해 렌더링하도록 하였다.

post() 방식으로 요청이 들어오면 get() 방식의 처리와 비슷하지만 body의 값이 있는 경우에는 body의 값을 컨텍스트에 담아 렌더링하도록 하였다.

body에 들어오는 값의 형태는 아래와 같다.

{"keyword”:”사용자 입력 문자열”}

 

get() 방식의 동작을 확인하기 위해서 웹브라우저에서 아래의 URL로 접속하면 된다.

http://127.0.0.1:8000/v4/name/IU

post() 방식의 동작을 확인하기 위해서 각자가 선호하는 방식을 채택하여 테스트 하면 되는데 여기서는 간단히 터미널에서 curl로 확인해보면 아래와 같이 동작되는 것을 확인할 수 있다.

curl -X POST "http://127.0.0.1:8000/v4/name/a" -H  "accept: application/json" -H  "Content-Type: application/json" -d "{\"keyword\":\"joy\"}"

결과

 

이상으로 클래스형 뷰를 활용하여 views.py를 생성하는 몇가지 방법들을 살펴보았다.

 

이 글에서 사용하느 코드 전체는 아래의 주소에서 확인이 가능하다.

https://github.com/joyhong85/Tutorial.git

 

coming soon…

'Django' 카테고리의 다른 글

Django #6 장고의 Form  (0) 2022.03.20
Django #4 홈페이지 만들기 feat.부트스트랩  (0) 2022.03.15
Django #3 Template 사용하기  (0) 2022.03.15
Django #2 간단한 웹페이지 만들기  (0) 2022.03.15
Django #1 시작하기  (0) 2022.03.15
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함