[RESUMAI 프로젝트 2] DRF 카카오 소셜로그인, 이것만 보면 한번에 끝!!

Youngjoon Jang
10 min readApr 28, 2024

--

지난 글에서는 DRF를 활용하여 카카오 로그인을 마무리하는 과정에 대해 설명했다. 그러나 이전 글 방법대로 하면 클라이언트 입장에서 개발하기 쉽지 않아, 좀 더 근본 있는(?) 방식으로 변경하게 되었다. 우선 코드는 다음과 같다.

# accouts/views.py
User = get_user_model()


def kakao_login(request):
return redirect(
f"https://kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={KAKAO_CALLBACK_URI}&response_type=code"
)


class KakaoLoginView(APIView):
def post(self, request, *args, **kwargs):
code = request.data.get("code")

if not code:
return Response(
{"error": "Code is required"}, status=status.HTTP_400_BAD_REQUEST
)

# 카카오 인가코드를 사용해 access_token 획득
token_res = requests.get(
f"https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={REST_API_KEY}&client_secret={CLIENT_SECRET}&redirect_uri={KAKAO_CALLBACK_URI}&code={code}"
)
logger.fatal(token_res)

if token_res.status_code != 200:
logger.fatal(token_res.json())
return Response(
{"error": "Failed to obtain access token"},
status=status.HTTP_400_BAD_REQUEST,
)

token_json = token_res.json()
access_token = token_json.get("access_token")

# 카카오 access_token으로부터 사용자 정보 획득
headers = {"Authorization": f"Bearer {access_token}"}
profile_res = requests.get("https://kapi.kakao.com/v2/user/me", headers=headers)

if profile_res.status_code != 200:
return Response(
{"error": "Failed to obtain user information"},
status=status.HTTP_400_BAD_REQUEST,
)

profile_json = profile_res.json()

kakao_oid = profile_json.get("id")
nickname = profile_json.get("properties")["nickname"]
profile_image = profile_json.get("properties")["profile_image"]
email = profile_json.get("kakao_account")["email"]

user, created = User.objects.get_or_create(
email=email,
defaults={
"username": f"{nickname}",
"kakao_oid": kakao_oid,
"profile_image": f"{profile_image}",
},
)

# 사용자에 대한 토큰 생성
refresh = RefreshToken.for_user(user)
data = {
"access_token": str(refresh.access_token),
"refresh_token": str(refresh),
"user_info": {
"id": user.id,
"email": user.email,
"username": user.username,
"profile_image": user.profile_image,
"is_created": created,
},
}

return Response(data, status=status.HTTP_200_OK)

각각의 코드 조각을 설명해 보면 다음과 같다.

1. 인가 코드 받아오기

User = get_user_model()

def kakao_login(request):
return redirect(
f"https://kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={KAKAO_CALLBACK_URI}&response_type=code"
)

이 부분은 클라이언트 부분과의 싱크를 맞추기 위해서 작성했는데, 위에 작성한 url로 접속하면 code? 라는 query로 카카오에서 인자 코드가 반환된다.

예시

이렇게 뒤의 code를 클라이언트 측에서 파싱하여 서버로 보내면, 카카오의 AT (Access Token)을 받아 유저 정보를 반환받고, 자체 AT, RT를 만들어 반환하는 코드를 작성해야 한다.

2. 자체 AT, RT 반환

2–1. 인가코드로 카카오 access_token 획득

먼저, 사용자가 인가 코드를 post하게 하여, kauth로부터 카카오 AT를 반환받는다.
코드는 다음과 같다.

class KakaoLoginView(APIView):
def post(self, request, *args, **kwargs):
code = request.data.get("code")

if not code:
return Response(
{"error": "Code is required"}, status=status.HTTP_400_BAD_REQUEST
)

# 카카오 인가코드를 사용해 access_token 획득
token_res = requests.get(
f"https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={REST_API_KEY}&client_secret={CLIENT_SECRET}&redirect_uri={KAKAO_CALLBACK_URI}&code={code}"
)

if token_res.status_code != 200:
return Response(
{"error": "Failed to obtain access token"},
status=status.HTTP_400_BAD_REQUEST,
)

token_json = token_res.json()
access_token = token_json.get("access_token")

2–2. 인가코드로 카카오 access_token 획득

그렇게 받은 카카오 AT를 Bearer와 함께 header로 유저 프로필을 받아오는 request를 보낸다. 그렇게 되면, 유저의 카카오 oid,닉네임, 프로필 이미지, email을 받아올 수 있다.

이후, ‘user, created = User.objects.get_or_create’ 함수를 사용해서 ‘user’로 유저 정보를, ‘created’로 유저가 새로 생성됐는지 여부를 반환받을 수 있다.

        # 카카오 access_token으로부터 사용자 정보 획득
headers = {"Authorization": f"Bearer {access_token}"}
profile_res = requests.get("https://kapi.kakao.com/v2/user/me", headers=headers)

if profile_res.status_code != 200:
return Response(
{"error": "Failed to obtain user information"},
status=status.HTTP_400_BAD_REQUEST,
)

profile_json = profile_res.json()

kakao_oid = profile_json.get("id")
nickname = profile_json.get("properties")["nickname"]
profile_image = profile_json.get("properties")["profile_image"]
email = profile_json.get("kakao_account")["email"]

user, created = User.objects.get_or_create(
email=email,
defaults={
"username": f"{nickname}",
"kakao_oid": kakao_oid,
"profile_image": f"{profile_image}",
},
)

2–3. 자체 AT, RT 생성

마지막으로, rest_framework 라이브러리의 RefreshToken을 사용해서 자체 RT, AT를 발급받을 수 있다. 본인은 user_info도 함께 반환했다.

from rest_framework_simplejwt.tokens import RefreshToken

# 사용자에 대한 토큰 생성
refresh = RefreshToken.for_user(user)
data = {
"access_token": str(refresh.access_token),
"refresh_token": str(refresh),
"user_info": {
"id": user.id,
"email": user.email,
"username": user.username,
"profile_image": user.profile_image,
"is_created": created,
},
}

return Response(data, status=status.HTTP_200_OK)

3. 결과

Swagger로 보이는 결과는 다음과 같다.

실제 인가 코드를 첨부하여 실행해보면 결과는 다음과 같다.

결국은 그냥 1개의 api만 있으면 끝난다. (url로 접속해서 인가 코드를 받아오는 작업은 그냥 클라이언트 측에서 하면 되기 때문에..)

이렇게 간단하게 DRF로 카카오 소셜 로그인을 끝냈다. 생각보다 크게 할게 별거 없었다. 지난번에 비해 클라이언트 측에서 카카오 로그인 로직을 비교적 수월하게 할 수 있게 된 것 같다.

다음 글에서는 DRF+Docker+ELB 배포 방법에 대해 다뤄볼 예정이다.

--

--