본문 바로가기

BackEnd/Python

decorator(데코레이터) 사용법

반응형

Decorator ::


함수를 Wrapping 해서 기능을 재사용할 수 있게 해주는 기법입니다.
실제로 어떻게 개발에 사용되고, 왜 필요한지에 대해 알아보도록 하겠습니다.

def test_func():
  if user is None:                   # login 여부를 확인
    return redirect('/login')    # login 되어있지 않은 경우 login page로 redirect
  print('Do something')          # login이 되어 있는 경우 실행


def test_func2():
  if user is None:
    return redirect('/login')
  print('Do something2')

..... * 100                      # 이런 함수가 100여개가 있는 경우를 가정

test_func 함수는 실행될 때, 우선적으로 login 여부를 확인(if문 실행)합니다. login 되어 있지 않은 경우 login page로 redirect 시키면서 실제 실행되어야 하는 기능(print('Do something'))이 실행되지 않도록 합니다.

위와 같은 예시는 쇼핑몰 사이트에서 흔하게 찾아볼 수 있습니다. 예를 들어 상품을 장바구니에 담을 때, 담긴 장바구니를 확인할 때, 장바구니에 있는 물품을 구매할 때 등과 같이 개인의 정보를 확인하려고 하는 경우 login 여부를 확인합니다.

따라서 굉장히 많은 경우 로그인을 확인하는 로직(if문)이 사용됩니다. 기능이 100개가 있는 경우에 100번의 login 로직을 사용해야하기 때문에 코드가 길어지고 복잡해집니다. 위와 같은 예시는 간단한 예시이기 때문에 느껴지지 않으실 수 있지만 다른 로직이 10줄 이상만 되더라도 굉장히 복잡해보입니다.

따라서 우리는 이런 길고 복잡한 코드를 피하기 위해서 decorator(데코레이터)를 사용합니다.

def login_required(func):   # 함수를 인자 값으로 받음(밑을 예로 들면 test_func()을 인자 값으로 받음)
    def wrap():                     # wrap() 을 실행, 만약 user가 로그인을 안했으면 로그인 페이지로 돌려줌
        if user is None:                 
            return redirect('/login')
        return func()                     # 유저가 로그인 상태면 인자값으로 받았던 함수를 실행함
    return wrap                           # 그 결과 값을 출력함


@login_required
def test_func():
    print('Do something')

@login_required
def test_func2():
    print('Do something2')

위의 예시가 decorator를 사용한 예시입니다.

우선, login 기능을 만든 후에 각 기능들에 login 기능을 한 줄로 붙여주는 것입니다. 이렇게 할 경우 무엇이 필요한지 바로바로 알게 되고, 길었던 코드도 짧아지니 관리하기가 더욱 수월해집니다.


Django에 적용


1. decorator.py 파일을 만들어 사용하고자 하는 decorator를 만듭니다.
2. decorator를 사용하고자 하는 view.py에 "from django.utils.decorators import metod_decorator"를 import합니다.
3. 1번에서 만든 decorator.py 을 import 합니다.

먼저 class view를 실행할 때는 url을 받아서 view 안에 있는 dispatch() 라는 함수를 가장 먼저 실행합니다. 따라서 login_required 라는 decorator를 실행하기 위해서는 이 dispatch() 함수에 붙여주어야 합니다.

class example_view(CreateView):

    @login_required
    def dispatch(self,):

 

하지만 method_decorator를 사용하면 class 단에서 decorator를 사용할 수 있습니다.

@method_decorator(login_required, name='dispatch')
class OrderList(ListView):


dispatch()의 인자값을 wrap()에서 똑같이 받고 똑같이 전달해주어야 오류가 생기지 않습니다.

즉, dispatch(request, *args, **kwargs) 라고 한다면 decorator에서도
    wrap(request, *args, **kwargs)
    return func(request, *args, **kwargs) 로 받아야합니다.

login 인증을 위한 decorator 예시입니다.

import django.shortcuts import redirect

def login_required(function):
    def wrap(request, *args, **kwargs):
        user = request.session.get('user')
        if user is None or not user:
            return redirect('/login')
        return function(request, *args, **kwargs)
    return wrap

이번에는 admin required decorator 예시입니다.

from .models import TESTuser

def admin_required(function):
    def wrap(request, *args, **kwargs):
        user = request.session.get('user')
        if user is None or not user:
            return redirect('/login')
        user = TESTuser.objects.get(email=user)
        # TESTuser 모델에 level 컬럼을 하나 더 만들어야함
        if user.level != 'admin':
            return redirect('/')
        return function(request, *args, **kwargs)
    return wrap
반응형