React + Django (Django Ninja) Google Login

DARTZ·2022년 5월 29일
1

React

목록 보기
1/14

기술 스택

  • React 18
  • Django(Wagtail)
  • Django Ninja

프론트 엔드는 React를 사용했고 백엔드는 Django와 Django Ninja를 사용해서 구현했다.

구현 방법

  1. 구글 클라우드 플랫폼에서 클라이언트 ID와 Secret Key를 생성한다.
  2. FrontEnd에서 Google에 클라이언트 ID를 보내서 JWT(Json Web Token)을 받아온다.
  3. 받아온 JWT와 User 정보를 쿠키에 저장해놓는다.
  4. JWT를 Header에 담아서 Backend로 보낸다.
  5. Backend에서 토큰을 해석하고 유효한 토큰이면 인증을 허가 해준다. (Authentication)
  6. JWT 토큰이 만료가 되면 Backend 에서 401(권한 없음)오류를 Response 해주는데 이를 FrontEnd에서 Axios Interceptor를 통해 자동으로 Refresh Token을 보내준다.

구현

구글 클라이언트 ID 생성

구글 클라우드 플랫폼
참고 블로그

전에 한번 블로그에 포스팅을 한적이 있어서 자세하게 다루진 않을 예정이다.

Front End 코드 작성

1. React Google Login 라이브러리 설치
npm install react-google-login
2. 라이브러리 사용

import GoogleLogin from 'react-google-login';

...


      <GoogleLogin
        clientId="클라이언트 ID"
        onSuccess={loginSuccess}
        onFailure={loginFail}
        cookiePolicy={'single_host_origin'}
        render={(renderProps) => (
          <StyledButton onClick={renderProps.onClick} style={LoginButtonCss}>
            <GoogleLogo />
            <StyledGoogle>Google로 로그인</StyledGoogle>
          </StyledButton>
        )}
      />
3. 함수 구현
import { useCookies } from 'react-cookie';

...

const Login = () => {
  const navigate = useNavigate();
  const [cookies, setCookie] = useCookies(['AccessToken', 'RefreshToken', 'UserInfo']);

  const loginSuccess = async (response: any) => {
    const res = await fetch('토큰을 보낼 백엔드 주소', {
      method: 'POST',
      body: JSON.stringify({
        access_token: response.accessToken,
      }),
      headers: {
        'Content-Type': 'application/json',
      },
    });
    const data = await res.json();
    setCookie('AccessToken', data.access_token);
    setCookie('RefreshToken', data.refresh_token);
    setCookie('UserInfo', data.user);
    navigate('/');
  };

  const loginFail = () => {
    window.alert('로그인 실패했습니다. 관리자에게 문의해주세요.');
  };
}

글쓴이는 사실 리액트를 잘 모른다. 프로젝트에서 구현한 구글 로그인을 정리해보면서 글을 쓰기 때문에 React 코드의 수정이 필요할 것으로 보인다. react cookie를 사용하여 백엔드에서 토큰을 보내서 받아온 Response 값 -> AccessToken, RefreshToken, UserInfo을 Cookie에 저장 했다.

쿠키 사용을 위해 react-cookie를 설치해주자.

npm i react-cookie

Google Login과 관련된 글이기 때문에 Cookie에 관해서는 따로 다루지 않도록 하겠다.
정상적으로 백엔드에서 Response가 온다면 일단 로그인은 성공이 되었다.

4. Refresh Token 처리하기

로그인이 성공했으면 다 된게 아닐까 싶을텐데 Refresh Token을 처리해줘야한다. 잠시 Refresh Token에 대해 알아보면 만약 우리의 Access Token을 탈취당한다면 어떻게 될까? 아마도 누군가 토큰을 사용하여 나의 정보를 사용할 것이다.

그렇다고 Access Token의 유효기간을 짧게 지정해놓으면 아마 사용자가 일정시간이 지나면 계속 로그인을 해야하는 문제가 생긴다.

이를 해결할 수 있는 방법이 Refresh Token이다. AccessToken이 만료가 되면 Refresh Token을 통해 새로운 Access Token을 발급받아서 로그인을 지속하는 것이다.

흐름은 다음과 같다.

  1. 만약 Access Token이 만료가 된다면 서버에서는 보통 401오류 (권한 없음)를 전달한다.
  2. React에서는 Axios Interceptor를 사용하여 만약 401오류가 발생하면 Refresh Token을 전송하여 새로운 Access Token을 발급받는다.

정말 간단한 흐름이다. 이를 코드로 구현하면 다음과 같다.

import axios from 'axios';
import { Cookies } from 'react-cookie';

const cookies = new Cookies();

const BASE_URL = '백엔드 주소';

const axiosInstance = axios.create({
  baseURL: `${BASE_URL}`,
  timeout: 5000,
});

axiosInstance.interceptors.request.use(
  function (config: any) {
    const accessToken = cookies.get('AccessToken');
    if (accessToken) {
      config.headers['Content-Type'] = 'application/json';
      config.headers['Authorization'] = `Bearer ${accessToken}`;
    }

    return config;
  },
  function (error) {
    return Promise.reject(error);
  }
);

axiosInstance.interceptors.response.use(
  function (response) {
    return response.data;
  },
  async (error) => {
    const originalRequest = error.config;

    if (error.response.status === 401) {
      const RefreshToken = await cookies.get('RefreshToken');

      const { data } = await axios.post(`${BASE_URL}api/token/refresh/`, {
        refresh: RefreshToken,
      });

      const newAccessToken = data.access;
      const newRefreshToken = data.refresh;

      await cookies.set('AccessToken', newAccessToken, {
        path: '/',
        secure: true,
        sameSite: false,
      });
      await cookies.set('RefreshToken', newRefreshToken, {
        path: '/',
        secure: true,
        sameSite: false,
      });
      originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;

      const retryOriginalRequest = new Promise((resolve) => {
        resolve(axiosInstance(originalRequest));
      });

      return retryOriginalRequest;
    }

    return Promise.reject(error);
  }
);

export default axiosInstance;

로그인을 위해 기존 Axios에서 Instance를 만들어서 사용한다.
(Bearer 방식을 사용할 때에는 Token값 앞에 'Bearer 토큰' 형식으로 보내야한다.)

이제 API 통신에서 Login기능을 사용하고 싶을 때, AxiosInstance를 사용해서 구현하면 된다.

profile
사람들이 비용을 지불하고 사용할 만큼 가치를 주는 서비스를 만들고 싶습니다.

0개의 댓글