logo

DowanKim

15. 인증 관련 문제 발생

2025년 10월 7일

이게머니

문제1 : 현재 인증된 사용자가 아직도 로그인버튼을 눌러야 하는 문제가 있음을 알았고, 이 또한 제가 구현을 아직 안했음을 깨달았습니다.

해결 : 쿠키에 토큰 있을시 자동 로그인

splashPage에서, 기존 구현한 useAuth를 통해 토큰을 체크하고 로그인된 상태라면 /home으로, 아니면 /login으로 이동하게 합니다.

import { useState, useEffect } from 'react'; import { useTokenCookies } from '@/utils/cookie'; import { useNavigate } from 'react-router-dom'; export const useAuth = () => { const { accessToken, refreshToken, clearTokens } = useTokenCookies(); const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(true); const navigate = useNavigate(); useEffect(() => { setIsAuthenticated(!!accessToken); setIsLoading(false); }, [accessToken]); const logout = () => { clearTokens(); setIsAuthenticated(false); navigate('/login'); }; const checkAuth = () => { return !!accessToken; }; return { isAuthenticated, isLoading, accessToken, refreshToken, logout, checkAuth, }; };
export const SplashPage = () => { const navigate = useNavigate(); const { isAuthenticated, isLoading } = useAuth(); useEffect(() => { if (!isLoading) { setTimeout(() => { if (isAuthenticated) { navigate('/home'); } else { navigate('/login'); } }, 1000); } }, [isAuthenticated, isLoading, navigate]); return ( <CenteredContainer> <Logo src={LogoFace} alt="앱 로고" /> {isLoading && ( <LoadingContainer> <LoadingSpinner size="medium" message="앱을 시작하는 중..." /> </LoadingContainer> )} </CenteredContainer> ); };

문제2 : 현재 인증되지 않은 사용자가 /home이나 /quiz 같은 인증이 필요한 페이지에 url을 직접 작성함으로써 접근이 가능한 문제를 발견했습니다.

해결 : ProtectedRoute 컴포넌트로 토큰 없으면 페이지 접근 불가

import { Navigate } from 'react-router-dom'; import { useAuth } from '@/hooks/useAuth'; import { FullScreenLoadingSpinner } from './LoadingSpinner'; interface ProtectedRouteProps { children: React.ReactNode; } export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => { const { isAuthenticated, isLoading } = useAuth(); if (isLoading) { return <FullScreenLoadingSpinner message="인증 확인 중..." />; } if (!isAuthenticated) { return <Navigate to="/login" replace />; } return <>{children}</>; };

이렇게 useAuth를 사용해 ProtectedRoute라는 컴포넌트를 만들어 주었습니다.

인증이 안된 사용자는, url로 접근을 해도 들어갈 수 없게 하기 위해서 입니다.

그리고 router 파일에서 다음과 같이 설정하였습니다.

export const router = createBrowserRouter([ { path: '/', element: <SplashPage /> }, { path: '/login', element: <LoginPage /> }, { path: '/character-create', element: <CharacterCreatePage /> }, { path: '/auth/kakao/callback', element: <KakaoCallbackPage /> }, { path: '/home', element: ( <ProtectedRoute> <HomePage /> </ProtectedRoute> ), }, { path: '/quizList', element: ( <ProtectedRoute> <TopicSelectPage /> </ProtectedRoute> ), }, { path: '/quizSolve', element: ( <ProtectedRoute> <QuizSolvePage data={data} /> </ProtectedRoute> ), }, { path: '/quizResult', element: ( <ProtectedRoute> <QuizResultPage data={data} /> </ProtectedRoute> ), }, { path: '/mypage', element: ( <ProtectedRoute> <MyPage /> </ProtectedRoute> ), }, { path: '/sharing', element: ( <ProtectedRoute> <SharingPage /> </ProtectedRoute> ), }, { path: '/rank', element: ( <ProtectedRoute> <RankPage /> </ProtectedRoute> ), }, { path: '/dev/api-test', element: <ApiTestPage /> }, { path: '/test', element: ( <ProtectedRoute> <TestPage /> </ProtectedRoute> ), }, { path: '/test/result', element: ( <ProtectedRoute> <TestResultPage /> </ProtectedRoute> ), }, { path: '/record', element: ( <ProtectedRoute> <LearningRecordPage /> </ProtectedRoute> ), }, { path: '/tier', element: ( <ProtectedRoute> <TierPage /> </ProtectedRoute> ), }, { path: '*', element: <NotFoundPage /> }, ]);

접근하면 안되는 페이지에 대해, 하나하나 전부 ProtectedRoute 컴포넌트로 감싸 주었습니다.