Weekly Planner
Architecture

Routing & auth wiring

TanStack Router routes, Instant auth context, and navigation guards.

Routing & auth wiring

File: src/router.jsx

Routes

PathBehavior
/If signed in → Planner. If signed out → Landing.
/homeAlways Landing (marketing). Signed-in CTAs open the planner; signed-out keep guest/login.
/loginMagic-code login. Authenticated users redirect to /.
/s/$tokenPublic shared planner. No auth guard.
/accountAccount page. Signed-out visitors redirect to /login.

Root layout only shows a compact error status if auth fails. While Instant auth is loading, routes still paint — / uses a compact status only when no cached user is restored yet; /login, /home, and share links render immediately.

Eager vs lazy routes

Landing and Planner are lazy so signed-in cold loads do not pull marketing JS (and signed-out loads skip the planner bundle). Compact BootStatus is the defaultPendingComponent while chunks resolve. Login, Account, and SharedPlanner stay lazy as well.

Boot sequence (signed-in /)

Avoid stacking blank full-page “불러오는 중…” gates. Intended order:

  1. Auth/ shows a compact status while auth.isLoading; other routes are not gated.
  2. Planner chrome — cold empty workspace still mounts a light header shell; once the board list has rows, the full header stays mounted (list board drives the shell while detail hydrates).
  3. Surface — Prefer an empty grid from the list board while detail hydrates. surfacePending only when there is no list row yet. Prefs may fill in after paint; they must not unmount chrome.
  4. Viewer bannershouldShowViewerBanner / roleKnown gate the 보기 전용 bar until owner/editors links are resolved (no owner flash).

Helpers: src/board/workspace-loading.js, roleKnown in src/sharing/member-policy.js.

Guest sign-in keeps the landing CTA spinner until auth resolves, then the planner paints an optimistic board shell (empty grid) while ensureWorkspace seeds — presence joins only once a real board id exists. Todo badge subscription is unchanged.

Auth context

const auth = db.useAuth();
// mapped into router context; router.invalidate() when loading/user changes

Guards re-run when auth changes because the router is invalidated. Shared links are not gated in the router — password gating lives in useSharedBoard / share-access.js.

Landing vs login

  • Landing (/ when signed out, always on /home) — marketing preview, guest sign-in (db.auth.signInAsGuest()), theme toggle (local only). Signed-out: link to /login. Signed-in: “시간표 열기” → /.
  • Login — email → 6-digit code via useMagicCodeAuth.

PWA and SPA fallback

vercel.json rewrites non-API paths to /index.html. Do not enable cleanUrls — it breaks SPA fallback (guarded by test/vercel-spa.test.js). The service worker navigate fallback explicitly denies /api/*.

On this page