본문 바로가기
IT관련/Python

Django와 React를 이용해서 github 로그인을 해봅시다 !(OAuth) 2편

728x90

 

https://heokknkn.tistory.com/54

지난편은 여기서 확인해주세요 !!!! 

자 ! 저번에 밑에와 같은 화면에서 끝났는데요 !! 바로 이어서 시작해보겠습니다 ! 

아래에서는 리액트 쿼리에 대한것들도 있는데 이 부분끝나고 바로 다뤄볼게요 !!!! 

 

딱 필요한 부분만 가져와서 설명하겠습니다 ! 

먼저 저희는 code가 필요합니다 ! 어떻게 가지고 올까요?

useLocation()와 URLSearchParams, code를 이용해서 손쉽게 code를 빼올 수 있습니다. 

code를 가져오는 방법은 다양하지만 저는 이 방법으로 가져왔어요 !! 

import { useLocation, useNavigate } from "react-router";

export default function GithubConfirm() {
  const { search } = useLocation();

  const confirmLogin = async () => {
    const params = new URLSearchParams(search);
    const code = params.get("code");
    if (code) {
      mutation.mutate(code);
    }
  };
  useEffect(() => {
    confirmLogin();
  }, []);
}

차례대로 search, params, code입니다

자 ! 저렇게 해주시고 mutation을 알아봐야겠죠?? 

 

전 요번에 tanstack를 이용해서 useMuation기능을 이용했습니다 !!! 사용해보니 너무 편하더라구요 !! 

useMutation을 이용해서 성공하면 onSuccess를 ! 실패하면 onError을 !!! 

기본적으로 useMutation는 

useMutation(mutationFn, options);

mutationFn (variables: TVariables) => Promise<TData>

Required 필수 ! 
비동기 작업을 수행하고 프로미스를 반환하는 함수입니다 ! (쉽게 말해 api 요청하는 함수)

이정도만 알고 넘어가주세요 이번편에서는요 ! 

 

혹시라도 위에처럼 안하시거나 그러시다면 그냥 비동기를 이용해서 서버단으로 request를 해서 state를 관리하셔도 괜찮습니다 !!

또한 useQueryClient를 이용해서 useMuation에서 성공을 하면 refetchQueryies를 해줘서 서버단에서 가져온 정보를 바로 넣어줍니당 ! 또한 성공하고 나서는 redirect도 시켜주죠 !! 

다음에 사용 방법에 대해서 다뤄보려고 하는데 혹시라도 지금 당장 위에 부분은 이해 안가시면 댓글 달아주세요 !!!! 

import { useMutation, useQueryClient } from "@tanstack/react-query";
import { githubLogIn } from "../api";
import { useToast } from "@chakra-ui/react";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router";

export default function GithubConfirm() {
  const toast = useToast();
  const queryClient = useQueryClient();
  const navigate = useNavigate();
  const { reset } = useForm();
  
  const mutation = useMutation(githubLogIn, {
    onSuccess: (data) => {
      toast({
        status: "success",
        title: " Welcome!!!",
        description: "Happy to have you back!!",
        position: "bottom-right",
      });
      reset();
      queryClient.refetchQueries(["me"]);
      navigate("/");
    },
    onError: (error) => {
      console.log("error");
      toast({
        status: "error",
        title: "Login fail",
        description: "Check your Github information",
        position: "bottom-right",
      });
    },
  });
}

 

GibhubLogin

export const githubLogIn = (code: string) => {
  return axios
    .post(`http://127.0.0.1:8000/api/v1/users/github`, { code })
    .then((response) => response.status);
};

 

자 여기까지가 프론트입니당 !! 

그러면 백엔드로 넘어가봅시다 ! 

 


먼저 저렇게 url하나 만들어주시구요 !!! 전 참고로 ! http://127.0.0.1:8000/api/v1/users/github 이런식입니당 ! 

 

먼저 위에 공식문서를 봐주셔야합니다 !! 

저희는 서버단으로 code를 넘겨줬습니다 !! 이것을 가지고 다시 서버쪽에서는 POST로 github에 요청을 보내야하죠 !!! 

https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps

요기 참고 ! 

저기서 가장 중요한것은 client_id, client_secret, code 입니당 ! 

client_id란 code는 저번에 구해오는것을 알았는데 client_sercret는 어디서 할까요???

redirect_uri는 해주시려면 해도 되고 안해도 됩니다 ^^ ( 저는 프론트에서 할거라서요 ! )

 

자 ! 저번에 만들었던 곳에서 할 수 잇습니다 !! 

한번 다운로드 받고 새로고침이라던가 다시 돌아오면 없어지니 

서버쪽에서 env파일로 관리하면 좋아요 !! 

자 이렇게 하면 준비물은 끝 !!! 

그럼 전체적인 코드를 봐볼까요? 

def post(self, request):
        try:
            code = request.data.get("code") # 프론트에서 보내준 code로 access_token을 구해와야한다 !! 
            access_token = requests.post(
                f"https://github.com/login/oauth/access_token?code={code}&client_id=0d776929a7852cb9c693&
                client_secret=ca4ee34e324d4c87bb94e6c499029e4512a5e71c",
                headers={"Accept": "application/json"},
            )
            access_token = access_token.json().get("access_token")
            # 밑에는 기본적인 user data ( not include email)
            user_data = requests.get(
                "https://api.github.com/user",
                headers={
                    "Authorization": f"Bearer {access_token}",
                    "Accept": "application/json",
                },
            )
            user_data = user_data.json()
            # 위에서 email이 없었으니 밑에서 불러오자 ! 
            user_emails = requests.get(
                "https://api.github.com/user/emails",
                headers={
                    "Authorization": f"Bearer {access_token}",
                    "Accept": "application/json",
                },
            )
            user_emails = user_emails.json()
            
            try:
                user = User.objects.get(email=user_emails[0]["email"])
                login(request, user)
                return Response(status=status.HTTP_200_OK)
            except User.DoesNotExist:
                user = User.objects.create(
                    username=user_data.get("login"),
                    email=user_emails[0]["email"],
                    name=user_data.get("name"),
                    avatar=user_data.get("avatar_url"),
                )
                user.set_unusable_password()    # github로그인이니까 No password!
                user.save()
                login(request, user)
                return Response(status=status.HTTP_200_OK)
        except Exception:
            return Response(status=status.HTTP_400_BAD_REQUEST)

 

