Skip to content

Commit

Permalink
Merge pull request #167 from dnd-side-project/feat-166
Browse files Browse the repository at this point in the history
[# 166] 리프레쉬 토큰을 이용한 액세스 토큰 재갱신 기능 구현
  • Loading branch information
gihwan-dev authored Aug 31, 2024
2 parents e053aaf + f88ed1e commit eebef94
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 19 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ module.exports = {
'react/react-in-jsx-scope': 0,
'react-refresh/only-export-components': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'react-hooks/exhaustive-deps': 'off',
},
};
11 changes: 11 additions & 0 deletions src/apis/auth/refreshAccessToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { baseAxios } from '@/libs/baseAxios.ts';

export interface RefreshAccessTokenResponse {
accessToken: string;
expiresIn: number;
refreshToken: string;
refreshTokenExpiresIn: number;
}

export const refreshAccessToken = async (refreshToken: string) =>
baseAxios.post<RefreshAccessTokenResponse>('/token', { refreshToken });
1 change: 0 additions & 1 deletion src/components/register/RegionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const RegionList = ({ onSelectLocation, search }: RegionListProps) => {
setSearch(search);
};

// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedUpdateSearch = useCallback(debounce(updateSearch, 300), []);

useEffect(() => {
Expand Down
54 changes: 54 additions & 0 deletions src/hooks/useToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useCookies } from 'react-cookie';
import { useCallback } from 'react';

interface SetAccessTokenParams {
token: string;
expiresIn: number;
}

interface SetRefreshTokenParams {
token: string;
expiresIn: number;
}

export const useToken = () => {
const [cookies, setCookie] = useCookies(['access-token', 'refresh-token']);

const setAccessToken = useCallback(
({ token, expiresIn }: SetAccessTokenParams) => {
setCookie('access-token', token, {
expires: new Date(Date.now() + expiresIn),
});
},
[setCookie],
);

const setRefreshToken = useCallback(
({ token, expiresIn }: SetRefreshTokenParams) => {
setCookie('refresh-token', token, {
expires: new Date(Date.now() + expiresIn),
});
},
[setCookie],
);

const getRefreshToken = useCallback(() => {
return cookies['refresh-token'];
}, [cookies]);

const getAccessToken = useCallback(() => {
return cookies['access-token'];
}, [cookies]);

const isValidToken = useCallback((token?: any): token is string => {
return typeof token === 'string';
}, []);

return {
setAccessToken,
setRefreshToken,
getRefreshToken,
getAccessToken,
isValidToken,
};
};
6 changes: 6 additions & 0 deletions src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { myPlantDetail } from '@/mocks/mockDatas/myPlantDetail.ts';
import { randomSigninData } from '@/mocks/mockDatas/randomSigninData.ts';
import { regions } from '@/mocks/mockDatas/regions.ts';
import { weathers } from '@/mocks/mockDatas/weatherMessage.ts';
import { refreshAccessTokenResponseData } from '@/mocks/mockDatas/refreshAccessToken.ts';

export const handlers = [
http.post(import.meta.env.VITE_API_URL + '/plants', async ({ request }) => {
Expand Down Expand Up @@ -130,4 +131,9 @@ export const handlers = [

return HttpResponse.json(weathers);
}),

http.post(import.meta.env.VITE_API_URL + '/token', async () => {
await delay(1000);
return HttpResponse.json(refreshAccessTokenResponseData);
}),
];
6 changes: 6 additions & 0 deletions src/mocks/mockDatas/refreshAccessToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const refreshAccessTokenResponseData = {
accessToken: 'accessToken',
expiresIn: 21599,
refreshToken: 'refreshToken',
refreshTokenExpiresIn: 2159900,
};
19 changes: 9 additions & 10 deletions src/pages/LoginRedirectPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { useSearchParams } from 'react-router-dom';
import { useEffect } from 'react';
import { useSignIn } from '@/queries/useSignIn.ts';
import useInternalRouter from '@/hooks/useInternalRouter.ts';
import { useCookies } from 'react-cookie';
import LoadingSpinner from '@/components/LoadingSpinner.tsx';
import { SECOND } from '@/constants/day.ts';
import { useToken } from '@/hooks/useToken.ts';

const LoginRedirectPage = () => {
const router = useInternalRouter();
const [, setCookie] = useCookies(['access-token', 'refresh-token']);
const { setAccessToken, setRefreshToken } = useToken();
const { mutate: signIn } = useSignIn();
const [searchParams] = useSearchParams();
const code = searchParams.get('code');
Expand All @@ -25,16 +25,15 @@ const LoginRedirectPage = () => {
},
{
onSuccess: (response) => {
const currentDate = new Date();
switch (response.data.status) {
case 'success':
setCookie('access-token', response.data.accessToken, {
expires: new Date(currentDate.getTime() + response.data.expiresIn * SECOND),
setAccessToken({
token: response.data.accessToken,
expiresIn: response.data.expiresIn * SECOND,
});
setCookie('refresh-token', response.data.refreshToken, {
expires: new Date(
currentDate.getTime() + response.data.refreshTokenExpiresIn * SECOND,
),
setRefreshToken({
token: response.data.refreshToken,
expiresIn: response.data.refreshTokenExpiresIn * SECOND,
});
router.replace('/');
break;
Expand All @@ -48,7 +47,7 @@ const LoginRedirectPage = () => {
},
},
);
}, [code, signIn, router, setCookie]);
}, [code, signIn, router, setAccessToken, setRefreshToken]);

return <LoadingSpinner />;
};
Expand Down
24 changes: 24 additions & 0 deletions src/pages/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ import { useGetHomeData } from '@/queries/useGetHomeData.ts';
import { withAsyncBoundary } from '@toss/async-boundary';
import ErrorPage from '@/pages/ErrorPage.tsx';
import LoadingSpinner from '@/components/LoadingSpinner.tsx';
import { useRefreshAccessToken } from '@/queries/useRefreshAccessToken.ts';
import { useToken } from '@/hooks/useToken.ts';
import { SECOND } from '@/constants/day.ts';

const Main = () => {
const { openToast } = useToast();
const { data: homeData } = useGetHomeData();
const { mutate: refreshAccessToken } = useRefreshAccessToken();
const { getRefreshToken, isValidToken, setRefreshToken, setAccessToken } = useToken();

const register = homeData.myPlantInfo.length !== 0;

Expand Down Expand Up @@ -43,6 +48,25 @@ const Main = () => {
void requestPermission();
}, [requestPermission]);

useEffect(() => {
const refreshToken = getRefreshToken();
if (isValidToken(refreshToken)) {
refreshAccessToken(refreshToken, {
onSuccess: (response) => {
const { accessToken, refreshToken, expiresIn, refreshTokenExpiresIn } = response.data;
setAccessToken({
token: accessToken,
expiresIn: expiresIn * SECOND,
});
setRefreshToken({
token: refreshToken,
expiresIn: refreshTokenExpiresIn * SECOND,
});
},
});
}
}, []);

return (
<Screen className="bg-Gray50 min-h-dvh">
<MyPlant
Expand Down
15 changes: 8 additions & 7 deletions src/pages/RegisterPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { NumericRange } from '@/types/NewmericRange.ts';
import CompleteFunnel from '@/funnel/register/CompleteFunnel.tsx';
import { useRegisterUser } from '@/queries/useRegisterUser.ts';
import useInternalRouter from '@/hooks/useInternalRouter.ts';
import { useCookies } from 'react-cookie';
import useToast from '@/hooks/useToast.tsx';
import LoadingSpinner from '@/components/LoadingSpinner.tsx';
import { SECOND } from '@/constants/day.ts';
import { useToken } from '@/hooks/useToken.ts';

type RegisterForm = {
nickname: string;
Expand All @@ -19,7 +19,7 @@ type RegisterForm = {
};

const RegisterPage = () => {
const [, setCookie] = useCookies(['access-token', 'refresh-token']);
const { setRefreshToken, setAccessToken } = useToken();
const [registerForm, setRegisterForm] = useState<RegisterForm>({
nickname: '',
location: '',
Expand Down Expand Up @@ -50,13 +50,14 @@ const RegisterPage = () => {
{ ...data, registerToken },
{
onSuccess: (response) => {
const currentDate = new Date();
setStep('완료하기');
setCookie('access-token', response.data.accessToken, {
expires: new Date(currentDate.getTime() + response.data.expiresIn * SECOND),
setAccessToken({
token: response.data.accessToken,
expiresIn: response.data.expiresIn * SECOND,
});
setCookie('refresh-token', response.data.refreshToken, {
expires: new Date(currentDate.getTime() + response.data.refreshTokenExpiresIn * SECOND),
setRefreshToken({
token: response.data.refreshToken,
expiresIn: response.data.refreshTokenExpiresIn * SECOND,
});
},
onError: () => {
Expand Down
1 change: 0 additions & 1 deletion src/pages/SearchPlantPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ interface SearchPlantPageProps {
const SearchPlantPage = ({ onClose }: SearchPlantPageProps) => {
const [query, setQuery] = useState('');

// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedSetQuery = useCallback(debounce(setQuery, 500), []);

return (
Expand Down
1 change: 1 addition & 0 deletions src/queries/keyStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const keyStore = createQueryKeyStore({
},
auth: {
signIn: null,
refreshAccessToken: null,
},
user: {
register: null,
Expand Down
9 changes: 9 additions & 0 deletions src/queries/useRefreshAccessToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useMutation } from '@tanstack/react-query';
import { keyStore } from '@/queries/keyStore.ts';
import { refreshAccessToken } from '@/apis/auth/refreshAccessToken.ts';

export const useRefreshAccessToken = () =>
useMutation({
mutationKey: keyStore.auth.refreshAccessToken.queryKey,
mutationFn: refreshAccessToken,
});

0 comments on commit eebef94

Please sign in to comment.