DRF를 활용하여 JWT 인증을 구현하다 마주한 문제 상황이다. 우선 로그인 기능을 다음과 같이 구현했다.
class UserLoginAPIView(APIView):
permission_classes = [AllowAny]
def post(self, request: Request):
username = request.data['username']
password = request.data['password']
user = User.objects.filter(username=username).first()
if user is None:
return Response(
{"message": "등록되지 않은 사용자입니다."},
status=status.HTTP_400_BAD_REQUEST
)
if not check_password(password, user.password):
return Response(
{"message": "비밀번호가 틀렸습니다."},
status=status.HTTP_400_BAD_REQUEST
)
try:
token = TokenObtainPairSerializer.get_token(user)
return Response(
{
"message": "로그인에 성공했습니다.",
"data": {
"access": str(token.access_token),
"refresh": str(token)
},
},
status=status.HTTP_200_OK
)
except Exception as e:
logger.error(e)
return Response({"message": "로그인 처리 중 문제가 발생했습니다."},
status=status.HTTP_400_BAD_REQUEST)
post method로 api 호출할 때 에러 메세지로 Expected a string value
라는 문구가 콘솔창에 찍혔다. TokenObtainPairSerializer의 get_token() 메서드 호출 시 발생하는 것 같아서 관련된 내용을 찾아 보다가 나와 비슷한 문제에 직면한 글을 stackoverflow에서 발견했다. 한 가지 다른 점은 나는 simple-jwt를 사용하여 구현한 것과 달리 작성자는 PyJWT를 사용한 것이다. 하지만 문제 해결에 도움이 됐다.
답변 중 JWT_SECRET_KEY 값을 설정해야 한다는 내용이 있었다.(물론 나는 simple-jwt를 사용했기 때문에 SIGNING_KEY를 설정해야 했다.) 공식 문서에 따르면 SIGNING_KEY 값을 따로 설정하지 않으면 SECRET_KEY 값이 기본값으로 설정된다고 한다.
분명 SECRET_KEY 값을 설정했고, 값이 정상적으로 불러와지는 것을 확인했으나 에러 해결이 되지 않았다. 그러던 중 문제가 발생하는 원인을 찾았다. 설정 파일을 분리해서 개발하면서 놓쳤던 부분이 있었던 것이었다.
우선 나는 개발 환경을 local과 prod 환경으로 나누어서 개발을 진행 중이다. local.py와 prod.py는 base.py 내용을 import 한 후 덮어 쓰는 방식으로 동작한다.(참고로 base.py는 settings.py의 명칭을 변경한 것이다.) 기존의 base.py 내용의 일부는 다음과 같았다.
base.py
SECRET_KEY = None # local.py와 prod.py에 각각 서로 다른 SECRET_KEY 값을 지정했다.
...
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
'UPDATE_LAST_LOGIN': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
...
결론은 base.py의 SECRET_KEY 값이 None이였고, 자연스럽게 SIGNING_KEY 값에도 None 값이 할당 되었기 때문에 발생한 문제였다. 따라서 SIMPLE_JWT을 local.py와 prod.py 각각에 작성 후 base.py에 작성 했던 내용을 없앰으로써 해당 문제를 해결할 수 있었다.