TIL48 - Kakao Login with Django

Kiyong Lee·2021년 10월 31일
0

2차프로젝트

목록 보기
7/8

Kakao Login with Django


1. 계기

1차 프로젝트때 일반적인 회원가입과 로그인을 해봤으니(물론 제가 안했지만)
2차 때는 소셜로그인 API를 가져와보는 게 어떠냐는 멘토님의 말씀과
저 또한 이번엔 카카오 로그인을 해보고 싶어서 하게 되었습니다.

다만, 찾아보면서 대부분이 나 이렇게 했다식의 자랑이 많아서..
혹여나 보실 분이 계실지 모르지만 상세하게.. 적어보려고 합니다

되도록이면 공식문서 한 번 읽어보고 오시면 더 도움이 될 거라고 생각합니다!


2. 과정

이 그림 카카오 디벨로퍼스에서 많이 보셨을텐데, 백엔드로서 할 건 간단합니다.

프론트엔드에서 토큰을 보내주면, 회원가입을 시켜주던가 기존의 유저 정보를 주던가..!

이게 무슨 말이냐 하면, 당연히 처음에는 어떠한 회원도 없는 빈 DB입니다

하지만 나는 리디북스에 회원가입을 안했지, 카카오톡에는 되어 있기 때문에

받은 토큰을 이용해 카카오톡에 이 토큰을 가진 유저정보를 달라고 요청을 보냅니다.

그러면 카카오에서 리턴해준 정보를 이용해서, 신규 가입을 시켜주던가

이미 가입된 유저면 식별해서 로그인 시켜주면 되는 것입니다.


3. urls.py

# 최상위 url
from django.urls import path, include
    
urlpatterns = [
    path('account',   include('users.urls')),
    path('subscribe', include('subscribes.urls'))
]

#'/account로 들어왔을 때 url conf
from django.urls import path

from users.views import KakaoLoginView

urlpatterns = [
    path('/sign-in/kakao', KakaoLoginView.as_view())
]

최종적으로 카카오 로그인을 실행하기 위한 url은

http://localhost:8000/account/sign-in/kakao

가 되었습니다


4. models.py

from django.db   import models

from core.models import TimeStampModel

class SocialPlatform(TimeStampModel) :
    name = models.CharField(max_length=20)

    class Meta :
        db_table = 'social_platforms'

class User(TimeStampModel) :
    profile_nickname = models.CharField(max_length=40)
    profile_image    = models.CharField(max_length=500, null=True)
    account_email    = models.CharField(max_length=40)
    social_platform  = models.ForeignKey(SocialPlatform, on_delete=models.SET_NULL, null=True)

    class Meta :
        db_table = 'users'

소셜플랫폼은 나중 리팩토링을 위해 만들었고, 유저정보의 경우
카카오 디벨로퍼스에 나와있는걸 참고하여
프론트엔드 카카오로그인 개발자분과 닉네임/프사/이메일을 받자고 합의하였습니다.


5. kakao_users.py

아까 설명했다시피, 프론트로부터 받은 토큰을 이용하여 카카오에 유저정보 요청과
그걸 토대로 회원가입 및 로그인이 이루어지는 과정인데
처음에는 하나의 뷰에서 이 두 과정을 다 작성하였습니다.

그런데, 분리하는 것이 원칙적으로 일반적인 과정이라는 멘토님의 피드백을 받고
수정해보기로 했으며, 그래서 만든 게 kakao_users.py입니다.

이 파일은 프론트로부터 받은 토큰을 이용해 카카오에 유저정보를 요청하기 위해
만들어진 용도입니다.

starapp을 한 파일리스트에서 해당 파일만 추가된 것입니다.

import requests

from django.http import JsonResponse

class KakaoAPI :
    def __init__(self, access_token, social_url) :
        self.access_token = access_token
        self.social_url   = social_url
    
    def get_user(self) :
        headers  = {'Authorization' : f'Bearer {self.access_token}'}
        response = requests.get(self.social_url, headers=headers, timeout=3)
      
        if not response.status_code == 200 :
            return JsonResponse({'message' : 'Invalid Token'}, status=401)
        
        return response

토큰과 요청을 보낼 url을 받아서 유저정보를 리턴합니다.


6. views.py

카카오 디벨로퍼스에서 HTTP Method, URL, Authorization까지 다 나와서
그대로 따라했습니다.

import jwt

from json.decoder import JSONDecodeError
from django.views import View
from django.http  import JsonResponse

from users.models        import User
from users.kakao_users  import KakaoAPI
from ridibooksl.settings import (
    SECRET_KEY,
    ALGORITHMS
)

class KakaoLoginView(View):
    def post(self, request):
        try :
            access_token = request.headers.get("Authorization", None)
            kakao_user   = KakaoAPI(access_token, "https://kapi.kakao.com/v2/user/me")
            response     = kakao_user.get_user()

            if not response.status_code == 200 :
                return JsonResponse({'message' : 'Invalid Token'}, status=401)
            
            user_data = response.json()

        except KeyError:
            return JsonResponse({'message' : 'KEY ERROR'}, status=400)   
            
        except JSONDecodeError :
            return JsonResponse({'message' : 'JSON DECODE ERROR'}, status=400)

        login_user , state = User.objects.get_or_create(
            profile_nickname = user_data['properties']['nickname'],
            account_email    = user_data['kakao_account']['email'],
            profile_image    = user_data['kakao_account']['profile'].get('profile_image_url', None)
        )

        new_token = jwt.encode({'user_id' : login_user.id}, SECRET_KEY, ALGORITHMS)

        return JsonResponse({'new_token' : new_token, 'user_id': login_user.id}, status = 200)
  1. 토큰과 URL을 이용하여 kakao_users.py의 KakaoAPI에 유저정보 요청
  2. 성공적으로 불러오지 못할 시, 토큰에 문제가 있으므로 Invalid Token 에러 발생
  3. 성공적으로 불러올 시, 유저정보 json형태로 받음
  4. 예외처리 과정을 모두 뚫었으면 get_or_create를 이용하여 유저정보 불러오던가,
    생성하던가 해줌
  5. 'properties', 'kakao_account' 등 이런 것들은 리턴 결과를 보면 알 수 있음

response 예시

카카오 디벨로퍼스에서 보여준 response예시입니다.
이 정보들이 위의 코드에 있는 user_data에 들어가게 되며,
여기서 원하는 값을 가져와서 get_or_create 해주는 것이죠

HTTP/1.1 200 OK
{
    "id":123456789,
    "kakao_account": { 
        "profile_needs_agreement": false,
        "profile": {
            "nickname": "홍길동",
            "thumbnail_image_url": "http://yyy.kakao.com/.../img_110x110.jpg",
            "profile_image_url": "http://yyy.kakao.com/dn/.../img_640x640.jpg",
            "is_default_image":false
        },
        "email_needs_agreement":false, 
        "is_email_valid": true,   
        "is_email_verified": true,   
        "email": "sample@sample.com",
        "age_range_needs_agreement":false,
        "age_range":"20~29",
        "birthday_needs_agreement":false,
        "birthday":"1130",
        "gender_needs_agreement":false,
        "gender":"female"
    },  
    "properties":{
        "nickname":"홍길동카톡",
        "thumbnail_image":"http://xxx.kakao.co.kr/.../aaa.jpg",
        "profile_image":"http://xxx.kakao.co.kr/.../bbb.jpg",
        "custom_field1":"23",
        "custom_field2":"여"
        ...
    }
}
profile
ISTJ인 K-개발자

0개의 댓글