유저 기능이 구현된 프로젝트에서 로그인이 된 유저가 글을 작성할 때, 그 유저에 대한 정보가 글에 함께 저장되도록 합시다.
그리고 거기에 추가적으로 '로그인을 해야 글을 작성할 수 있고', '내가 작성한 글만 수정, 삭제를 할 수 있도록' 하는 기능을 추가해봅시다.
유저가 서버로 보내는 request 처리 과정에서 view에 Mixin이라는 것을 설치하고 이 Mixin에 접근 제어와 관련된 로직들을 추가하면, 로그인이 되어 있지 않은 경우 다른 페이지로 redirect하거나 forbidden response를 돌려주는 등의 일을 수행할 수 있습니다. Mixin은 보통 기존의 클래스에 어떤 기능을 더해줄 때 쓰이는 개념인데, 따라서 뷰 함수가 아닌 뷰 클래스에서만 적용될 수 있습니다.
이러한 접근 제어와 관련된 Mixin을 제공하는 패키지 중 django-braces 라는 것을 사용해 보도록 합시다.
해당 패키지는 터미널에서 다운 받으면 됩니다.
pip install django-braces
유저 필드 추가
우선 글 작성 모델에 ForeignKey를 이용하여 유저 필드를 추가합니다. 글 작성 모델의 이름은 Post, 유저 모델은 User라고 가정합시다.
# models.py
class User(AbstractUser):
nickname = models.CharField(max_length=15, unique=True, null=True)
...
class Post(models.Model):
...
author = models.ForeignKey(User, on_delete=models.CASCADE)
on_delete=models.CASCADE 는 참조하는 오브젝트가 삭제되면 기존 오브젝트도 자동으로 삭제해 주는 기능을 합니다. 유저 데이터가 삭제되면 해당 유저가 쓴 글들도 모두 자동으로 삭제되는 것이죠.
포스트의 유저 인스턴스에 접근하거나 유저 조건으로 포스트들을 필터링하는 방법은 다음과 같이 하면 되겠죠?
data = Post.objects.all().first()
data.author # 유저 인스턴스
data.author.nickname # 유저의 닉네임 접근
# 필터링
Post.objects.filter(author__id=1)
Post.objects.filter(author__nickname='KM')
로그인 접근 제어
로그인이 된 유저만 글 작성 페이지로 접근할 수 있고, 로그아웃된 상태에서 글 작성 페이지에 접근하려 한다면 로그인 페이지로 리디렉트 되도록 해봅시다.
우선 장고에게 로그인을 위한 url이 뭔지 알려줌으로써 접근 제어에 걸리면 로그인 페이지로 이동하게끔 셋팅해 줍시다.
# settings.py
LOGIN_URL = "account_login"
그리고 글 작성 페이지를 랜더링하는 뷰 클래스가 braces패키지의 LoginRequiredMixin를 상속받도록 합니다.
# views.py
from braces.views import LoginRequiredMixin
class PostCreateView(LoginRequiredMixin, CreateView):
...
LoginRequiredMixin은 로그인이 되어 있는 유저만 뷰에 접근할 수 있게 해 줍니다. 로그인이 안 되어있으면 로그인 페이지로 리디렉트되고, 로그인을 하면 원래 가려고 하던 페이지로 가게 됩니다.
이메일 인증 접근 제어
이런 특정 조건을 가지는 접근 제어를 구현하기 위해선 UserPassesTestMixin을 사용하면 됩니다.
UserPassesTestMixin은 우리가 오버라이딩 할 test_func 함수를 통과하는 유저만 뷰에 접근할 수 있게 해 줍니다.
test_func는 뷰에 접근할 수 있으면 True, 없으면 False를 리턴합니다.
# views.py
from braces.views import LoginRequiredMixin, UserPassesTestMixin
from allauth.account.models import EmailAddress
from allauth.account.utils import send_email_confirmation
def confirmation_required_redirect(self, request):
send_email_confirmation(request, request.user)
return redirect("메일 인증 요구하는 페이지의 url name")
class PostCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView):
...
redirect_unauthenticated_users = True
raise_exception = confirmation_required_redirect
...
def test_func(self, user):
return EmailAddress.objects.filter(user=user, verified=True).exists()
뷰에 접근하지 못하는 유저가 처리되는 방식을 제어하기 위해서는 두 가지 속성을 사용합니다.
- redirect_unauthenticated_users : 뷰에 접근하지 못하는 유저들 중, 로그인 되어있는 유저와 로그인이 되어 있지 않은 유저를 다르게 처리할 것인지를 정하는 속성입니다. 이걸 True로 하면, 로그인이 안돼있는 유저는 로그인 페이지로 리디렉트되고, 로그인 돼있는 유저는 raise_exception 속성의 값에 따라 처리 방식이 정해집니다. 반대로 이걸 False로 하면, 로그인 상태의 유저, 로그아웃 상태의 유저 모두 raise_exception 속성의 값에 따라 처리됩니다.
- raise_exception : raise_exception을 True로 설정해 주면 유저가 뷰에 접근할 수 없을 경우 403 Forbidden(권한 없음, 금지됨) 오류가 나고, 커스텀 함수로 설정해 주면 그 함수가 그대로 실행됩니다. 커스텀 함수는 self와 request를 파라미터로 받아야 합니다.
위 뷰 클래스의 경우에는, 로그인이 되어있지 않다면 글 작성 페이지 접근 시 로그인 페이지로 가게 되고, 로그인이 되어있다 하더라도 이메일 인증을 받지 않은 상태라면 이메일 인증을 요구하는 페이지로 리디렉트 되겠지요.
본인이 쓴 글만 수정, 삭제
위와 같은 방법으로 본인이 작성한 글만 수정, 삭제할 수 있도록 해봅시다.
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
...
raise_exception = True
def test_func(self, user):
post = self.get_object()
return post.author == user
raise_exception을 True로 설정해 주었으니, 유저가 뷰에 접근할 수 없는 경우 403에러가 발생하겠지요. redirect_unauthenticated_users의 기본값은 False이니 따로 설정할 필요는 없습니다.
글 삭제를 담당하는 클래스 뷰도 똑같이 따라서 추가해주면 됩니다.
'백엔드 > Django' 카테고리의 다른 글
[Django] allauth 패키지 (0) | 2021.10.25 |
---|---|
[Django] 제네릭 뷰 (0) | 2021.10.11 |
[Django] 페이지네이션(Pagination) 기능 (0) | 2021.10.10 |
[Django] 폼(Form)에 관하여 (0) | 2021.10.05 |
[Django] MVT 구조 (0) | 2021.10.02 |