def SocialSiginin(**kwargs): # 소셜 로그인/회원가입
# 각각 소셜 로그인에서 유저 정보를 받아오고 None인 값들은 빼줌
data = {k: v for k, v in kwargs.items() if v is not None}
email = data.get("email")
signin_type = data.get("signin_type")
if not email:
# email이 없으면 회원가입이 불가능하므로 프론트에 error메시지와 http status를 보냄
return Response(
{"error": "해당 계정에 email정보가 없습니다."}, status=status.HTTP_400_BAD_REQUEST
)
try:
user = User.objects.get(email=email)
if signin_type == user.signin_type:
if user.is_active == 0:
return Response(
{"message": "탈퇴한 계정입니다."}, status=status.HTTP_403_FORBIDDEN
)
# 로그인 타입이 같으면, 토큰 발행해서 프론트로 보내주기
refresh_token = RefreshToken.for_user(user)
access_token = CustomTokenObtainPairSerializer.get_token(user)
return Response(
{
"refresh": str(refresh_token),
"access": str(access_token.access_token),
},
status=status.HTTP_200_OK,
)
else:
# 유저의 다른 소셜계정으로 로그인한 유저라면, 해당 로그인 타입을 보내줌.
# (프론트에서 "{signin_type}으로 로그인한 계정이 있습니다!" alert 띄워주기)
return Response(
{"error": f"{user.signin_type}로 이미 가입된 계정이 있습니다!"},
status=status.HTTP_400_BAD_REQUEST,
)
except User.DoesNotExist:
# 유저가 존재하지 않는다면 회원가입시키기
new_user = User.objects.create(**data)
new_user.set_unusable_password() # pw는 사용불가로 지정
new_user.save()
# 회원가입 후 토큰 발급해서 프론트로 보냄
refresh_token = RefreshToken.for_user(new_user)
access_token = CustomTokenObtainPairSerializer.get_token(new_user)
return Response(
{"refresh": str(refresh_token), "access": str(access_token.access_token)},
status=status.HTTP_200_OK,
)
<문제점/>
일단, 이 코드는 이전 프로젝트의 다른 팀원 분이 작성한 코드로 거의 비슷하게 작성했기 때문에 내가 작성한 코드라고 할 수 없다
그래서 내가 작성한 코드라고 당당히 말할 수 있도록 코드를 보고 어떤 부분을 고치면 좋을지 고민했다
위 코드에서 내 마음에 걸린 건 try-except문이었다(최근 추천 함수 리팩토링에도 try-except문을 썼는데...ㅋㅋㅋ내친 김에 그것도 다시 고쳐봐야겠다)
try-except문을 사용하면 가장 안 좋은 점이 모르는 사람이 코드를 봤을 때 어떤 에러 때문에 예외 처리를 한 건지 명확히 알 수 없다는 점이다
이 코드는 except문에 user가 존재하지 않으면 이렇게 해라 라고 되어 있기 때문에 모르는 사람이 봐도 왜 이렇게 작성된 건지 알 수 있지만..!
장고 공식 문서를 보다가 다른 방식도 있다는 걸 알게 되었으니 그걸 적용해 보고 싶은 마음도 있다
<수정한 코드/>
def SocialSignin(**kwargs): # 소셜 로그인/회원가입
# 소셜 로그인에서 유저 정보를 받아오고 None인 값들은 빼줌
data = {k: v for k, v in kwargs.items() if v is not None}
email = data.get("email")
signin_type = data.get("signin_type")
if not email:
# email이 없으면 회원가입이 불가능하므로 프론트에 error메시지와 http status를 보냄
return Response(
{"error": "해당 계정에 email정보가 없습니다."}, status=status.HTTP_400_BAD_REQUEST
)
user, created = User.objects.get_or_create(email=email, defaults=data)
if created:
user.set_unusable_password()
user.save()
# 회원가입 후 토큰 발급해서 프론트로 보냄
refresh_token = RefreshToken.for_user(user)
access_token = CustomTokenObtainPairSerializer.get_token(user)
return Response(
{
"refresh": str(refresh_token),
"access": str(access_token.access_token),
},
status=status.HTTP_200_OK,
)
else:
# DB에서 찾은 사용자와 로그인 타입이 일치하는 경우
if user.signin_type == signin_type:
if user.is_active == 0:
return Response(
{"message": "탈퇴한 계정입니다."}, status=status.HTTP_403_FORBIDDEN
)
refresh_token = RefreshToken.for_user(user)
access_token = CustomTokenObtainPairSerializer.get_token(user)
return Response(
{
"refresh": str(refresh_token),
"access": str(access_token.access_token),
},
status=status.HTTP_200_OK,
)
else:
return Response(
{"error": f"{user.signin_type}로 이미 가입된 계정이 있습니다!"},
status=status.HTTP_400_BAD_REQUEST,
)
try-except문 대신 장고 쿼리셋API 중 하나인 get_or_create 메서드를 사용해보았다
해당 메서드는 변수를 obj, created 이렇게 2개 설정해서 DB에 찾는 객체가 있다면 obj 변수에 해당 객체가 들어가고 created 변수에는 boolean값으로 false가 들어간다
찾는 객체가 없다면 새롭게 객체를 생성해서 obj에 해당 객체가 들어가고, created에는 true가 들어간다
이렇게 수정함으로써 코드의 가독성이 높아지고, 실제로 코드가 동작할 때에도 try-except문으로 되어 있으면 try문을 무조건 통과해서 에러가 발생하면 그때 except로 빠지는데 수정한 코드에서는 바로 if문이든 else문이든 코드가 바로 실행되니 불필요한 과정을 줄였다고 볼 수 있다