개인 프로젝트를 진행하면서 소셜로그인을 구현해야겠다는 생각으로 진행했다.
이전에 카카오 로그인을 시도하고, 구글 로그인을 시도하면서 DB를 싹 갈아엎은 후에 jwt 토큰을 이용하는 방식으로 시작했다.
초보 개발자(호소인)이므로 그냥 내가 나중에 보고 참고하기 위한 글이기에 제대로 안적혀 있을 수 있으니 참고하길 바람.
카카오가 확실히 문서가 직관적으로 되어있어서 그런지, 구글로그인에 비하면 금방코드를 적용할 수 있었고 실행되었다.
이전 구글로그인의 경험과 함께 아래 블로그를 참고해서 작성을 했다.
이번에는 django에서만 진행한 것 없이 바로 react와 django를 연동해서 시작했다.
카카오 로그인 버튼을 만들어주었다.
몰랐는데 카카오에서 제공하는 자체 규격 디자인이 있었다.
<div onClick={() => {navgiateToKakaoLogin()}}>
<img src={process.env.PUBLIC_URL+"images/kakaoLoginBtn1.png"} alt="" />
</div>

이제 인가 코드를 받아야한다.
구글 로그인과 마찬가지로 redirect_uri가 필요한데 이것과 함께 "client_id", "response_type"을 작성해주었다.
const kakaoParams :KakaoParamsType = {
"client_id" : kakaoClientId,
"redirect_uri" : kakaoRedirectUri,
"response_type" : "code",
}
const kParams = new URLSearchParams(kakaoParams).toString()
function navgiateToKakaoLogin(){
window.location.href = `https://kauth.kakao.com/oauth/authorize?${kParams}`
}
window.location.href =
https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code;
kParams를 작성해서 대입하는 방법말고 직접넣어줘도 된다.
이렇게 하면 내가 redirect_uri로 넘어가는데, 난 그 부분에 컴포넌트로 만들어주고 그곳에서 code를 받고 넘겨서 토큰을 받아오는 작업을 진행했다.
export async function getKakaoLoginCode(code:string) {
try {
const response = await axios.post(`${base_url}accounts/kakao/login/`, {code})
console.log("response===", response)
return response
} catch (e:any) {
// 실패 시 처리
console.error(e.response.status);
}
}
여기서 accounts/kakao/login/은 backserver와의 연동이다.
django에서 urls.py를 다음과 같이 설정하고 views.py를 생성했다.
path('kakao/login/', views.kakao_login, name='kakao_login'),
kakao_login 코드는 이전 구글로그인에서처럼 다음처럼 구성했다.

토큰을 받아오는 URL은 요기,

사용자 정보 받아오는 URL은 요기.
여기서도 구글로그인에서 했던것처럼, allauth를 제대로 쓰지않고, simple-jwt를 이용해서 token을 만들고 사용했다.
구글로그인에서는 내가 못찾은건지 모르겠지만, profile_image_url을 확인못했는데, 카카오에는 있었기에, 이를 활용해주었다.
카카오에서 별도의 로그아웃도 있었는데, 이 부분은 이번 개인 프로젝트에서는 구현하지 않기로 했다.
아래는 전체코드
@api_view(["POST"])
def kakao_login(reqeust):
client_id = os.environ.get("SOCIAL_AUTH_KAKAO_CLIENT_ID")
redirect_uri = os.environ.get("SOCIAL_AUTH_KAKAO_REDIRECT_URI")
# 시도
try:
reqeust_data = reqeust.data
# request에서 code 꺼내기
code = reqeust_data.get("code")
# 카카오에서 토큰 받아오기
kakao_token_response = requests.post(
'https://kauth.kakao.com/oauth/token',
headers={"Content-type": "application/x-www-form-urlencoded;charset=utf-8"},
data={
"grant_type": "authorization_code",
"client_id": client_id,
"redirect_uri": redirect_uri,
"code": code
}
)
access_token = kakao_token_response.json().get("access_token")
# 카카오에서 정보 받아오기
kakao_user = requests.get(
"https://kapi.kakao.com/v2/user/me",
headers={
"Authorization": f"Bearer {access_token}",
"Content-type": "application/x-www-form-urlencoded;charset=utf-8",
},
)
print("kakao_user:", kakao_user)
kakao_user = kakao_user.json()
kakao_account = kakao_user.get("kakao_account")
# social = "kakao"
social_id = kakao_user.get("id")
email = kakao_account.get("email")
nickname = kakao_account["profile"]["nickname"]
profile_image_url = kakao_account["profile"]["profile_image_url"]
print("kakao_account:", kakao_account)
print("social_id", social_id)
print("email", email)
print("nickname", nickname)
print("profile_image_url", profile_image_url)
# user데이터가 있으면 token과 데이터 가져오기
try:
user = User.objects.get(email=email, social='kakao')
refresh_token = RefreshToken.for_user(user) # jwt발급
response_data = {
'refresh': str(refresh_token),
'access': str(refresh_token.access_token),
# 'email': email,
'message': '가져오기 성공',
'userInfo' : {
'nickname' : user.nickname,
'social' :user.social,
'email' : user.email,
'profile_image_url' : user.profile_image_url
}
}
return Response(response_data, status=status.HTTP_200_OK)
# user데이터가 없으면 생성하고 token 가져오기
except User.DoesNotExist:
# 닉네임이 없으면 닉네임을 설정
if not nickname:
nickname = f"{random.choice(nickname_a)}{random.choice(nickname_b)}{random.randint(1000, 9999)}"
data = {
'email' : email,
'username' : email + str(random.randint(100000, 1000000)),
'nickname' : nickname,
'social' : 'kakao',
'social_id' : str(social_id),
'profile_image_url' : profile_image_url
}
try:
# user 생성
user = User.objects.create(
email=data.get("email"),
username=data.get("username"),
nickname=data.get("nickname"),
social=data.get("social"),
social_id=data.get("social_id"),
profile_image_url=data.get("profile_image_url")
)
user.set_unusable_password() # 소셜로그인이니까 No password!
user.save() # user 저장
print('저장성공')
# user 꺼내기
user = User.objects.get(username=data.username, social=data.social, email=email)
refresh_token = RefreshToken.for_user(user) # 자체 jwt 발급
response_data = {
'refresh': str(refresh_token),
'access': str(refresh_token.access_token),
'message': '저장성공',
'userInfo' : {
'nickname' : user.nickname,
'social' :user.social,
'email' : user.email,
'profile_image_url' : user.profile_image_url
}
}
return Response(response_data, status=status.HTTP_201_CREATED)
except:
response_data = {
'message': '저장실패'
}
return Response(status=status.HTTP_400_BAD_REQUEST)
# 이게 안되네...
except:
response_data = {
'message': '실패'
}
return Response(status=status.HTTP_400_BAD_REQUEST)
수정을 하나 하긴 해야할 듯 하다.
분명 저장도 잘되는것을 확인했는데, 저장하고, 즉시 들고 오는것이 불가능한 건지 에러가발생했다.
db를지우고 다시시도도 해봐야겠다.
물론 이미 DB로 저장됬으므로 처음이 안되는거지 이후에는 잘 들고온다.
user = User.objects.get(username=data.username, social=data.social, email=email)
이 부분이 잘 못 되어있었다.
수정후 꺼낼 때, 아래처럼 해야한다. data.get("") 또는 data[""]의 방식으로 해야되는데 이 부분을 잘 못 작성했다. 아래처럼 수정하니 문제는 해결되었다.
user = User.objects.get(username=data.get("username"), social=data.get("social"), email=email)