티스토리 뷰

웹사이트를 만들때 페이지마다 허용되는 유저가 같으면 곤란한 일이 많이 발생할 것이다. 예를 들어 관리자 페이지인데 일반 유저가 들어간다던가, 아니면 유료로만 이용이 가능한 페이지인데 로그인도 안한 유저가 사용한다던가 하는 경우 말이다.
이런 일을 방지하기 위해서 Django View 상에서 동작하기 전에 무조건 유저를 검사해주도록 하기 위해 장고 뷰의 기본 메서드중 dispatch와 데코레이터를 활용해보기로 했다.

우선 기본 뷰의 메서드 중에는 dispatch라는 녀석이 있다. 이 녀석이 뭐냐면  request와 기타 argument를 입력받고 HTTP Response를 리턴하는 놈인데, HTTP 메서드를 검사하고 해당 메서드와 일치하는 메서드로 연결을 해주는 녀석이다. GET은 get()으로, POST는 post()로.
따라서 이 녀석이 실행되는 선에서 유저를 검사하면 get이나 post를 실행하기 전에 유저의 권한을 검사하고 접근을 제어할 수 있다!
처음에는 dispatch 내에 유저를 검사하는 로직을 넣으려고 했는데 생각해보니 그럼 매번 뷰에 같은 로직을 넣어야할 것 같아서 데코레이터로 선회했다.

(근데 지금 생각해보니 어차피 내가 만든 프로젝트의 경우엔 뷰 로직이 거기서 거기니 그냥 공통되는 뷰를 만들고 걔를 상속하는 방식으로 만들어도 상관없을것같긴 한데 시도해보지 않았으니 일단 생각으로만 남겨놓자.)


장고에서 데코레이터를 적용하는 방법은 공식문서상에선 두가지를 보여주고 있다. 하나는 URLconf 상에서 적용시키는 것이고 다른 하나는 클래스에서 적용시키는 것인데 나의 경우엔 후자를 사용했다.
공식문서상에서도 후자를 사용하려면 dispatch에 데코레이터를 적용하라고 되어있다.
클래스 내의 메서드는 그냥 standalone한 함수가 아니므로 바로 데코레이터를 적용할 수 없으므로 먼저 데코레이터를 method_decorator로 변경해줘야한다고 한다. (이 부분을 안 읽고 바로 적용해보려다가 안되서 삽질을 좀 했다.) method_decorator는 데코레이터를 함수(function) 데코레이터로 바꿔준다고 한다. method_decorator는 *args, **kwargs를 클래스상의 데코레이팅된 메서드에 파라미터로 통과시켜주고, 만약 해당 메서드가 파라미터를 제대로 못 받을 경우엔 TypeError 예외를 일으킨다.

적용은 클래스 위에 @method_decorator(decorator_name, name=method_name) 이라고 장식해주거나 아니면 그냥 해당 메서드 위에 @method_decorator(decorator_name)이라고 적어주면 된다고 한다.

참고: 장고3.1공식문서

 

그럼 이제 적용한 코드를 보자!

 

먼저 데코레이터다.

from functools import wraps
 
from django.shortcuts import redirect
from django.template.response import TemplateResponse
 
def check_user_perm(func):
    """
    - 로그인 안했으면 로그인화면으로 돌려보낸다.
    - 유저 검사해서 어드민 권한 없으면 권한없다고 알려주는 페이지로 보낸다.
    """
    def wrapper(request, *args, **kwargs):
        user = request.user
        if not user.is_authenticated:
            return redirect('login')
        if not user.is_admin:
            data = {'error': 'NOT_ADMIN', 'message': '관리자 권한이 없어 접근이 불가능합니다.' }
            return TemplateResponse(
                request, 
                '<template_name>',
                 {'data': data}
                 )
        return func(request, *args, **kwargs)
    return wrapper

 

아래는 해당 데코레이터를 적용한 모습이다.

from django.utils.decorators import method_decorator
from myutils.decorators import check_user_perm
from models import my_model
 
 
class MyModelView(ListView):
    model = my_model
    template_name = "my_models/my_model.html"
    paginate_by = 15
 
    @method_decorator(check_user_perm)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

 

 

반응형
댓글