django, drf

drf dj_rest_auth 로그인 커스텀, non_field_error 해결방법

땅콩콩 2022. 11. 17. 13:24

요즘 학교에서 알파프로젝트로 우리 학교 학생들을 위한 정보공유서비스를 만드는 중이다.

거기서 유저관련 기능을 구현하면서 알게된 dj_rest_auth로 로그인방식을 편하게 커스텀하는 방법을 공유하려고한다.

 

먼저 처음 로그인 오류를 만나게 된건 회원단계에서 이메일 인증을 하도록 구현하면서 시작됐다.

회원가입 이메일 인증 코드들을 살펴보면서 설정을 추가하다가 다음 설정을 추가하게 된다.

ACCOUNT_AUTHENTICATION_METHOD = 'email'

이메일 인증을 위한 설정이라고 했고... 이름도 account authentication method... 당연히 이메일 인증을 하기위한 dj_rest_auth 기본 설정이라고 생각했다. 그리고 여기서 모든 문제가 시작됐다. ㅠㅠ

회원가입만 커스텀했을 뿐 swagger api에서는 아이디, 이메일, 비밀번호로 로그인하라고 나와있는데 어째서인지 해당 필드를 가지고 postman으로 post 요청을 보내면 뜬금 자격증명 오류가 났다...

그리고 이 문제의 해결방법은 dj_rest_auth github에서 찾을 수 있다..

 

https://github.com/iMerica/dj-rest-auth/blob/6b394d9d6bb1f2979ea2d31e5a1199368d5616c1/dj_rest_auth/views.py

 

 

GitHub - iMerica/dj-rest-auth: Authentication for Django Rest Framework

Authentication for Django Rest Framework. Contribute to iMerica/dj-rest-auth development by creating an account on GitHub.

github.com

 

먼저 LoginView의 login메소드 첫줄을 보면 self.serializer를 호출한다.

def login(self):
        self.user = self.serializer.validated_data['user'] # 여기!
        token_model = get_token_model()

        if getattr(settings, 'REST_USE_JWT', False):
            self.access_token, self.refresh_token = jwt_encode(self.user)
        elif token_model:
            self.token = create_token(token_model, self.user, self.serializer)

        if getattr(settings, 'REST_SESSION_LOGIN', True):
            self.process_login()

 

여기서 사용하는 시리얼라이저는 LoginSerializer이다.

serializer_class = LoginSerializer

그래서 serializers.py파일의 LoginSerializer를 살펴보면 validate함수에서 validate > get_auth_user > get_auth_user_using_allauth > authenticate 이런 경로로 진행된다.

참고로 오픈소스에서 하나의 클래스에 딸려있는 여러 함수의 경로를 살펴볼때는 대부분 밑에서 위로 읽어보면 된다!

 

 

그런데 여기서 문제는 get_auth_user_using_allauth의 분기문이다!!

def get_auth_user_using_allauth(self, username, email, password):
        from allauth.account import app_settings

        # Authentication through email
        if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
            return self._validate_email(email, password)

        # Authentication through username
        if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
            return self._validate_username(username, password)

        # Authentication through either username or email
        return self._validate_username_email(username, email, password)

여기서 로그인을 하기 위한 필드(매개변수)로 어떤 조합을 받을지를 정하게 되는데, setting에 AUTHENTICATION_METHOD를 지정해주면 거기에 해당하는 조합으로 로그인 필드들을 사용할 수 있는것이다.

 

그런데 나는 ACCOUNT_AUTHENTICATION_METHOD = 'email' 을 설정해주어서 첫번째 분기문에서 걸리게 됐고 (이메일, 비밀번호만 받을 수 있게됨) , swagger는 serializer를 기반으로 api를 보여주기때문에 거기에는 이 설정이 반영되지 않아 이메일, 아이디, 비밀번호 필드가 모두 뜨게 된 것이다. ㅠㅠ

 

 

그래서 결국 _validate_email 함수가 받을 수 있는 매개변수와 postman으로 보내주는 요청간의 불일치로 해당문제가 발생한 것이었고, 기획 회의에 따라서 로그인은 이메일+비밀번호만 받는 것으로 결정하고 serializer를 커스텀해서 api에 해당 커스텀 사항이 반영되게 처리했다.

from dj_rest_auth.serializers import LoginSerializer
from rest_framework import serializers


class CustomLoginSerializer(LoginSerializer):
    email = serializers.CharField(required=True, allow_blank=False)
    password = serializers.CharField(style={'input_type': 'password'})

 

그리고 해당 api에 맞는 요청을 보내서 다음과 같이 200 OK를 받을 수 있었다! >__<