From 06feaa067945b0fccb0336fb4199bf1c9baff399 Mon Sep 17 00:00:00 2001 From: kyuran kim <57716832+gxxrxn@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:15:43 +0900 Subject: [PATCH] =?UTF-8?q?Revert=20"[#596]=20accessToken=EC=9D=B4=20?= =?UTF-8?q?=EB=A7=8C=EB=A3=8C=EB=90=9C=20=EC=A7=81=ED=9B=84=20=EB=8B=A4?= =?UTF-8?q?=EB=A5=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=ED=95=A0=20=EB=95=8C=20=EC=97=90=EB=9F=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EA=B3=A0=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20(#6?= =?UTF-8?q?00)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 59407c6a05c7e0691160e76f9fcbf044f58f33e4. --- src/apis/core/axios.ts | 46 +++++++++++-------- src/app/error.tsx | 29 ++++++++++++ src/app/global-error.tsx | 38 --------------- src/app/layout.tsx | 5 +- src/components/AuthFailedErrorBoundary.tsx | 43 ----------------- src/components/ContextProvider.tsx | 6 ++- src/components/ReactQueryProvider.tsx | 10 +--- .../customError/AuthRefreshIgnoredError.ts | 11 ----- src/types/customError/index.ts | 1 - 9 files changed, 64 insertions(+), 125 deletions(-) create mode 100644 src/app/error.tsx delete mode 100644 src/app/global-error.tsx delete mode 100644 src/components/AuthFailedErrorBoundary.tsx delete mode 100644 src/types/customError/AuthRefreshIgnoredError.ts delete mode 100644 src/types/customError/index.ts diff --git a/src/apis/core/axios.ts b/src/apis/core/axios.ts index 5f5a8b4a..25951871 100644 --- a/src/apis/core/axios.ts +++ b/src/apis/core/axios.ts @@ -1,12 +1,12 @@ import axios, { CreateAxiosDefaults, InternalAxiosRequestConfig } from 'axios'; -import { AuthRefreshIgnoredError } from '@/types/customError'; import { ACCESS_TOKEN_STORAGE_KEY, SERVICE_ERROR_MESSAGE } from '@/constants'; import { isAuthFailedError, isAuthRefreshError, isAxiosErrorWithCustomCode, } from '@/utils/helpers'; +import isClient from '@/utils/isClient'; import webStorage from '@/utils/storage'; const storage = webStorage(ACCESS_TOKEN_STORAGE_KEY); @@ -38,6 +38,10 @@ const requestHandler = (config: InternalAxiosRequestConfig) => { return config; }; +/** api 요청이 병렬적으로 이뤄질 때, + * 토큰 업데이트는 한번만 요청하기 위해 사용되는 flag 변수 */ +let isRefreshing = false; + const responseHandler = async (error: unknown) => { if (isAxiosErrorWithCustomCode(error)) { const { config: originRequest, response } = error; @@ -46,7 +50,7 @@ const responseHandler = async (error: unknown) => { console.warn(code, message); - if (originRequest && isAuthRefreshError(code)) { + if (originRequest && isAuthRefreshError(code) && !isRefreshing) { return silentRefresh(originRequest); } @@ -62,39 +66,45 @@ const responseHandler = async (error: unknown) => { const silentRefresh = async (originRequest: InternalAxiosRequestConfig) => { try { + isRefreshing = true; + const newToken = await updateToken(); storage.set(newToken); setAxiosAuthHeader(originRequest, newToken); + isRefreshing = false; + return await publicApi(originRequest); } catch (error) { removeToken(); + isRefreshing = false; + return Promise.reject(error); } }; -/** api 요청이 병렬적으로 이뤄질 때, - * 토큰 업데이트는 한번만 요청하기 위해 사용되는 flag 변수 */ -let isTokenRefreshing = false; +const updateToken = async () => { + try { + const { + data: { accessToken }, + } = await axios.post<{ accessToken: string }>('/service-api/auth/token'); -const updateToken = () => - new Promise((resolve, reject) => { - if (isTokenRefreshing) { - reject(new AuthRefreshIgnoredError('Already trying to refresh token')); - return; + if (!accessToken) { + throw new Error('새로운 accessToken을 받아오지 못했어요.'); } - isTokenRefreshing = true; - - axios - .post<{ accessToken: string }>('/service-api/auth/token') - .then(({ data }) => resolve(data.accessToken)) - .catch(reason => reject(reason)) - .finally(() => (isTokenRefreshing = false)); - }); + return accessToken; + } catch (error) { + return Promise.reject(error); + } +}; const removeToken = () => { storage.remove(); + + if (isClient()) { + window.location.reload(); + } }; const setAxiosAuthHeader = ( diff --git a/src/app/error.tsx b/src/app/error.tsx new file mode 100644 index 00000000..f2c65513 --- /dev/null +++ b/src/app/error.tsx @@ -0,0 +1,29 @@ +'use client'; + +import Button from '@/v1/base/Button'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; + +export const ErrorPage = () => { + const router = useRouter(); + + return ( +
+ loading +
+ 다독이도 몰라요~ 왜 + 이래요~ +
+ +
+ ); +}; + +export default ErrorPage; diff --git a/src/app/global-error.tsx b/src/app/global-error.tsx deleted file mode 100644 index c11a3ef5..00000000 --- a/src/app/global-error.tsx +++ /dev/null @@ -1,38 +0,0 @@ -'use client'; - -import Button from '@/v1/base/Button'; -import Image from 'next/image'; -import { useRouter } from 'next/navigation'; - -export const ErrorPage = () => { - const router = useRouter(); - - return ( - - -
- loading -
- 다독이도 몰라요~ 왜 - 이래요~ -
- -
- - - ); -}; - -export default ErrorPage; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8e19e35d..4c2ed7e6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,4 @@ import ContextProvider from '@/components/ContextProvider'; -import AuthFailedErrorBoundary from '@/components/AuthFailedErrorBoundary'; import Layout from '@/v1/layout/Layout'; import { LineSeedKR } from '@/styles/font'; @@ -19,9 +18,7 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => { {/* @todo Chakra 제거시 app-layout 프로퍼티 제거. */} - - {children} - + {children} diff --git a/src/components/AuthFailedErrorBoundary.tsx b/src/components/AuthFailedErrorBoundary.tsx deleted file mode 100644 index 3274a7c2..00000000 --- a/src/components/AuthFailedErrorBoundary.tsx +++ /dev/null @@ -1,43 +0,0 @@ -'use client'; - -import { useEffect } from 'react'; -import { QueryErrorResetBoundary } from '@tanstack/react-query'; -import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; - -import useToast from '@/v1/base/Toast/useToast'; -import { isAuthFailedError, isAxiosErrorWithCustomCode } from '@/utils/helpers'; -import Loading from '@/v1/base/Loading'; - -const AuthFailedErrorBoundary = ({ - children, -}: { - children?: React.ReactNode; -}) => { - return ( - - {({ reset }) => ( - - {children} - - )} - - ); -}; - -export default AuthFailedErrorBoundary; - -const AuthFailedFallback = ({ error, resetErrorBoundary }: FallbackProps) => { - const { show: showToast } = useToast(); - - useEffect(() => { - if ( - isAxiosErrorWithCustomCode(error) && - isAuthFailedError(error.response.data.code) - ) { - showToast({ message: '다시 로그인 해주세요' }); - resetErrorBoundary(); - } - }, [error, resetErrorBoundary, showToast]); - - return ; -}; diff --git a/src/components/ContextProvider.tsx b/src/components/ContextProvider.tsx index 0bef940a..68edb417 100644 --- a/src/components/ContextProvider.tsx +++ b/src/components/ContextProvider.tsx @@ -1,8 +1,10 @@ 'use client'; import { ReactNode } from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; import { RecoilRoot } from 'recoil'; +import ErrorPage from '@/app/error'; import ChakraThemeProvider from '@/components/ChakraThemeProvider'; import ReactQueryProvider from '@/components/ReactQueryProvider'; import ToastProvider from '@/v1/base/Toast/ToastProvider'; @@ -12,7 +14,9 @@ const ContextProvider = ({ children }: { children: ReactNode }) => { - {children} + + {children} + diff --git a/src/components/ReactQueryProvider.tsx b/src/components/ReactQueryProvider.tsx index 1871e422..fff2f458 100644 --- a/src/components/ReactQueryProvider.tsx +++ b/src/components/ReactQueryProvider.tsx @@ -1,4 +1,3 @@ -import AuthRefreshIgnoredError from '@/types/customError/AuthRefreshIgnoredError'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { NextPage } from 'next/types'; @@ -15,18 +14,11 @@ const ReactQueryProvider: NextPage = ({ children }) => { defaultOptions: { queries: { refetchOnWindowFocus: false, - retry: (_count, error) => { - if (error instanceof AuthRefreshIgnoredError) { - return true; - } - - return false; - }, + retry: false, }, }, }) ); - return ( diff --git a/src/types/customError/AuthRefreshIgnoredError.ts b/src/types/customError/AuthRefreshIgnoredError.ts deleted file mode 100644 index b85f0478..00000000 --- a/src/types/customError/AuthRefreshIgnoredError.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * accessToken을 갱신하는 요청이 진행 중인 경우, 갱신 요청은 무시되고 해당 에러가 발생합니다. - */ -class AuthRefreshIgnoredError extends Error { - constructor(message: string) { - super(message); - this.name = this.constructor.name; - } -} - -export default AuthRefreshIgnoredError; diff --git a/src/types/customError/index.ts b/src/types/customError/index.ts deleted file mode 100644 index 176fe8d4..00000000 --- a/src/types/customError/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as AuthRefreshIgnoredError } from './AuthRefreshIgnoredError';