Django 커스텀 데코레이터의 동작 원리와 작성법
데코레이터는 Django에서 뷰 함수나 클래스에 공통적인 로직을 쉽게 추가할 수 있도록 해주는 강력한 도구입니다. 이 글에서는 데코레이터의 작동 원리와 작성 방법을 단계별로 설명하여, 여러분이 직접 커스텀 데코레이터를 작성할 수 있도록 돕겠습니다.
1. 데코레이터란 무엇인가?
데코레이터는 Python의 고차 함수(Higher-order function)입니다. 즉, 하나의 함수를 인수로 받아 내부에서 새로운 함수를 반환하는 함수입니다.
- 데코레이터의 역할:
- 함수를 감싸 특정 기능을 추가하거나 수정할 수 있습니다.
- 코드를 재사용 가능하고 간결하게 만들어줍니다.
2. 데코레이터의 기본 동작 원리
데코레이터의 작동 원리를 이해하기 위해 간단한 예제를 살펴봅시다.
def simple_decorator(func):
def wrapper():
print("Before the function runs")
func()
print("After the function runs")
return wrapper
@simple_decorator
def say_hello():
print("Hello!")
say_hello()
출력 결과:
Before the function runs
Hello!
After the function runs
설명:
@simple_decorator
는say_hello
함수를simple_decorator
함수에 전달합니다.simple_decorator
는 내부에서 새로운wrapper
함수를 생성해 반환합니다.say_hello
가 호출될 때, 원래 함수 대신wrapper
가 실행됩니다.wrapper
함수 내부에서 추가 동작(프린트 메시지)을 실행하고, 원래 함수를 호출합니다.
3. Django에서 커스텀 데코레이터를 작성하는 원리
Django에서 데코레이터를 작성하려면 뷰 함수의 인자인 request
객체를 다룰 줄 알아야 합니다. 이를 기반으로 특정 조건에 따라 HTTP 응답을 조작하거나 로직을 실행할 수 있습니다.
4. 커스텀 데코레이터 작성의 단계별 구조
다음은 Django에서 커스텀 데코레이터를 작성하는 기본 구조입니다:
functools.wraps
사용: 원래 함수의 이름, 문서화 문자열 등 메타데이터를 유지하기 위해@wraps
를 사용합니다.request
를 기반으로 로직 추가: 요청 객체를 분석하거나 검증하는 로직을 작성합니다.- 원래 함수를 호출하거나, 대체 응답 반환: 특정 조건에 따라 원래 함수 호출을 차단하고 다른 응답을 반환할 수 있습니다.
다음은 예제 코드입니다:
from functools import wraps
from django.http import HttpResponseForbidden
def custom_decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
# 요청 객체에서 사용자 정보를 확인
if not request.user.is_authenticated:
return HttpResponseForbidden("You must be logged in to access this page.")
# 원래 뷰 함수를 호출
return view_func(request, *args, **kwargs)
return _wrapped_view
5. 데코레이터에 매개변수를 추가하려면?
데코레이터가 매개변수를 받아야 하는 경우에는 함수를 두 번 감싸는 구조를 사용해야 합니다. 그 이유는 첫 번째 함수는 데코레이터의 매개변수를 처리하기 위해 사용되며, 실제 뷰 함수를 감싸는 역할은 두 번째 함수에서 이루어지기 때문입니다.
기본 구조:
def decorator_with_params(param1, param2):
def actual_decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
# 매개변수 param1, param2를 활용한 로직
return view_func(request, *args, **kwargs)
return _wrapped_view
return actual_decorator
설명:
- 첫 번째 함수
decorator_with_params
는 데코레이터의 매개변수(param1
,param2
)를 처리합니다. - 내부에서 실제 뷰 함수를 감싸는 데코레이터 함수(
actual_decorator
)를 반환합니다. actual_decorator
내부에서 원래 뷰 함수를 호출하거나 추가 로직을 실행합니다.
6. 실전 예제: 시간대 제한 데코레이터
다음은 특정 시간대에만 접근 가능한 데코레이터 예제입니다. 이 데코레이터는 매개변수로 허용 시간대를 받아 처리합니다.
from datetime import datetime
from django.http import HttpResponseForbidden
def time_restricted_access(start_hour, end_hour):
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
current_hour = datetime.now().hour
if not (start_hour <= current_hour < end_hour):
return HttpResponseForbidden("This page is accessible only during certain hours.")
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
@time_restricted_access(9, 17) # 오전 9시부터 오후 5시까지 허용
def working_hours_view(request):
return render(request, 'working_hours.html')
동작 원리 설명:
time_restricted_access
는 매개변수(start_hour
,end_hour
)를 처리하는 첫 번째 함수입니다.- 첫 번째 함수는 내부에서 뷰를 감싸는 데코레이터 함수를 반환합니다.
- 데코레이터 함수는
request
를 받아서 현재 시간(datetime.now().hour
)을 확인하고, 시간 조건에 따라:- 조건이 만족하면 뷰를 호출하고 결과를 반환합니다.
- 조건이 불만족하면
HttpResponseForbidden
으로 차단합니다.
왜 함수를 두 번 감싸야 할까?
함수를 두 번 감싸는 이유는 매개변수 처리와 뷰 함수 처리의 역할을 분리하기 위해서입니다.
- 첫 번째 함수: 매개변수(
start_hour
,end_hour
)를 받아 내부에서 데코레이터를 생성합니다. - 두 번째 함수: 생성된 데코레이터가 뷰 함수를 감싸고, 요청에 대해 동작합니다.
이 구조는 데코레이터를 유연하고 재사용 가능하게 만들어줍니다.
7. 클래스 기반 뷰(CBV)에서 사용하기
클래스 기반 뷰에는 method_decorator
를 활용해야 합니다.
from django.utils.decorators import method_decorator
from django.views import View
@method_decorator(time_restricted_access(9, 17), name='dispatch')
class MyView(View):
def get(self, request):
return render(request, 'my_template.html')
8. 결론: 커스텀 데코레이터 작성 시 유의점
- 단순함 유지: 데코레이터 로직은 간단하고 명확해야 합니다.
- 재사용성 고려: 특정 로직이 여러 뷰에서 사용된다면 데코레이터로 추출하세요.
- 매개변수 처리: 매개변수가 필요한 경우 함수를 두 번 감싸는 구조를 사용하세요.
- 메타데이터 보존:
@wraps
를 사용해 원래 함수의 메타데이터를 유지하세요.
마무리
이제 여러분은 Django에서 커스텀 데코레이터의 내부 동작 원리를 이해하고, 매개변수를 받아 동작하는 데코레이터를 작성할 수 있게 되었습니다. 이 글을 기반으로 프로젝트에 맞는 데코레이터를 작성해보세요!
Add a New Comment