Mixin이란?
Mixin은 파이썬의 일반적인 개념인데, 기존의 클래스에 어떤 기능을 더해줄 때 쓰입니다. 우리의 경우 mixin을 활용해서 뷰 클래스에 접근 제어 기능을 더해줬습니다.
우리는 django-braces(링크 추가)라는 패키지를 사용했는데요. django-braces는 사실 django에서 사용할 수 있는 다양한 mixin을 제공합니다. 우리는 그중에서 LoginRequiredMixin과 UserPassesTestMixin을 사용해 봤습니다.
LoginRequiredMixin
LoginRequiredMixin은 로그인이 돼있는 유저만 뷰에 접근할 수 있게 해 줍니다. 로그인 여부를 확인하는 로직이 뷰 로직보다 먼저 실행돼야 하기 때문에 제네릭 뷰 왼쪽에 씁니다.
class MyView(LoginRequiredMixin, CreateView): ...
로그인이 안 돼있으면 로그인 페이지(settings 파일의 LOGIN_URL에 해당하는 URL)로 리디렉트되고, 로그인을 한 후에는 원래 가려고 하던 페이지로 또다시 리디렉트됩니다.
UserPassesTestMixin
(로그인 여부가 아닌) 어떤 특정 조건을 충족하는지 확인하고 싶을 때는 UserPassesTestMixin을 씁니다. UserPassesTestMixin은 우리가 정의하는 커스텀 테스트 (test_func)를 통과하는 유저만 뷰에 접근할 수 있게 해 줍니다.
UserPassesTestMixin을 따로 쓸 수도 있지만, 보통 유저가 로그인이 돼있는지를 먼저 확인하기 때문에 (그래야 그 유저에 대한 여러 가지 조건을 확인할 수 있겠죠?) LoginRequiredMixin과 같이 씁니다. LoginRequiredMixin 오른쪽, 제네릭 뷰 왼쪽에 써 주시면 됩니다.
class MyView(LoginRequiredMixin, UserPassesTestMixin, CreateView): ...
test_func
test_func는 뷰에 접근할 수 있으면 True, 없으면 False를 리턴합니다.
class MyView(LoginRequiredMixin, UserPassesTestMixin, CreateView): def test_func(self, user): if <condition>: return True else: return False
이걸 더 간단히 써 줄 수도 있고요.
class MyView(LoginRequiredMixin, UserPassesTestMixin, CreateView): def test_func(self, user): return <condition>
뷰에 접근하지 못하는 유저들 처리하기
뷰에 접근하지 못하는 유저가 처리되는 방식을 제어하기 위해서는 두 가지 속성을 사용합니다.
- redirect_unauthenticated_users
뷰에 접근하지 못하는 유저들 중, 로그인 돼있는 유저와 로그인이 안 돼있는 유저를 다르게 처리할 것인지를 정하는 속성입니다. 이걸 True로 하면, 로그인이 안 돼있는 유저는 로그인 페이지로 리디렉트되고, 로그인 돼있는 유저는 raise_exception 속성의 값에 따라 처리 방식이 정해집니다. 반대로 이걸 False로 하면, 로그인 돼있는 유저 안 돼있는 유저 모두 raise_exception 속성의 값에 따라 처리됩니다.
- raise_exception
raise_exception에 가장 흔히 사용되는 값은 True와 커스텀 함수인데요. raise_exception을 True로 설정해 주면 유저가 뷰에 접근할 수 없을 경우 403 Forbidden(권한 없음, 금지됨) 오류가 나고, 커스텀 함수로 설정해 주면 그 함수가 그대로 실행됩니다.
def some_func(self, request): # 필요한 로직 수행 return redirect('some_url') class ReviewCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView): ... raise_exception = some_func
커스텀 함수는 self와 request를 파라미터로 받아야 합니다.
Decorator는 어떻게 쓸까요?
접근 제어에 대해 처음으로 배울 때 함수형 뷰에는 decorator를 쓰고, 클래스형 뷰에는 mixin을 쓴다고 했습니다. 우리는 클래스형 뷰를 사용하기 때문에 이번 챕터에서는 AccessMixin을 활용해 봤는데요. 이번 레슨에서는 decorator에 대해 간단히 설명드릴게요. Decorator를 어떻게 사용하는지 참고만 하시면 좋을 것 같습니다.
Decorator도 mixin과 비슷하게 '유저가 뷰에 접근할 수 있는지'를 확인하는 로직을 뷰에 추가해 줍니다. 하지만 사용법은 mixin과 조금 다른데요. 우선 접근 관련 decorator는 django.contrib.auth 앱에서 찾올 수 있습니다. (django-braces는 django 관련 mixin만 제공합니다.)
LoginRequiredMixin과 비슷한 역할을 하는 login_required decorator가 있는데, 아래와 같이 임포트할 수 있습니다.
from django.contrib.auth.decorators import login_required
그리고 decorator를 뷰 위에 달아주면 됩니다.
# views.py from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
그러면 로그인이 안 돼있는데 my_view를 접근하려고 하면 로그인 페이지(settings 파일의 LOGIN_URL에 해당하는 URL)로 리디렉트됩니다.
참고로 decorator는 뷰를 정의할 때가 아닌 URL 패턴을 정의할 때 사용할 수도 있는데요.
# views.py def my_view(request): ... class YourView(View): ... # urls.py from django.contrib.auth.decorators import login_required from .views import my_view, YourView urlpatterns = [ path('my/url/', login_required(my_view), name='my_url'), path('your/url/', login_required(YourView.as_view()), name='your_url'), ]
뷰를 decorator로 감싸주면 됩니다. (YourView는 클래스형 뷰지만 .as_view() 메소드를 적용해 주면 decorator를 사용할 수 있습니다.) 위와 같은 방식도 많이 사용됩니다. 뷰를 직접 정의하는 것이 아니라 뷰를 임포트해서 URL 패턴에 사용한다면 유용할 수 있겠죠?
UserPassesTestMixin과 비슷한 user_passes_test decorator도 있는데요. user_passes_test는 뷰에 접근하지 못하는 유저를 처리하는 로직을 커스터마이즈할 수 없다는 단점이 있습니다. user_passes_test는 뷰에 접근하지 못하는 유저를 어떤 URL로 리디렉트하는 로직밖에 구현할 수 없습니다. (자세한 사용법은 링크를 참고하세요!) 다른 로직이 필요하다면 로직을 함수형 뷰 안에 직접 넣거나 클래스형 뷰를 사용해야겠죠?