개인 프로젝트를 진행하면서 소셜로그인을 구현해야겠다는 생각으로 진행했다.
구글 로그인과 카카오 로그인을 하고 깃허브 로그인 까지 진행해본다.
초보 개발자(호소인)이므로 그냥 내가 나중에 보고 참고하기 위한 글이기에 제대로 안적혀 있을 수 있으니 참고하길 바람.

깃허브도 검색하니, 어렵진 않아보였고, 카카오와 구글 로그인의 경험으로 금방 할 수 있었다.
초기 준비 및 로그인 구현을 이번에는 아래를 참고해서 작성했다.
여기서도 client_id그리고 redirect_uri가 필요하며 또한 client_secret key가 필요하다
우선 깃허브 로그인 버튼을 만들어 주었다.
<div onClick={() => {navigateGithubLogin()}}>
<img src={process.env.PUBLIC_URL+"images/githubLogo1.png"} alt=""/>
<span>Github 로그인</span>
<div></div>
</div>
인가 코드를 받아야 한다.

이를 위해 navigateGithubLogin()를 아래처럼 구성해서 진행해주었다.
const githubParams :GithubParamsType = {
"client_id" : githubClientId,
"redirect_uri" : githubRedirectUri,
"scope" : "read:user, user:email",
}
const gitParams = new URLSearchParams(githubParams).toString()
function navigateGithubLogin(){
window.location.href = `https://github.com/login/oauth/authorize?${gitParams}`
}
카카오와 구글에서 했던것처럼 프론트에서 redirect_uri에 해당하는 컴포넌트를 만들고, 그곳으로 잠깐이지만, 이동해서 코드를 받도록 했다.
해당 컴포넌트에서
const params = new URLSearchParams(search);
const code = params.get("code");
를 통해서 code를 받아오고
이 code를 Django(back server)으로 github에서 토큰과 유저 정보를 받아서,DB의 유저를 불러와 다시 jwt를 발급해서 사용하는 방식을 이용했다.
그리고 받은 것은 쿠키에 저장하여 이용하도록 했다.
그래서 redirect_uri에 있는 코드는 아래와 같다.
async function handleGetGithubLoginCode(code:string) {
try {
const respose = await getGithubLoginCode(code);
const responseData = respose?.data
console.log(responseData)
const tmt_token = responseData.access
const tmt_refresh_token = responseData.refresh
const nickname = responseData.userInfo.nickname
const social = responseData.userInfo.social
const email = responseData.userInfo.email
const profile_image_url = responseData.userInfo.profile_image_url
Cookies.set('tmt_token', tmt_token);
Cookies.set('tmt_refresh_token', tmt_refresh_token);
Cookies.set('provide', social);
Cookies.set('userinfo', JSON.stringify({
profile_image_url: profile_image_url,
nickname: nickname,
email: email
}));
navigate('/')
} catch (error) {
console.log(error);
}
}
getGithubLoginCode()
는 아래처럼 연결시켰다.
// 깃허브 로그인을 위한
export async function getGithubLoginCode(code:string) { // async, await을 사용하는 경우
console.log('getGithubLoginCode: ',code)
try {
const response = await axios.post(`${base_url}accounts/github/login/`, {code}) // Backtick(`)을 이용해 이렇게 요청할 수도 있다.
console.log("response===", response)
return response
} catch (e:any) {
// 실패 시 처리
console.error(e.response.status);
}
}
우선 urls.py에서 아래처럼 작성을 했다.
path('github/login/', views.github_login, name='github_login'),
views.py에서는 github_login을 아래처럼 작성했다.
전체적인 틀은 아래처럼 했다.
- 코드를 받아온다.
- 코드를 통해 토큰과 유저정보를 받아온다.
- 이메일도 필요하므로 이메일 정보도 받아온다.
- 정보를 통해 유저 데이터를 가져온다.
- 유저 데이터가 없으면 생성.
- JWT 발행!
Django에서의 전체 코드는 아래처럼 구성을 했다.
# 깃허브 로그인
@api_view(["POST"])
def github_login(request):
if request.method == "POST":
client_id = os.environ.get("SOCIAL_AUTH_GITHUB_CLIENT_ID")
redirect_uri = os.environ.get("SOCIAL_AUTH_GITHUB_REDIRECT_URI")
client_secret = os.environ.get("SOCIAL_AUTH_GITHUB_CLIENT_SECRET")
# 로그인 시도
try:
# 토큰과 유저정보 받아오기
request_data = request.data
code = request_data.get("code")
print('code===',code)
# 유저 토큰 가져오기
github_token = requests.post(f"https://github.com/login/oauth/access_token?code={code}&client_id={client_id}&client_secret={client_secret}&redirect_uri={redirect_uri}",
headers={"Accept": "application/json"},
)
access_token = github_token.json().get("access_token")
# 유저 정보 데이터 가져오기
user_data = requests.get("https://api.github.com/user",
headers={"Authorization": f"Bearer {access_token}"}
)
user_data = user_data.json()
# 유저 이메일 데이터 가져오기
user_emails = requests.get("https://api.github.com/user/emails",
headers={
"Authorization" : f"Bearer {access_token}",
"Accept" : "application/json"
}
)
user_emails = user_emails.json()
# print("user_emails===", user_emails)
email = user_emails[0].get('email')
social_id= user_data.get("id")
social = "github"
# email
nickname = user_data.get("login")
profile_image_url = user_data.get("avatar_url")
# DB에 유저가 있는지 확인
try:
user = User.objects.get(email=email, social="github")
refresh_token = RefreshToken.for_user(user) # jwt발급
response_data = {
'refresh': str(refresh_token),
'access': str(refresh_token.access_token),
'message': '가져오기 성공',
'userInfo' : {
'nickname' : nickname or user.nickname,
'social' :user.social,
'email' : user.email,
'profile_image_url' : profile_image_url or user.profile_image_url
}
}
return Response(response_data, status=status.HTTP_200_OK)
# DB에 유저가 없으면
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' : 'github',
'social_id' : str(social_id),
'profile_image_url' : profile_image_url
}
# DB에 유저 생성
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)
print('꺼내기성공')
refresh_token = RefreshToken.for_user(user) # 자체 jwt 발급
print('토큰 발급성공')
response_data = {
'refresh': str(refresh_token),
'access': str(refresh_token.access_token),
'message': '저장성공',
'userInfo' : {
'nickname' : data.get("nickname"),
'social' : data.get("social"),
'email' : data.get("email"),
'profile_image_url' : data.get("profile_image_url")
}
}
print('완전 성공')
return Response(response_data, status=status.HTTP_201_CREATED)
# DB에서 유저 생성 실패
except:
response_data = {
'message': '연결되고 유저생성 실패'
}
return Response(response_data, status=status.HTTP_400_BAD_REQUEST)
# 로그인 시도 실패
except:
response_data = {
'message': '로그인 시도 실패'
}
return Response(response_data, status=status.HTTP_400_BAD_REQUEST)
response_data = {
'message': '로그인 실패'
}
return Response(response_data, status=status.HTTP_400_BAD_REQUEST)
여기서 3번의 요청을 github로 보낸다.
첫번째
requests.post(f"https://github.com/login/oauth/access_token?code={code}&client_id={client_id}&client_secret={client_secret}&redirect_uri={redirect_uri}",
headers={"Accept": "application/json"},
)
code, client_id, client_secret, redirect_uri를 통해 토큰을 받아온다.
header에는 "Accept": "application/json"을 추가한다.
두번째
requests.get("https://api.github.com/user",
headers={"Authorization": f"Bearer {access_token}"}
)
유저의 정보를 앞서 받은 토큰을 이용해서 받아온다.
header에는 첫번째에서 발급받은 토큰을 , "Authorization": f"Bearer {access_token}" 대입하여 추가한다.
세번째*
requests.get("https://api.github.com/user/emails",
headers={
"Authorization" : f"Bearer {access_token}",
"Accept" : "application/json"
}
)
두번째 에서 받아온 유저정보에는 이메일 정보가 없기에, 이메일 정보를 받아왔다.
Django에서는 이 3번의 절차를 거쳐서 깃허브의 유저정보를 받아와서 내 user DB에 존재유무를 확인하고 존재하면 가져오고 JWT발급, 존재하지 않으면, DB에 user정보를 기입한 뒤 JWT를 발급하는 방식을 이용했다.
JWT 발급
JWT발급은 아래 simple-jwt에서 제공하는 방식인 creating tokens manually의 코드를 이용했다.
여기서 user는
user = User.objects.get(어쩌고=저쩌고)
를 가져온 것이다.
refresh = RefreshToken.for_user(user)
return {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
로그아웃은 dj-rest-auth에 있는 endpoint를 그대로 이용했다.
`${base_url}/dj-rest-auth/logout/`
이를 이용해주어도 토큰 로그아웃이 되는 것을 확인 할 수 있었다.
수정을 하나 하긴 해야할 듯 하다.
분명 저장도 잘되는것을 확인했는데, 저장하고, 즉시 들고 오는것이 불가능한 건지 에러가발생했다.
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)