Skip to content

Commit

Permalink
feat: isLogin 값 전역 상태로 관리 (#120)
Browse files Browse the repository at this point in the history
* feat: useAuthAtom 추가

* feat: 기존의 로그인 후 토큰 저장 로직 useAuthAtom 메소드로 대체

* feat: useLogout onSuccessCallback 메소드 파라미터 추가

* feat: useAuthAtom 토큰 추가, 제거 메소드명 변경 및 적용

* feat: ProfileDropdown 에서 로그아웃 시 reload 로직 제거

* feat: useCallback dependency에 setToken 추가

* feat: useLogout onSuccess 콜백 수정

* feat: isLogin useMemo 제거
  • Loading branch information
hexdrinker authored Jul 17, 2024
1 parent 324d14f commit 86e0da4
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 61 deletions.
9 changes: 4 additions & 5 deletions apps/admin/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'the-new-css-reset/css/reset.css';
import './index.css';

import { LOCAL_STORAGE, QueryClientProvider } from '@boolti/api';
import { QueryClientProvider } from '@boolti/api';
import { BooltiUIProvider } from '@boolti/ui';
import { setDefaultOptions } from 'date-fns';
import { ko } from 'date-fns/locale';
Expand Down Expand Up @@ -30,6 +30,7 @@ import ShowSettlementPage from './pages/ShowSettlementPage/ShowSettlementPage';
import ShowTicketPage from './pages/ShowTicketPage/ShowTicketPage';
import SignUpCompletePage from './pages/SignUpComplete/SignUpCompletePage';
import SitePolicyPage from './pages/SitePolicyPage/SitePolicyPage';
import { useAuthAtom } from './atoms/useAuthAtom';

setDefaultOptions({ locale: ko });

Expand Down Expand Up @@ -75,11 +76,9 @@ const publicRoutes = [
];

const PrivateRoute = () => {
const isLogin =
window.localStorage.getItem(LOCAL_STORAGE.ACCESS_TOKEN) &&
window.localStorage.getItem(LOCAL_STORAGE.REFRESH_TOKEN);
const { isLogin } = useAuthAtom();

if (!isLogin) {
if (!isLogin()) {
return <Navigate to={PATH.LOGIN} replace />;
}

Expand Down
48 changes: 48 additions & 0 deletions apps/admin/src/atoms/useAuthAtom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { LOCAL_STORAGE } from '@boolti/api';
import { useAtom } from 'jotai';
import { atomWithStorage, RESET } from 'jotai/utils';

const storageMethod = {
getItem: (key: string, initialValue: string | null) => {
return window.localStorage.getItem(key) ?? initialValue;
},
setItem: (key: string, value: string | null) => {
window.localStorage.setItem(key, value as string);
},
removeItem: (key: string) => {
window.localStorage.removeItem(key);
},
};
const accessTokenAtom = atomWithStorage<string | null>(
LOCAL_STORAGE.ACCESS_TOKEN,
window.localStorage.getItem(LOCAL_STORAGE.ACCESS_TOKEN),
storageMethod,
);
const refreshTokenAtom = atomWithStorage<string | null>(
LOCAL_STORAGE.REFRESH_TOKEN,
window.localStorage.getItem(LOCAL_STORAGE.REFRESH_TOKEN),
storageMethod,
);

export const useAuthAtom = () => {
const [accessToken, setAccessToken] = useAtom(accessTokenAtom);
const [refreshToken, setRefreshToken] = useAtom(refreshTokenAtom);

const setToken = (accessToken: string, refreshToken: string) => {
setAccessToken(accessToken);
setRefreshToken(refreshToken);
};

const removeToken = () => {
setAccessToken(RESET);
setRefreshToken(RESET);
};

const isLogin = () => !!accessToken && !!refreshToken;

return {
setToken,
removeToken,
isLogin,
};
};
11 changes: 7 additions & 4 deletions apps/admin/src/components/ProfileDropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useNavigate } from 'react-router-dom';
import { PATH } from '~/constants/routes';

import Styled from './ProfileDropdown.styles';
import { useAuthAtom } from '~/atoms/useAuthAtom';

interface ProfileDropdownProps {
image?: string;
Expand Down Expand Up @@ -34,7 +35,12 @@ const ProfileSVG = () => (

const ProfileDropdown = ({ image }: ProfileDropdownProps) => {
const { isOpen, toggleDropdown } = useDropdown();
const logoutMutation = useLogout();
const { removeToken } = useAuthAtom();
const logoutMutation = useLogout({
onSuccess: () => {
removeToken();
},
});
const navigate = useNavigate();

return (
Expand All @@ -51,9 +57,6 @@ const ProfileDropdown = ({ image }: ProfileDropdownProps) => {
colorTheme="netural"
onClick={async () => {
await logoutMutation.mutateAsync();
if (location.pathname === PATH.INDEX) {
location.reload();
}
navigate(PATH.INDEX, { replace: true });
}}
>
Expand Down
9 changes: 7 additions & 2 deletions apps/admin/src/components/ShowDetailLayout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { HREF, PATH } from '~/constants/routes';
import Header from '../Header/index.tsx';
import Layout from '../Layout/index.tsx';
import Styled from './ShowDetailLayout.styles.ts';
import { useAuthAtom } from '~/atoms/useAuthAtom.ts';

const settlementTooltipText = {
SEND: '내역서 확인 및 정산 요청을 진행해 주세요',
Expand All @@ -35,7 +36,7 @@ const ShowDetailLayout = ({ showName, children, onClickMiddleware }: ShowDetailL
initialInView: true,
});
const theme = useTheme();

const { removeToken } = useAuthAtom();
const navigate = useNavigate();
const params = useParams<{ showId: string }>();
const matchInfoTab = useMatch(PATH.SHOW_INFO);
Expand All @@ -46,7 +47,11 @@ const ShowDetailLayout = ({ showName, children, onClickMiddleware }: ShowDetailL

const { data: lastSettlementEvent } = useShowLastSettlementEvent(Number(params!.showId));
const { data: settlementInfo } = useShowSettlementInfo(Number(params!.showId));
const logoutMutation = useLogout();
const logoutMutation = useLogout({
onSuccess: () => {
removeToken();
},
});

const tooltipStyle = {
color: theme.palette.grey.w,
Expand Down
8 changes: 7 additions & 1 deletion apps/admin/src/pages/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@ import UserProfile from '~/components/UserProfile';
import { HREF, PATH } from '~/constants/routes';

import Styled from './HomePage.styles';
import { useAuthAtom } from '~/atoms/useAuthAtom';

const bannerDescription = {
REQUIRED: '공연의 정산 내역서가 도착했어요. 내역을 확인한 후 정산을 요청해 주세요.',
DONE: '공연의 정산이 완료되었어요.',
};

const HomePage = () => {
const { removeToken } = useAuthAtom();
const navigate = useNavigate();
const logout = useLogout();
const logout = useLogout({
onSuccess: () => {
removeToken();
},
});
const confirm = useConfirm();

const { data: userAccountInfoData, isLoading: isUserAccountInfoLoading } = useUserAccountInfo();
Expand Down
22 changes: 12 additions & 10 deletions apps/admin/src/pages/Landing/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import { LINK } from '~/constants/link';
import { PATH } from '~/constants/routes';
import { useDeviceWidth } from '~/hooks/useDeviceWidth';
import { useScrollDirection } from '~/hooks/useScrollDirection';
import { getIsLogin } from '~/utils/auth';

import { visibleSectionAtom } from '../../atoms/visibleSectionAtom';
import { Tab } from '..';
import Styled from './Header.styles';
import { useAuthAtom } from '~/atoms/useAuthAtom';

const Header = () => {
const { isLogin, removeToken } = useAuthAtom();
const currentVisibleSection = useAtomValue(visibleSectionAtom);
const scrollDirection = useScrollDirection();
const visible = currentVisibleSection === 'key-visal' || scrollDirection === 'up';
Expand All @@ -25,19 +26,20 @@ const Header = () => {
const theme = useTheme();
const isMobile = deviceWidth < parseInt(theme.breakpoint.mobile, 10);

const logout = useLogout();
const logout = useLogout({
onSuccess: () => {
removeToken();
},
});
const navigate = useNavigate();

const [isExpanded, setIsExpanded] = useState(false);
const [isLogin, setIsLogin] = useState(Boolean(getIsLogin()));

const { data } = useUserSummary({ enabled: isLogin });
const { data } = useUserSummary({ enabled: isLogin() });
const { imagePath } = data ?? {};

const onClickAuthButton = async () => {
if (isLogin) {
if (isLogin()) {
await logout.mutateAsync();
setIsLogin(false);
return;
}

Expand Down Expand Up @@ -78,7 +80,7 @@ const Header = () => {
앱 바로가기
</Styled.InternalLink>
<Styled.InternalLink to={PATH.HOME}>공연 준비하기</Styled.InternalLink>
{isLogin ? (
{isLogin() ? (
<Styled.DropDownContainer>
<ProfileDropdown image={imagePath} />
</Styled.DropDownContainer>
Expand Down Expand Up @@ -123,12 +125,12 @@ const Header = () => {
</Styled.InternalLink>
<Styled.InternalLink to={PATH.HOME}>공연 준비하기</Styled.InternalLink>
<Styled.MobileAuthButton
colorTheme={isLogin ? 'netural' : 'primary'}
colorTheme={isLogin() ? 'netural' : 'primary'}
size="bold"
role="button"
onClick={onClickAuthButton}
>
{isLogin ? '로그아웃' : '로그인'}
{isLogin() ? '로그아웃' : '로그인'}
</Styled.MobileAuthButton>
</Styled.MobileMenu>

Expand Down
12 changes: 5 additions & 7 deletions apps/admin/src/pages/Login/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LOCAL_STORAGE, useAppleLogin, useSignUp } from '@boolti/api';
import { useAppleLogin, useSignUp } from '@boolti/api';
import { AppleIcon, BooltiSmallLogo, KakaotalkIcon } from '@boolti/icon';
import { jwtDecode } from 'jwt-decode';
import { useNavigate } from 'react-router-dom';
Expand All @@ -7,6 +7,7 @@ import { LINK } from '~/constants/link';
import { PATH } from '~/constants/routes';

import Styled from './LoginPage.styles';
import { useAuthAtom } from '~/atoms/useAuthAtom';

declare global {
interface Window {
Expand Down Expand Up @@ -48,6 +49,7 @@ declare global {
const kakaoRedirectUri = `${window.location.origin}${PATH.OAUTH_KAKAO}`;

const LoginPage = () => {
const { setToken } = useAuthAtom();
const navigate = useNavigate();

const appleLoginMutation = useAppleLogin();
Expand All @@ -69,9 +71,7 @@ const LoginPage = () => {
oauthIdentity,
});

window.localStorage.setItem(LOCAL_STORAGE.ACCESS_TOKEN, accessToken);
window.localStorage.setItem(LOCAL_STORAGE.REFRESH_TOKEN, refreshToken);

setToken(accessToken, refreshToken);
navigate(PATH.SIGNUP_COMPLETE, { replace: true });
};

Expand All @@ -97,9 +97,7 @@ const LoginPage = () => {
return;
}

window.localStorage.setItem(LOCAL_STORAGE.ACCESS_TOKEN, accessToken);
window.localStorage.setItem(LOCAL_STORAGE.REFRESH_TOKEN, refreshToken);

setToken(accessToken, refreshToken);
navigate(PATH.HOME);
} catch (error) {
console.error(error);
Expand Down
22 changes: 7 additions & 15 deletions apps/admin/src/pages/OAuth/OAuthKakaoPage.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import {
LOCAL_STORAGE,
useKakaoLogin,
useKakaoToken,
useKakaoUserInfo,
useSignUp,
} from '@boolti/api';
import { useKakaoLogin, useKakaoToken, useKakaoUserInfo, useSignUp } from '@boolti/api';
import { useCallback, useEffect, useMemo } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useAuthAtom } from '~/atoms/useAuthAtom';

import { PATH } from '~/constants/routes';

const OAuthKakaoPage = () => {
const { setToken } = useAuthAtom();
const location = useLocation();
const navigate = useNavigate();

Expand Down Expand Up @@ -45,12 +41,10 @@ const OAuthKakaoPage = () => {
imgPath: userInfo.kakao_account?.profile?.profile_image_url,
});

window.localStorage.setItem(LOCAL_STORAGE.ACCESS_TOKEN, accessToken);
window.localStorage.setItem(LOCAL_STORAGE.REFRESH_TOKEN, refreshToken);

setToken(accessToken, refreshToken);
navigate(PATH.SIGNUP_COMPLETE, { replace: true });
},
[kakaoUserInfoMutation, navigate, signUpMutation],
[kakaoUserInfoMutation, navigate, signUpMutation, setToken],
);

const kakaoLogin = useCallback(async () => {
Expand All @@ -67,11 +61,9 @@ const OAuthKakaoPage = () => {
return;
}

window.localStorage.setItem(LOCAL_STORAGE.ACCESS_TOKEN, accessToken);
window.localStorage.setItem(LOCAL_STORAGE.REFRESH_TOKEN, refreshToken);

setToken(accessToken, refreshToken);
navigate(PATH.HOME);
}, [code, kakaoLoginMutation, kakaoSignUp, kakaoTokenMutation, navigate]);
}, [code, kakaoLoginMutation, kakaoSignUp, kakaoTokenMutation, navigate, setToken]);

useEffect(() => {
kakaoLogin();
Expand Down
9 changes: 4 additions & 5 deletions apps/admin/src/pages/QRPage/QRPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,21 @@ import { BooltiGrey, BooltiLogo } from '@boolti/icon';
import { Footer, TextButton } from '@boolti/ui';
import { useTheme } from '@emotion/react';
import { QRCodeSVG } from 'qrcode.react';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';

import Header from '~/components/Header';
import Layout from '~/components/Layout';
import ProfileDropdown from '~/components/ProfileDropdown';
import { LINK } from '~/constants/link';
import { PATH } from '~/constants/routes';
import { getIsLogin } from '~/utils/auth';

import Styled from './QRPage.styles';
import { useAuthAtom } from '~/atoms/useAuthAtom';

const QRPage = () => {
const { isLogin } = useAuthAtom();
const theme = useTheme();
const [isLogin] = useState(Boolean(getIsLogin()));
const { data: userAccountInfoData } = useUserSummary({ enabled: isLogin });
const { data: userAccountInfoData } = useUserSummary({ enabled: isLogin() });
const navigate = useNavigate();

return (
Expand All @@ -42,7 +41,7 @@ const QRPage = () => {
</>
}
right={
isLogin ? (
isLogin() ? (
<ProfileDropdown image={userAccountInfoData?.imagePath} />
) : (
<TextButton
Expand Down
8 changes: 0 additions & 8 deletions apps/admin/src/utils/auth.ts

This file was deleted.

10 changes: 6 additions & 4 deletions packages/api/src/mutations/useLogout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ const useLogout = (options?: UseMutationOptions) =>
useMutation(postLogout, {
...options,
onSuccess: (data, variables, context) => {
window.localStorage.removeItem(LOCAL_STORAGE.ACCESS_TOKEN);
window.localStorage.removeItem(LOCAL_STORAGE.REFRESH_TOKEN);

options?.onSuccess?.(data, variables, context);
if (options?.onSuccess) {
options.onSuccess(data, variables, context);
} else {
window.localStorage.removeItem(LOCAL_STORAGE.ACCESS_TOKEN);
window.localStorage.removeItem(LOCAL_STORAGE.REFRESH_TOKEN);
}
},
});

Expand Down

0 comments on commit 86e0da4

Please sign in to comment.