프론트 엔드는 React를 사용했고 백엔드는 Django와 Django Ninja를 사용해서 구현했다.
전에 한번 블로그에 포스팅을 한적이 있어서 자세하게 다루진 않을 예정이다.
npm install react-google-login
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>
)}
/>
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가 온다면 일단 로그인은 성공이 되었다.
로그인이 성공했으면 다 된게 아닐까 싶을텐데 Refresh Token을 처리해줘야한다. 잠시 Refresh Token에 대해 알아보면 만약 우리의 Access Token을 탈취당한다면 어떻게 될까? 아마도 누군가 토큰을 사용하여 나의 정보를 사용할 것이다.
그렇다고 Access Token의 유효기간을 짧게 지정해놓으면 아마 사용자가 일정시간이 지나면 계속 로그인을 해야하는 문제가 생긴다.
이를 해결할 수 있는 방법이 Refresh Token이다. AccessToken이 만료가 되면 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를 사용해서 구현하면 된다.