이렇게 짧게 끝납니다 ! 그러면 하나씩 알아보겠습니다 ! 

우선 이 부분입니다 !

위에 공식문서에서도 봤듯이 code, client_id, client_secret는 필수입니다 ! 

참고로 !! client_secret는 정말 중요한 키니까 꼭 안보이게 해주세요 !!! (env파일이라던가 준비해주세요 !!! 전 공개한 이유가 어차피 사용하고 지울거라서요 ! ) 즉 ! 밑에 주석친것처럼 저런식으로요 ^^ 

그렇게 하고 headersjson형식으로 해줘서 response 받을 때 json형식으로 받자구요 ! 

code = request.data.get("code") # 프론트에서 보내준 code로 access_token을 구해와야한다 !! 
access_token = requests.post(
	f"https://github.com/login/oauth/access_token?code={code}&client_id=0d776929a7852cb9c693&
    client_secret=ca4ee34e324d4c87bb94e6c499029e4512a5e71c",
    #f"https://github.com/login/oauth/access_token?code={code}&client_id=0d776929a7852cb9c693&
    client_secret={settings.GH_LOCAL_SECRET}",
	headers={"Accept": "application/json"},
)

 

자 ! 위에서 access_token을 가지고 왔으니 

이번엔 그것을 가지고 user_data를 가져와봅시다 !!! 

특히 Authorization 부분은 꼭 저렇게 해주세요 !!! 

아래에선 requests를 꼭 import해주시구요 ! 

access_token = access_token.json().get("access_token")
# 밑에는 기본적인 user data ( not include email)
user_data = requests.get(
	"https://api.github.com/user",
	headers={
            "Authorization": f"Bearer {access_token}",
            "Accept": "application/json",
    },
)

이 부분 참고 !!

그리고 나서 한번 로그를 찍어보세요 !! 엄청나게 많은 데이터를 가지고 온답니다 !!! 

중요한 정보도 있으니 사진으로는 남기진 않을게요 ㅜㅜ 

 

하지만 ! 저는 이메일을 찾아야합니다 !!! 이메일을 가지고 유저 체크를 하기 위해서입니다 !!! 

github의 경우에는 따로 email을 찾는 코드를 추가해야합니다 ! 

밑에처럼 해주세요 ! 

user_emails = requests.get(
	"https://api.github.com/user/emails",
	headers={
            "Authorization": f"Bearer {access_token}",
            "Accept": "application/json",
    },
)

user_emails = user_emails.json()

 

자 ! 나머진 기존에 장고방식대로 로그인을 하는것입니다 ! 

유저가 있으면 로그인 시키고 없으면 회원가입을 시키는것이죠 ! 필요한거만 가지구요 !!! 

그리고 또한 유의할 점이 ! 어디까지나 github로 회원가입 및 로그인을 시키는거니까 password가 필요없겠죠??? 

그래서 아래처럼 set_unusable_password를 해줬습니다 ! 

try:
    user = User.objects.get(email=user_emails[0]["email"])
    login(request, user)
    return Response(status=status.HTTP_200_OK)
except User.DoesNotExist:
    user = User.objects.create(
        username=user_data.get("login"),
        email=user_emails[0]["email"],
        name=user_data.get("name"),
        avatar=user_data.get("avatar_url"),
    )
    user.set_unusable_password()    # github로그인이니까 No password!
    user.save()
    login(request, user)
    return Response(status=status.HTTP_200_OK)

 

자 이제 끝입니다 !! 고생하셨습니다 ㅜㅜㅜㅜㅜ

그러면 이제 화면에서만 한번 확인해볼까요??? 

 

먼저 장고쪽 admin쪽입니다 ! 

그럼 회원가입 버튼을 누르고 다시 이 화면을 본다면 ? 

 

전 참고로 프론트쪽에서 onsuccess하면 리다이랙트로 home화면으로 가게 합니당 ^^

 

짠 !!! 유저 추가 완료 !!!

 

다들 너무 고생하셨습니다 !! 길긴 했지만 생각보다 쉬웠다고 생각합니다 !! 

 

마지막으로 정리만 하고 넘어갑시당 !!! 

첫번째 https://github.com/login/oauth/authorize 여기로 client_id와 scope를 사용해서 1회용성 code를 가져와야합니다 ! 
두번째 서버에서 https://github.com/login/oauth/access_token 여기로 code를 이용해서 access_token을 구해야합니다 !
세번째 https://api.github.com/user이쪽으로 access_token을 가지고 user_date 및 이메일을 구해서 사용하세요 ! 

 

자 ! 이제 손쉽게 자신의 사이트에서도 gibhub로그인을 사용해보자구요 !!! ^^ 

 

그리고 여기서 처음 나온 

카카오 로그인이나 리액트 쿼리, env파일 만들고 사용하기 같은것들은 추후에 쓸거니 기대해주세요 !!! 

728x90