[Django] 카카오 소셜 로그인 구현
프로젝트에서 카카오 소셜 로그인 구현을 진행하였습니다..
소셜 로그인 구현을 처음 해보다보니 삽질을 많이 해서 저처럼 처음 해보시는 분들에게 도움이 되었으면 해서 글 남깁니다!
저와 함께 프로젝트를 진행하는 친구가 리액트 네이티브로 Front-end를 맡았고 저는 DRF로 백엔드를 구현하였습니다.
먼저 토큰(Token)기반 인증과 JWT, 카카오 로그인에 대해서 공부하였습니다. 아래 글은 공부하면서 작성한 글입니다. 참고하시면 될 것 같습니다.
https://purplemissbunny.tistory.com/47?category=1010354
[JWT] 토큰(Token) 기반 인증에 대한 소개
API를 사용하는 웹서비스를 개발한다면, 토큰을 사용하여 유저의 인증작업을 처리하는 것이 가장 좋은 방법이다. 📌토큰 기반 인증 시스템을 선택하는 이유는 다음과 같다. 1. Stateless 서버 Statele
purplemissbunny.tistory.com
https://purplemissbunny.tistory.com/48?category=1010354
카카오 로그인의 작동방식에 대해서 알아봅시다.
1) 사용자가 앱에서 카카오 버튼을 클릭합니다.
2) 사용자가 [카카오톡으로 로그인]을 선택하면 카카오톡 실행 또는 실행 중인 카카오톡으로 연결되고, [다른 카카오계정으로 로그인]을 선택하면 직접 계정 정보를 입력하는 화면이 출력됩니다. 카카오톡이 설치되어 있지 않은 기기나 PC 웹환경에서는 직접 계정 정보를 입력하여 카카오계정으로 로그인하게끔 진행됩니다.
3) [카카오톡으로 간편로그인] 한 경우, 카카오톡에 연결된 카카오계정의 자격정보(Credentials)를 통해 사용자를 인식합니다. 직접 카카오계정을 입력해 로그인한 경우에는 해당 계정의 자격정보로 인식합니다.
4) 자격정보가 올바르다면 카카오 로그인 동의 화면을 통해 사용자로부터 사용자 정보 및 기능 활용 동의를 받습니다.
5) 사용자가 필수 항목에 동의하고 로그인 요청하면 인가 코드(Authorization Code)가 발급됩니다. 이 코드는 앱정보의 Redirect URI에 전달됩니다.
6) 앱은 전달 받은 인가 코드를 기반으로 토큰(액세스 토큰, 리프레시 토큰)을 요청하고 받습니다.
카카오 로그인에 사용되는 토큰은 두 가지가 있습니다.
1) 액세스 토큰(Access Token): 사용자를 인증하고 카카오 API 호출 권한을 부여합니다.
2) 리프레시 토큰(Refresh Token): 액세스 토큰을 갱신합니다. 사용자가 매번 카카오계정 정보를 입력하거나 카카오톡으로 로그인하지 않고도 액세스 토큰을 발급받을 수 있게 합니다.
만약 서비스 앱이나 서버 에러가 아닌 요청 정보 문제로 로그인에 실패한다면 카카오 서버는 실패 원인을 응답으로 전달합니다. 이 때는 응답 코드를 참고해 원인을 찾아 수정할 수 있습니다.
로그인을 통해 앱과 사용자가 연결되고 토큰도 발급받았다면 사용자 정보 가져오기를 통해 현재 로그인한 사용자의 정보를 확인하여 서비스에 활용할 수 있습니다.
개발순서는 다음과 같습니다.
1) 프론트에서 Kakao Login 버튼 구현한다.
2) 프론트에서 네이티브 앱키를 사용해 카카오 서버와 통신하고 access token 받는다.
3) access token을 백엔드로 보낸다.
4) 백엔드에서 access token을 통해 사용자 정보를 받는다.
5) 사용자 정보를 통해 JWT을 받고 회원가입 유무를 체크해 신규 가입을 진행한다.
- 프로젝트 시퀀스 다이어그램입니다. 등록되어 있지 않은 사용자일 때 저희 프로젝트는 회원가입 부분을 추가로 진행하였지만 이 글에서는 등록되어 있지 않은 사용자일 때 자동 회원가입을 하도록 해보겠습니다.
여기서 프론트 쪽은 제가 진행하지 않아서 잘 모르기 때문에 백엔드 쪽만 작성하겠습니다.
프론트 쪽은 아래 문서 참고하시길 바랍니다.
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#request-code
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
프론트 쪽을 간단히 작성하자면
1. Fronted Kakao Login
리액트 카카오 로그인을 받아줍니다. 아래와 같은 구조로 되어 있습니다.
<KakaoLogin
token={token}
onSuccess={console.log}
onFail={console.error}
onLogout={console.info}
/>
2. 카카오 서버와 통신
구현된 버튼을 클릭하면 성공시에 access token을 받을 수 있습니다.
3. Back-end로 토큰 보내기
Django에서 url로 지정한 로그인 서버를 켜놔야 합니다. 그러면 views.py로 구현해놓은 KakaoSignInCallbackView(View) class로 access token이 이동합니다.
const kakaoResponse = async(response)=>{
console.log(response.response.access_token)
let res = await axios.get(
"http://localhost:8000/rest-auth/kakao/",
{
params:
{
code:response.response.access_token
},
}
);
console.log(res)
}
여기서부터 백엔드!
4. 백엔드에서는 프론트에서 받은 카카오의 access token을 이용하여 카카오 서버에 사용자 정보를 요청하고 db에 기존에 카카오 계정이 존재했는지 확인하여 있으면 카카오로그인을 진행하고 저장되어 있지 않으면 자동으로 db에 정보를 넣어 회원가입하고 카카오 로그인을 진행합니다.
@method_decorator(csrf_exempt, name='dispatch')
class KakaoSignInCallbackView(View): # 카카오톡 소셜로그인을 위한 클래스
# 백엔드에서는 프론트에서 받은 카카오의 사용자 토큰을 이용해 카카오에 사용자 정보를 요청한다.
def get(self, request):
kakao_access_code=request.GET.get("access_token",None)
print(kakao_access_code)
url="https://kapi.kakao.com/v2/user/me"
headers={
"Authorization":f"Bearer {kakao_access_code}",
"Content-type":"application/x-www-form-urlencoded; charset=utf-8"
}
kakao_response=requests.post(url, headers=headers)
kakao_response = json.loads(kakao_response.text) # 유저의 정보를 json화해서 변수에 저장
# 관리자가(employee) 기존에 카카오톡 계정이 DB에 저장되어 있는지 확인
if Account.objects.filter(social_login_id=kakao_response['id']).exists(): # 지금 접속한 카카오 아이디가 데이터베이스에 존재하는지 확인
user_info = Account.objects.get(social_login_id=kakao_response['id']) # 존재하는 카카오 아이디를 가진 유저 객체를 가져옴
encoded_jwt = jwt.encode({'id': user_info.id}, SECRET_KEY, algorithm='HS256') # jwt토큰 발행
return HttpResponse(f'id:{user_info.id}, token:{encoded_jwt}, exist:true')
# 저장되어 있지 않다면 회원가입
else:
Account(
social_login_id = kakao_response['id'],
email = kakao_response['kakao_account'].get('email', None), # 이메일 선택동의여서 없을 수도 잇음
).save()
user_info = Account.objects.get(social_login_id=kakao_response['id'])
encoded_jwt = jwt.encode({'id' : user_info.id}, SECRET_KEY, algorithm='HS256') #JWT 토큰 발행
return HttpResponse(f'id:{user_info.id}, token:{encoded_jwt}, exist:false')
- view.py KakaoSignInCallbackView(View)
코드를 뜯어보면
이 부분은 사용자 정보를 받는 부분입니다.
kakao_access_code=request.GET.get("access_token",None)
print(kakao_access_code)
url="https://kapi.kakao.com/v2/user/me"
headers={
"Authorization":f"Bearer {kakao_access_code}",
"Content-type":"application/x-www-form-urlencoded; charset=utf-8"
}
kakao_response=requests.post(url, headers=headers)
kakao_response = json.loads(kakao_response.text) # 유저의 정보를 json화해서 변수에 저장
request.GET.get을 통해 access token을 받아오고 이를 requests.post를 사용해 카카오 서버로 보냅니다. 그러면 kakao_response로 원하는 사용자 정보를 받을 수 있습니다. 카카오 개발자 홈페이지에서 미리 설정한 동의 항목을 바탕으로 가져옵니다. 아래와 같은 형태로 가져옵니다.
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"
},
"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":"여"
...
},
}
다음 코드를 봅시다. 이 부분은 기존 회원의 유무를 판단해서 로그인 or 자동으로 회원가입을 시켜서 로그인을 합니다.
Django의 model.py에 미리 Account Class를 구현해놓았습니다.
위에서 받은 사용자 정보중에 id항목은 사용자 고유 항목이기 대문에 중복이 발생하지 않습니다. 따라서 id를 이용하여 db와 비교하여 기존 회원이면 로그인을 진행하고 기존 회원이 아니면 자동으로 회원가입을 시키고 로그인을 진행합니다.
기존 회원일 경우 id 값을 바탕으로 JWT 토큰으로 만들어 프론트로 전달합니다. 신규 회원일 경우 Account 모델에 사용자 정보를 저장해주고 id 값을 바탕으로 JWT토큰을 만들어 프론트로 보내줍니다.
# 관리자가(employee) 기존에 카카오톡 계정이 DB에 저장되어 있는지 확인
if Account.objects.filter(social_login_id=kakao_response['id']).exists(): # 지금 접속한 카카오 아이디가 데이터베이스에 존재하는지 확인
user_info = Account.objects.get(social_login_id=kakao_response['id']) # 존재하는 카카오 아이디를 가진 유저 객체를 가져옴
encoded_jwt = jwt.encode({'id': user_info.id}, SECRET_KEY, algorithm='HS256') # jwt토큰 발행
return HttpResponse(f'id:{user_info.id}, token:{encoded_jwt}, exist:true')
# 저장되어 있지 않다면 회원가입
else:
Account(
social_login_id = kakao_response['id'],
email = kakao_response['kakao_account'].get('email', None), # 이메일 선택동의여서 없을 수도 잇음
).save()
user_info = Account.objects.get(social_login_id=kakao_response['id'])
encoded_jwt = jwt.encode({'id' : user_info.id}, SECRET_KEY, algorithm='HS256') #JWT 토큰 발행
return HttpResponse(f'id:{user_info.id}, token:{encoded_jwt}, exist:false')
Reference
https://developers.kakao.com/docs/latest/ko/kakaologin/common
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
https://velog.io/@jnano94/ReactDjangoKakao-Social-Login
[React+Django]Kakao Social Login
개발순서 Frontend에서 Kakao Login 버튼 구현 Frontend에서 앱키를 사용해 카카오 서버와 통신, access token받기 access token을 Backend(Django)로 보냄 Backend에서 access token를 통해 사용자
velog.io
(개인적으로 매우 많은 도움을 받았던 블로그)