Skip to content

Commit

Permalink
[FEAT] OAuth 로그인 callback 로딩 스피너 구현 (#172)
Browse files Browse the repository at this point in the history
* feat: OAuth 로그인 상황 callback 로딩 스피너 구현

* fix: api 호출 axios로 전환

* fix: 코드리뷰 반영 및 build에러 수정
  • Loading branch information
haejinyun authored Dec 9, 2024
1 parent 071bd70 commit ed3fbba
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 40 deletions.
7 changes: 7 additions & 0 deletions src/apis/queryFunctions/Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
PostRegisterResponseData,
PostEmailAuthParams,
CheckEmailAuthParams,
LoginParams,
PostLoginResponseData,
} from '@/apis/types/Auth';
import { Response } from '@/apis/types/common';

Expand All @@ -29,3 +31,8 @@ export const postEmailValidationCode = async (params: CheckEmailAuthParams) => {
const response = await apiClient.post<Response<boolean>>('v2/mail/code', params);
return response.data;
};

export const postLogin = async (params: LoginParams) => {
const response = await apiClient.post<Response<PostLoginResponseData>>('v2/auth/login', params);
return response.data;
};
11 changes: 9 additions & 2 deletions src/apis/queryFunctions/apiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ function createApiClient() {
},
async error => {
const refreshToken = localStorage.getItem('refreshToken');

const originalRequest = error.config;

if (error.response?.status === 401) {
Expand All @@ -60,10 +59,18 @@ function createApiClient() {
originalRequest.headers.Authorization = newAccessToken;
return await client(originalRequest);
}

console.error('refreshToken도 만료되었습니다.');
alert('재로그인이 필요합니다.');
localStorage.removeItem('refreshToken');
sessionStorage.removeItem('accessToken');
window.location.href = '/login';
} catch (e) {
console.error('Failed to reissue token:', e);
alert('재로그인이 필요합니다.');
console.error('accessToken 재발급 실패');
localStorage.removeItem('refreshToken');
sessionStorage.removeItem('accessToken');
window.location.href = '/login';
}
}
}
Expand Down
34 changes: 0 additions & 34 deletions src/apis/queryFunctions/setTokenCookies.ts

This file was deleted.

8 changes: 4 additions & 4 deletions src/apis/queryHooks/Auth/useLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useMutation } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { useRouter } from 'next/navigation';

import setTokenCookies from '@/apis/queryFunctions/setTokenCookies';
import { postLogin } from '@/apis/queryFunctions/Auth';
import { LoginParams } from '@/apis/types/Auth';
import { Response } from '@/apis/types/common';
import { useAuth } from '@/provider/authProvider';
Expand All @@ -17,10 +17,10 @@ function useLogin() {
const { setIsLoggedIn } = useAuth();

const { mutate } = useMutation({
mutationFn: (loginParams: LoginParams) => setTokenCookies(loginParams),
mutationFn: (loginParams: LoginParams) => postLogin(loginParams),
onSuccess: data => {
sessionStorage.setItem('accessToken', data.accessToken);
localStorage.setItem('refreshToken', data.refreshToken);
sessionStorage.setItem('accessToken', data.result_data.access_token);
localStorage.setItem('refreshToken', data.result_data.refresh_token);
setIsLoggedIn(true);
showToast('success', '로그인 되었습니다.');

Expand Down
50 changes: 50 additions & 0 deletions src/app/oauthcallback/OauthCallbackHandler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client';

import { useEffect } from 'react';

import { useRouter, useSearchParams } from 'next/navigation';

import LoadingSpinner from '@/components/LoadingSpinner';
import { useAuth } from '@/provider/authProvider';
import { useToast } from '@/provider/toastProvider';

function OauthCallbackHandler() {
const router = useRouter();

const { showToast } = useToast();
const { setIsLoggedIn } = useAuth();

const searchParams = useSearchParams();

useEffect(() => {
const accessToken = searchParams.get('access_token');
const refreshToken = searchParams.get('refresh_token');

const handleLogin = () => {
if (accessToken && refreshToken) {
sessionStorage.setItem('accessToken', accessToken);
localStorage.setItem('refreshToken', refreshToken);
setIsLoggedIn(true);
showToast('success', '로그인 되었습니다.');

router.push('/');
} else {
localStorage.removeItem('refreshToken');
sessionStorage.removeItem('accessToken');
showToast('error', '로그인에 실패하였습니다.');
router.push('/login');
}
};

const timer = setTimeout(handleLogin, 500);

return () => clearTimeout(timer);
}, [router, searchParams, setIsLoggedIn, showToast]);
return (
<div>
<LoadingSpinner />
</div>
);
}

export default OauthCallbackHandler;
17 changes: 17 additions & 0 deletions src/app/oauthcallback/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';

import { Suspense } from 'react';

import OauthCallbackHandler from './OauthCallbackHandler';

// TODO 로그인을 이미 했다면 로그인 페이지로 못들어가게 하기

function Oauthcallback() {
return (
<Suspense fallback="OauthCallbackHandler">
<OauthCallbackHandler />
</Suspense>
);
}

export default Oauthcallback;
25 changes: 25 additions & 0 deletions src/components/LoadingSpinner/LoadingSpinner.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { style, keyframes } from '@vanilla-extract/css';

import colors from '@/styles/color';

const spin = keyframes({
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' },
});

export const spinnerContainer = style({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
width: '100%',
});

export const spinner = style({
width: '48px',
height: '48px',
border: '6px solid #f3f3f3',
borderTop: `6px solid ${colors.primary9}`,
borderRadius: '50%',
animation: `${spin} 1s linear infinite`,
});
11 changes: 11 additions & 0 deletions src/components/LoadingSpinner/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as S from './LoadingSpinner.css';

function LoadingSpinner() {
return (
<div className={S.spinnerContainer}>
<div className={S.spinner} />
</div>
);
}

export default LoadingSpinner;

0 comments on commit ed3fbba

Please sign in to comment.