2차 프로젝트는 프립이라는 사이트를 맡게되었다.
얼핏봐도 1차에서 해보지 않았던 것들을 많이 마주치게될것 같다.(살려줘)
그래서 본격적으로 프로젝트에 들어가기전에 눈에띄는 공부할 사항들을 정리해보고자 한다.
2차 프로젝트에 분명 마주치게될것 같았던 녀석을 마주쳤다. 요즘에는 대부분의 사이트에서 지원하는 기능이다. 즉, 이런 OAUTH2.0도 잘 다룰 줄 알아야된다는 의미이다.
카카오톡의 로그인 구현 단계를 알아보자.
출처 : kakao developers - 카카오 로그인
kakao developers 에서 개발자 등록을 하고 앱을 등록해서 키를 발급받자.
나의 경우 exam이라는 앱을 만들고 키를 발급받았다. 저 앱 키
는 원래는 노출되어서는 안된다.
로그인의 경우 REST API 키
를 사용할 예정이다.
이제 먼저 [제품설정-카카오 로그인-동의항목] 으로 이동해주자.
해당 페이지에서는 서비스를 하며 사용자들에게서 받고 싶은 정보를 설정할 수 있다.
프로필 정보는 필수 동의
만 선택할 수 있지만 나머지는 선택 동의
, 이용 중 동의
등을 선택할 수 있다.
일단 프로필 정보와 카카오계정(이메일)을 받는 식으로 설정해놓자.
동의항목을 설정해놓고 [제품 설정-카카오 로그인] 페이지로 이동하면 이제 활성화 설정을 ON 으로 바꿔줄 수 있다. 만약 동의항목을 아무것도 설정해놓지 않으면 OFF 에서 바뀌지 않는다.
이제부터는 REST API
를 사용한 로그인 구현 방법을 알아볼 것이다.
카카오 - REST API를 사용한 로그인 구현 방법 를 참조하면서 천천히 알아보자.
프론트엔드는 로그인 동의 화면을 호출하고, 로그인 버튼 클릭 시 사용자의 동의를 거쳐서 토큰을 받을 수 있는 코드를 받아와야한다. 받은 인증 코드는 사용자 토큰 받기에 사용된다.
kakkao developer
인증 코드 받기 요청 시, 브라우저에 카카오계정 세션 존재 여부에 따라 사용자 동선이 다릅니다. 브라우저에 카카오계정 세션이 없다면 사용자는 카카오계정 정보 입력 및 인증을 거쳐 동의 항목 확인 화면을 보게 되고, 이미 카카오계정 로그인이 되어 있어 카카오계정 세션이 있다면 상태라면 다시 계정 정보를 묻지 않고 바로 동의 항목 확인 화면을 봅니다.
가이드에 따르면 동의 항목 확인 화면에서 사용자가 동의 버튼을 클릭하면 아래의 엔드포인트에 GET Request
를 보내야하고, 이곳에 client_id
와 redirect_uri
를 넣어줘야한다고 한다.
GET /oauth/authorize?client_id={app_key}&redirect_uri={redirect_uri}&response_type=code HTTP/1.1
Host: kauth.kakao.com
client_id
는 앱을 등록하고 받은 REST API키
이다. 그리고 redirect_uri
에는 코드 발급 후 리다이렉트 해야할 페이지 주소를 넣어야한다. 해당 주소는 [제품설정-카카오 로그인-Redirect URI] 에서 추가할 수 있다. 일단 임시 경로를 설정해놓자.
from django.shortcuts import redirect
from django.views import View
from frip.settings import KAKAO_KEY
class KakaoSignInView(View):
def get(self, request):
app_key = KAKAO_KEY
redirect_uri = "http://127.0.0.1:8000/account/sign-in/kakao/callback"
return redirect(
f"https://kauth.kakao.com/oauth/authorize?client_id={app_key}&redirect_uri={redirect_uri}&response_type=code"
)
runserver로 서버를 돌리고 위에 view가 작동하는 주소 http://127.0.0.1:8000/account/sign-in/kakao/
를 입력하면, 아래와 같이 로그인 화면이 뜬다.
이 곳에서 카카오 계정으로 로그인하면 [동의항목]에서 설정한 정보들에 대해서 동의를 얻고 코드를 쿼리스트링에 담아 미리 설정해놓은 redirect_uri
로 리다이렉트한다. 이 때 우린 코드를 얻어야한다.
만약 사용자가 동의를 하지 않으면 에러를 쿼리스트링에 담아 리다이렉트한다.
로그인을 하고 허용할 정보들에 동의를 해보자.
그럼 Page not found
창이 뜨는데, 리다이렉트된 경로에 아직 아무런 페이지도 만들지 않아서 뜨는 것이다. 여기서 화면을 잘 보면 Request URL 뒤쪽에 code를 찾을 수 있다.
위에서도 설명했다싶이 이 코드를 카카오에 보내서 사용자 토큰
을 받아올 수 있다.
인증코드를 받은 뒤에 코드로 사용자 토큰을 발급받아야한다. 토큰 발급까지 완료해야 로그인 절차가 끝날 수 있다.
위 샘플을 참고해서 토큰을 요청해보자.
여기서 이제 쓰여질 코드는 redirect_uri
를 통해서 이동되는 callback
이라는 엔드포인트에 대한 코드(view)이다.
import requests
class KakaoSignInCallbackView(View):
def get(self, request):
try:
authorize_code = request.GET.get("code")
app_key = KAKAO_KEY
redirect_uri = "http://127.0.0.1:8000/account/sign-in/kakao/callback"
token_request = requests.get(
f"https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={app_key}&redirect_uri={redirect_uri}&code={authorize_code}"
)
token_json = token_request.json()
error = token_json.get("error", None)
if error is not None:
return JsonResponse({"message": "INVALID_CODE"}, status=400)
access_token = token_json.get("access_token")
return JsonResponse({'access_token': access_token}, status=200)
except KeyError:
return JsonResponse({"message": "INVALID_TOKEN"}, status=400)
except access_token.DoesNotExist:
return JsonResponse({"message": "INVALID_TOKEN"}, status=400)
redirect_uri
로 리다이렉트되면서 전달된 코드를 갖고와야한다. code = request.GET.get('code')
를 통해서 code
갖고오는게 가능하다. request.GET에 대해서는 해당 글에서 확인가능하다. 결론만 말하자면 쿼리딕셔너리를 리턴해주기에, 뒤에 .get("code")
로 안에 담긴 code
를 갖고오는게 가능하다.
가이드에서 말한 받길 원한 정보들(grant_type
, client_id
, redirect_uri
, code
)를 카카오로 보내주자.
응답 받은 데이터를 json으로 받으면 토큰을 받아온 것을 알 수 있다. access_token
을 갖고오자.
{"access_token": "vnS0IbXJx3MB_QgDCVzP9g5mAa2h9VFM5x2A-Qo9c-wAAAFx8wlPww"}
프론트에서 저장해둔 카카오의 사용자 토큰을 이용해서 카카오에 사용자 정보를 요청해야한다.
가이드를 따라가면 access_token
을 이용한 사용자 정보 요청에 대한 설명이 있다.
Authorization 은 HTTP request의 헤더 부분에 있다. 즉, 헤더에 Bearer {access_token}
을 담아서 요청을 보내라는 의미이다.
profile_request = requests.get(
"https://kapi.kakao.com/v2/user/me", headers={"Authorization": f"Bearer {access_token}"}
profile_json = profile_request.json()
kakao_account = profile_json.get("kakao_account")
email = kakao_account.get("email", None)
kakao_id = profile_json.get("id")
# 값을 제대로 갖고왔는지 알아보자. 개인정보가 있어서 예시는 샘플에 있는걸 갖고왔다.
{"email": "rfs@kakao.com", "kakao_id": 00000000}
헤더에 토큰을 담아서 요청을 보내고, 이메일과 카카오 id를 받아왔다.
참고로 Authorization에 Bearer xxxxx
라는 식으로 요청을 보내면 모든 정보를 갖고온다.
카카오로 부터 받은 Response 객체에서 정보들을 json 형태로 변환한 결과(즉, profile_json
)를 print문으로 뽑아봤다.
{
'id': 00000000, 'connected_at': '2020-05-08T06:18:31Z',
'properties': {
'nickname': '홍길동',
'profile_image': 'http://xxx.kakao.co.kr/.../aaa.jpg',
'thumbnail_image': 'http://xxx.kakao.co.kr/.../bbb.jpg'
},
'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'
},
'has_email': True,
'email_needs_agreement': False,
'is_email_valid': True,
'is_email_verified': True,
'xxxxxxx@xxxxx.com'}
}
개인정보가 들어가있는 부분들은 kakao developer 의 예시문장으로 대체했다.
그 다음은 무엇을 해야할까?
카카오로부터 아이디와 이메일을 받았으니, 이제 우리가 만들 사이트의 로그인 시스템에 적용시켜야한다.
if Account.objects.filter(kakao_id = kakao_id).exists():
user = Account.objects.get(kakao_id = kakao_id)
token = jwt.encode({'id': user.id}, SECRET_KET, algorithm='HS256').decode('utf-8')
return JsonResponse({"token": token}, status=200)
Account(
kakao_id = kakao_id,
email = email,
).save()
token = jwt.encode({'id': user.id}, SECRET_KET, algorithm='HS256').decode('utf-8').decode('utf-8')
return JsonResponse({"token": token}, status=200)
접속한 카카오 아이디가 데이터베이스에도 존재하는지 확인하고, 존재한다면 카카오 아이디를 갖고 있는 유저 객체를 갖고온다. 아이디 정보로 토큰을 발행하고, 토큰을 리턴해준다.
현재 접속한 카카오 아이디가 데이터베이스에 없다면 새로운 유저를 저장하고 토큰을 발행하게 한다.
참고 블로그
devmin 님의 velog
devzunky 님의 velog