TIL_29 | [Django] 장식하면 code가 이뻐지는 매직! Decorator👍

12718 단어 TILdjangoTIL

django를 배우고,
instagram 클론을 진행하다 보니 여러가지 문제에 봉착했다.

바로, 로.그.인.
로그인을 해야만 할 수 있는 문제들을 어떻게 해결 할 수 있을까?

뭘 장식하는 Decorator?!🤔

django에서 말하는 decorator란 어떤 function위에 붙어 작동한다.
(그 모습이 function을 장식하는 것 같다고 해서 decorator...😂)

예시) 프로불편러가 속한 모임

우리가 속해있는 모임에 프로 불편러가 있다고 생각해보자💣💣

이 프로 불편러는 사람들이 물어볼때마다 "알아서 뭐할껀데?"라고 반문한다.
그것을 코드로 짜보면 아래와 같을 것 같다.
(원래 이름을 줄여쓰면 안좋지만 너무 길어져 줄여썼다..)

# 프로불편러의 대답 함수
def SJW(): # Social Justice Warrior = 프로불편러
  return "알아서 뭐할껀데?"

# 물어보는 함수
def question():
  return "이건 이런건가요~?"

근데, 프로불편러가 불편한 대답을 하기 위해선 반드시 그 전에 질문을 받아야 대답을 한다. 즉, 대답을 하기위한 선행과제가 질문이다!

def question():
  def SJW()

이렇게 하면 완벽한 코드가 된 것 같지만,
이 프로불편러가 어떤 질문을 받을때만 대답하는 것이 아니라 질문에 물음표만 포함되어 있어도 불편한 답변을 한다고 생각하면, 그 과정을 다 연결시켜주는 것이 번거로울 수 있고 누락될 수도 있다.

그럴때 필요한 것이 decorator다!
decorator는 항상 어떤 함수를 실행하기 전에 장식처럼 달린 함수를 먼저 실행한다. 아래와 같이 사용이 가능하다.

@question
def SJW():
  return "알아서 뭐할껀데?"

Decorator 구현 방법!

중요한 점은,
아무 함수나 decorator함수가 될수는 없다는 것!

decorator함수가 되기 위해선 중첩 함수(nested function)을 리턴하는 함수만 decorator로 사용된다.

즉, decorator의 기능을 다르게 설명한다면 chain of function.
여러개의 함수가 연속적으로 호출이 자동으로 되게 해준다!
(한개가 가능하다면 여러개도 가능!🤩)

question 함수를 deocorator 함수로 구현하기

def question(func):
    ask = True # 간단화 하기 위해 무조건 True

    def wrapper(*args, **kwargs):
        if ask:
            func() 
        else:
            return

    return wrapper

이렇게 구현하면 된다!

# 실행 모습!
@login
def SJW():
  ...


응...? 이게 무슨 소리야?!?!?!?

진정해... 설명해줄께... 😂

대충 이런식으로 작동이 된다.
(이해가 될런지...👽)

실습!

나는 인스타그램을 구현하는 와중에 로그인하는 기능을 decorator 함수로 만들어 로그인이 필요한 기능들에 사용을 해줬다.
(게시글 등록, 댓글 등록, 팔로우 등등...)

# utils.py
import jwt
import json
import request

from django.http  import JsonResponse
from django.conf  import settings

from user.models import Accounts
from my_settings import SECRET, ALGORITHM

def login_decorator(func):
    def wrapper(self, request, *args, **kwargs):
        try:
            access_token = request.header['Authorization']
            payload      = jwt.decode(access_token, SECRET, algorithms=ALGORITHM)
            user         = Accounts.objects.get(email=payload)
            reqeust.user = user
        
        except jwt.exceptions.DecodeError:
            return JsonResponse({'message':'INVALID_TOKEN'}, status=400)
        
        except Accounts.DoesNotExist:
            return JsonResponse({'message':'INVALID_ACCOUNT'}, status=400)
        return func(self, request, *args, **kwargs)

    return wrapper
# posting app의 PostingView
# 게시글 등록

    @login_decorator
    def post(self, request):
        data = json.loads(request.body)

        try:
            account = Accounts.objects.get(nickname=data['account'])

            Posting.objects.create(
                account   = account,
                image_url = data['image_url'],
                contents  = data['contents']
            )

            return JsonResponse({'message':'SUCCESS'}, status=200)

        except KeyError:
            return JsonResponse({'message':'KEY_ERROR'}, status=400)

Today, Learned🧑🏻‍💻

배운점

  • decorator가 돌아가는 작동 방법 및 사용 방법!
  • wrap의 기능

느낀점

  • 확실히 사용하고 나니까 모든 로직에서 로그인 했는지에 대한 검사가 없어지니 편하고 좋았다! (코드의 반복성 제거!!)👍👍
  • 생각보다 쉽고 편하게 쓰이는 기능인만큼 안쓸 이유가 있을까...?

오늘의 한마디

decorator.. 장식이라는 뜻이지만 넌 없으면 안되는 녀석이야!!🤩

좋은 웹페이지 즐겨찾기