Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
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
Tags
more
Archives
Today
Total
관리 메뉴

Nonamed Develog

[TIL][240807] Django 주차 대비 Decorators 학습 본문

WHAT I LEARN/TIL

[TIL][240807] Django 주차 대비 Decorators 학습

노네임드개발자 2024. 8. 7. 21:58
어떤 문제가 있었나?

사전캠프, 웹개발, 파이썬, 자료구조 알고리즘, CS & SQL까지 달려오고 이제 Django 주차를 앞두고 꼭 정리하고 넘어가야겠다는 부분이 있었는데, 바로 데코레이터 Decorator였다. 팀 프로젝트를 할 때 의미도 모르고 사용했었고, 클래스를 학습할 때 사용하곤 했었지만 이해하고 있다고 생각이 들지 않는 부분이었다. 더불어 우연히 튜터님의 데코레이터의 중요성을 강조하는 연설을 들어서 꼭 정리하고 넘어가야겠단 생각을 했다.


무엇을 시도했나?

데코레이터를 이해하기 위해 classmethod와 staticmethod의 개념부터 차근차근 정리해 보기로 했다. 기본적인 사용법과 함께 차이점 및 용도를 살펴보고, 데코레이터의 원리를 이해하기 위해 간단한 데코레이터를 작성해 봤다. 이를 바탕으로 Django에서 데코레이터가 어떻게 활용되는지, 특히 API 관련 코드에서 사용되는지 알아봤다.


어떻게 해결됐는가?

classmethod와 staticmethod를 정리하고, 데코레이터의 기본 개념과 사용법을 이해하기 위해 데코레이터 코드를 작성해 봤다. 그리고 Django에서 제공하는 기본 데코레이터와 이를 활용한 API 뷰 코드와 FastAPI의 코드를 리뷰해 봤다.


새로 알게 된 점은 무엇인가?

https://realpython.com/primer-on-python-decorators/

classmethod

  • 클래스 메서드는 @classmethod 데코레이터를 사용하여 정의한다.
  • 첫 번째 인수로 클래스를 받으며, 클래스 속성과 비슷하게, 클래스 자체에 대한 동작을 넣고자 할 때 사용된다.
  • 클래스 상태나 클래스 변수를 변경하거나 접근할 때 사용된다.
class MyClass:
    class_variable = 0

    def __init__(self):
        MyClass.class_variable += 1

    @classmethod
    def get_class_variable(cls):
        return cls.class_variable

# 클래스 인스턴스 생성
instance1 = MyClass()
instance2 = MyClass()

# 클래스 메서드 호출
print(MyClass.get_class_variable())  # 2

 

staticmethod

  • staticmethod는 @staricmethod 데코레이터를 사용하여 정의한다.
  • 인스턴스나 클래스의 첫 번째 인수로 받지 않고 클래스와 연결되어 각 객체와 독립적으로 작동한다.
class MyClass:

    @staticmethod
    def add_numbers(a, b):
        return a + b

# 정적 메서드 호출
print(MyClass.add_numbers(3, 5))  # 8

 

데코레이터 Decorator

파이썬의 데코레이터는 기본적으로 다른 함수를 수정하지 않고 그 기능을 확장하거나 변경할 수 있도록 해주는 고급 기능이다. 

  • 데코레이터는 함수를 다른 함수의 인자로써 받아, 어떤 처리를 한 후에 그 함수를 반환하거나 다른 함수를 반환한다.
  • 이런 방식으로 기존 함수의 동작을 변경하거나 확장하는 데 사용한다.
  • 함수 또는 메서드를 다른 함수로 감싸서 추가적인 기능을 부여하는 방법이다.
# 일반적인 방법
def my_decorator(func):
    def wrapper():
        print("함수 호출 전")
        func()
        print("함수 호출 후")
    return wrapper

def hello():
    print("안녕하세요")

# hello()
say_hello = my_decorator(hello)
say_hello()
"""
함수 호출 전
안녕하세요
함수 호출 후
"""

 

위 코드를 읽어보면, hello()를 실행한다면 "안녕하세요"라는 출력만 나올 것이다. 하지만 hello() 함수를 my_decorator() 함수에 넣어 호출 전 후로 메시지를 넣고 싶다면 say_hello라는 변수에 my_decorator(hello)를 대입하여 say_hello()를 실행하면 출력이 될 것이다.

 

하지만 위와 같은 방법 대신 아래와 같이 데코레이터를 이용하면 손쉽게 코드를 작성할 수 있다.

def my_decorator(func):
    def wrapper():
        print("함수 호출 전")
        func()
        print("함수 호출 후")
    return wrapper

@my_decorator
def hello():
    print("안녕하세요")

hello()
"""
함수 호출 전
안녕하세요
함수 호출 후
"""

 

또 다른 예시 코드는 아래와 같다.

# 양수인지 확인하는 데코레이터
def validate_positive_number(func):
    def wrapper(arg):
        if arg < 0:
            raise ValueError("인자는 0보다 커야(positive) 합니다.")
        return func(arg)

    return wrapper


# integer인지 확인하는 데코레이터
def validate_integer(func):
    def wrapper(arg):
        if not isinstance(arg, int):
            raise ValueError("인자는 integer여야 합니다.")
        return func(arg)

    return wrapper

@validate_integer
@validate_positive_number
def process_numbers(num):
    print(f"숫자 검증: {num}")


# 함수 호출
process_numbers(2)  # 2

 

위의 코드는 기본 예시 코드와 구조적으로 유사하지만 다른 점이 하나 있다. 바로 데코레이터를 2개 사용했다는 것인데, 필요한 데코레이터만 불러와서 사용이 가능하고 위의 코드처럼 둘 다 사용할 수 있다. 따라서 위의 코드는 양수이면서 정수인 조건을 통과해야 출력이 될 것이다.

 

Django에서의 데코레이터 활용

  • 로그인 필요 데코레이터: 사용자가 로그인해야 접근할 수 있는 뷰를 정의할 때 사용한다.
from django.contrib.auth.decorators import login_required
from django.shortcuts import render

@login_required
def my_view(request):
    return render(request, 'my_template.html')
  • API 뷰에서의 데코레이터: Django REST framework에서 특정 권한이 필요한 API 뷰를 정의할 때 사용한다.
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_api_view(request):
    data = {"message": "Hello, authenticated user!"}
    return Response(data)
  • 커스텀 데코레이터: 특정 로직을 재사용하기 위해 커스텀 데코레이터를 사용할 수 있다.
from functools import wraps
from django.http import HttpResponse

def log_request(view_func):
    @wraps(view_func)
    def _wrapped_view(request, *args, **kwargs):
        print(f"Request Method: {request.method}")
        print(f"Request Path: {request.path}")
        return view_func(request, *args, **kwargs)
    return _wrapped_view

@log_request
def my_view(request):
    return HttpResponse("Hello, world!")

무엇을 느꼈고 내일은 무엇을 할까?

어떻게 활용해야 하는지는 아마 Django 주차를 진행하면서 알 수 있을 거 같다. 아직 할게 많아 시간 활용을 효율적이게 할 수 있도록 루틴을 꼼꼼하게 만들어야겠다.