[Bob Morgan] 소셜 로그인/회원가입 구현

이태권 (Taekwon Lee)·2022년 7월 7일
0

[Project] Bob Morgan

목록 보기
2/6

⭐️ 구현 코드는 맨 아래에 있다

🎉🎉🎉 [밥 먹언] 소셜 로그인/회원가입 구현

📝 원리

  • Frontend에서 카카오 access_token을 받아온 이후 해당 유저 정보를 가져와 database에 저장
  • Database에 저장된 user 정보를 바탕으로 서비스(밥 먹언)의 자체적인 토큰을 발행
  • 발행한 자체적인 토큰을 json의 객체 형태로 frontend에 전달

Frontend

1. 카카오 로그인 화면

2. 로그인 완료 시

Backend

1. 소셜 회원 가입 성공

2. 소셜 로그인 성공

3. MySQL

mysql> desc users;
+-------------------+---------------+------+-----+---------+----------------+
| Field             | Type          | Null | Key | Default | Extra          |
+-------------------+---------------+------+-----+---------+----------------+
| id                | bigint        | NO   | PRI | NULL    | auto_increment |
| created_at        | datetime(6)   | NO   |     | NULL    |                |
| updated_at        | datetime(6)   | NO   |     | NULL    |                |
| email             | varchar(80)   | YES  | UNI | NULL    |                |
| password          | varchar(200)  | YES  |     | NULL    |                |
| nickname          | varchar(80)   | YES  |     | NULL    |                |
| profile_image_url | varchar(1000) | YES  |     | NULL    |                |
| date_of_birth     | date          | YES  |     | NULL    |                |
| platform_id       | bigint        | YES  | MUL | NULL    |                |
+-------------------+---------------+------+-----+---------+----------------+
mysql> select * from platforms;
+----+---------------+-------------------+
| id | serial_number | social_network_id |
+----+---------------+-------------------+
|  1 |    2330177102 |                 1 |
+----+---------------+-------------------+
1 row in set (0.00 sec)
mysql> select * from users;
+----+----------------------------+----------------------------+-----------------+----------+----------+-------------------+---------------+-------------+
| id | created_at                 | updated_at                 | email           | password | nickname | profile_image_url | date_of_birth | platform_id |
+----+----------------------------+----------------------------+-----------------+----------+----------+-------------------+---------------+-------------+
|  1 | YYYY:MM:DD HH:MM:SS.SSSSSS | YYYY:MM:DD HH:MM:SS.SSSSSS | ???@hanmail.net | NULL     | ??       | http://~~~~       | NULL          |           1 |
+----+----------------------------+----------------------------+-----------------+----------+------------------------------+---------------+-------------+
1 row in set (0.00 sec)

중요한 정보만 확대해 보았다.

mysql> select id, email, nickname, platform_id from users;
+----+-----------------+----------+-------------+
| id | email           | nickname | platform_id |
+----+-----------------+----------+-------------+
|  1 | ???@hanmail.net | ??       |           1 |
+----+-----------------+----------+-------------+

❓ 문제

아래처럼 platform에서 create로 해놓았다.
따라서 로그인을 할 때마다 새롭게 platformid가 계속해서 생성된다.

문제 1

KakaoSigninView (users/views.py)

kakao    = SocialNetwork.objects.get(name='kakao').id
platform = Platform.objects.create(platform_id=kakao_id, social_network_id=kakao)

MySQL 결과

로그인 할 때마다 계속해서 platform_id가 생성된다.

mysql> select * from platforms;
+----+-------------+-------------------+
| id | platform_id | social_network_id |
+----+-------------+-------------------+
|  1 |           1 |                 1 |
|  2 |           1 |                 1 |
|  3 |  2330177102 |                 1 |
|  4 |  2330177102 |                 1 |
+----+-------------+-------------------+
mysql> select * from platforms;
+----+-------------+-------------------+
| id | platform_id | social_network_id |
+----+-------------+-------------------+
|  1 |           1 |                 1 |
|  2 |           1 |                 1 |
|  3 |  2330177102 |                 1 |
|  4 |  2330177102 |                 1 |
|  5 |  2330177102 |                 1 |
+----+-------------+-------------------+
mysql> select * from platforms;
+----+-------------+-------------------+
| id | platform_id | social_network_id |
+----+-------------+-------------------+
|  1 |           1 |                 1 |
|  2 |           1 |                 1 |
|  3 |  2330177102 |                 1 |
|  4 |  2330177102 |                 1 |
|  5 |  2330177102 |                 1 |
|  6 |  2330177102 |                 1 |
+----+-------------+-------------------+
  1. 그리고 platform_id 용어가 크게 두 가지라 혼동이 온다... 모델링을 수정해야 한다.


❗️ 해결

해결 1

KakaoSigninView (users/views.py)

create 대신에 get_or_create 써서 매번 create를 하지 않고 이미 is_createdTrue이면 get을 해 오도록 바꾸었다.

kakao                 = SocialNetwork.objects.get(name='kakao').id
platform, is_created  = Platform.objects.get_or_create(
    platform_id       = kakao_id,
    social_network_id = kakao
)

해결 2

platformsplatform_id 대신 serial number로 수정하였다.

MySQL

이제 계속 로그인 해도 없어지지 않는다

mysql> select * from platforms;
+----+---------------+-------------------+
| id | serial_number | social_network_id |
+----+---------------+-------------------+
|  1 |    2330177102 |                 1 |
+----+---------------+-------------------+

🖥 최종(?) CODE

import jwt, requests

from django.http  import JsonResponse
from django.views import View
from django.conf  import settings

from users.models import User, SocialNetwork, Platform

class KakaoSigninView(View):
    def get(self, request):
        try:
            kakao_token = request.headers.get('Authorization')
            KAKAO_URL   = 'https://kapi.kakao.com/v2/user/me'
            headers     = {'Authorization': f'Bearer {kakao_token}'}
            response    = requests.get(KAKAO_URL, headers=headers, timeout=3)
            kakao_user  = response.json()

            kakao_id          = kakao_user['id']
            kakao_nickname    = kakao_user['kakao_account']['profile']['nickname']
            kakao_email       = kakao_user['kakao_account']['email']
            profile_image_url = kakao_user['kakao_account']['profile']['profile_image_url']

            kakao                 = SocialNetwork.objects.get(name='kakao').id
            platform, is_created  = Platform.objects.get_or_create(
                serial_number     = kakao_id,
                social_network_id = kakao
            )

            platform, is_created  = User.objects.get_or_create(
                defaults = {
                    'platform_id'       : platform.id,
                    'nickname'          : kakao_nickname,
                    'email'             : kakao_email,
                    'profile_image_url' : profile_image_url,
                }
            )

            access_token = jwt.encode({'id': platform.id}, settings.SECRET_KEY, settings.ALGORITHM)

            if is_created:
                return JsonResponse({'message' : 'ACCOUNT_CREATED', 'access_token': access_token}, status=201)
            else:
                return JsonResponse({'message' : 'SIGN_IN_SUCCESS', 'access_token': access_token}, status=200)

        except KeyError:
            return JsonResponse({'message': 'KEY_ERROR'}, status=400)

📝 궁금점

  • 현재 get_or_create로는 생성과 조회를 할 수는 있으나,
  • 추후에 회원 정보를 수정한다면 get_or_create의 한계점이 드러날 것이다.
  • 그럴 때는 어떻게 해야 하는가?
profile
(Backend Dev.) One step at a time

0개의 댓글