/** * 라우팅 — 인증 필요 경로 보호 (PRD §3.5) */ import { useEffect } from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; import Layout from './components/Layout'; import AuthLayout from './components/AuthLayout'; import IntroLayout from './components/IntroLayout'; import ProtectedRoute from './components/ProtectedRoute'; import { http } from './lib/api'; import { migrateLegacyStorage } from './lib/storage'; import { useAuthStore } from './store/auth'; // Info pages — Phase 1~4 (공개 + 로그인 후 공용) import About from './pages/Info/About'; import TestGuide from './pages/Info/TestGuide'; import PrivacyPolicy from './pages/Info/PrivacyPolicy'; import TermsOfService from './pages/Info/TermsOfService'; import Research from './pages/Info/Research'; import Support from './pages/Info/Support'; import SignIn from './pages/SignIn'; import Intro from './pages/Intro'; import SignUp from './pages/SignUp'; import SignUpComplete from './pages/SignUpComplete'; import SignUpProfile from './pages/SignUpProfile'; import FindPassword from './pages/FindPassword'; import ErrorPage from './pages/Error'; import Home from './pages/Home'; import Survey from './pages/Survey'; import TestHome from './pages/TestHome'; import TestStart from './pages/TestStart'; import TestIntro from './pages/TestIntro'; import TestMC from './pages/TestMC'; import TestEP from './pages/TestEP'; import TestCST from './pages/TestCST'; import TestNB from './pages/TestNB'; import TestMR from './pages/TestMR'; import Dashboard from './pages/Dashboard'; import HistoryDetail from './pages/HistoryDetail'; import Checkout from './pages/Checkout'; import MyPage from './pages/MyPage'; /** 앱 시작 시 로그인 상태면 서버에서 최신 사용자 프로필을 로드 (닉네임 등 갱신) */ function Bootstrap() { const accessToken = useAuthStore((s) => s.accessToken); const setUser = useAuthStore((s) => s.setUser); const user = useAuthStore((s) => s.user); useEffect(() => { if (!accessToken) return; http .get<{ user: import('./store/auth').User }>('/api/profile') .then((r) => { setUser(r.user); // 레거시 localStorage 마이그레이션 (v1 → 사용자별 키) if (r.user?.id) { migrateLegacyStorage(r.user.id); } }) .catch(() => { /* 토큰 만료 시 api 클라이언트가 로그아웃 처리 */ }); }, [accessToken, setUser]); // user가 이미 store에 있는 경우 (세션 복원)에도 마이그레이션 useEffect(() => { if (user?.id) { migrateLegacyStorage(user.id); } }, [user?.id]); return null; } export default function App() { return ( <> {/* 공개 — AuthLayout(TopAppBar + 하단메뉴 없음) */} }> } /> } /> } /> } /> } /> {/* 공개 — 인트로 (TopAppBar/하단메뉴 없음) */} } /> {/* 공개 — Info 페이지 (IntroLayout: TopAppBar 없음, 하단 Footer 고정, 한글 페이지) */} }> } /> } /> } /> } /> {/* 로그인 필요 */} } > } /> } /> } /> } /> } /> } /> } /> } /> } /> {/* 인지검사 실행 — 가로 전체화면, 하단메뉴/광고 없음 */} } /> } /> } /> } /> } /> {/* 회원가입 직후 프로필 입력은 로그인 후 가능 */} } /> {/* 마이페이지에서 프로필 수정 (Layout 없음 → 상단 타이틀/하단 메뉴 Shell 내부) */} } > } /> {/* Info pages — Phase 1~4 */} } /> } /> } /> } /> } /> } /> } /> ); }