로그인하면 '/' 가 무조건 /sites 로 튕겼다. 그래서 **로고를 눌러도 랜딩이 안 뜨고**, /pricing 은 살아 있는데 링크가 랜딩과 MarketingShell 에만 있어 로그인한 사장님은 주소를 직접 쳐야 했다. 요금은 쓰는 도중에 확인하는 값이지 가입 전에만 보는 값이 아니다. - app/router.tsx: Home 의 리다이렉트 제거. '/' 는 누구에게나 랜딩이다. "로그인 직후엔 내 사이트로" 는 로그인·가입 화면이 직접 보낸다. - pages/LoginPage.tsx: 도착지를 `homePath` prop 으로 받는다. 기본값 '/' 라 내부 운영 앱(자기 '/' 가 사업장 목록으로 간다)은 그대로다. 사장님 앱만 '/sites'. - pages/SignupPage.tsx: 가입 후 '/' → '/sites'. 안 그러면 관문이 없어진 지금 랜딩에 떨어진다. - 로그인·가입 화면 로고에 '/' 링크. 그 화면에서 빠져나갈 길이 하나도 없었다. - layout/AppShell.tsx: 로그아웃 → '/login' 이 아니라 '/'. 나간 사람에게 로그인 폼을 다시 들이밀지 않는다. 사이드바에 [요금] 추가. - layout/MarketingShell.tsx: 헤더 [무료로 만들기] 제거 — 히어로 입력 카드가 이미 그 자리다. 시작하는 문이 한 화면에 둘이면 어느 쪽이 진짜인지 고르게 만든다. 검증: tsc·eslint 통과. docker compose up -d --build solution-site 로 띄워 눌러 확인 — 로그인 상태에서 로고 → 랜딩, 헤더는 [이렇게 나옵니다 · 요금 · 내 사이트].
99 lines
4.2 KiB
TypeScript
99 lines
4.2 KiB
TypeScript
import {createBrowserRouter, Navigate} from 'react-router';
|
|
import {Loader2} from 'lucide-react';
|
|
import {AccountPage} from '@/pages/AccountPage';
|
|
import {BuilderPage} from '@/pages/BuilderPage';
|
|
import {DevShowcasePage} from '@/pages/DevShowcasePage';
|
|
import {LandingPage} from '@/pages/LandingPage';
|
|
import {LoginPage} from '@/pages/LoginPage';
|
|
import {NotFoundPage} from '@/pages/NotFoundPage';
|
|
import {PricingPage} from '@/pages/PricingPage';
|
|
import {ShowcasePage} from '@/pages/ShowcasePage';
|
|
import {SignupPage} from '@/pages/SignupPage';
|
|
import {SitesPage} from '@/pages/SitesPage';
|
|
import {RequireAuth} from '@/components/layout/RequireAuth';
|
|
import {useAuthStore} from '@/stores/auth';
|
|
|
|
/**
|
|
* 첫 화면은 로그인 여부로 갈린다 — 이미 사이트를 가진 사장님의 용건은 "새로 만들기"가 아니라
|
|
* "내 것 고치기"다. 비로그인은 **랜딩**을 본다.
|
|
*
|
|
* ★ 예전엔 비로그인을 곧장 위저드로 보냈다. 그러면 이 제품이 무엇을 파는 물건인지 말할 자리가
|
|
* 한 곳도 없다 — 처음 온 사람이 업종 선택 화면부터 만난다.
|
|
* ★ 복구가 끝나기 전에 판단하면 새로고침할 때마다 화면이 한 번 번쩍이고 목록으로 튄다.
|
|
*/
|
|
function Home() {
|
|
const isRestoring = useAuthStore((s) => s.isRestoring);
|
|
const user = useAuthStore((s) => s.user);
|
|
|
|
if (isRestoring) {
|
|
return (
|
|
<div className="flex h-screen items-center justify-center text-muted-foreground">
|
|
<Loader2 className="size-5 animate-spin" />
|
|
</div>
|
|
);
|
|
}
|
|
/*
|
|
* ★ 로그인해도 랜딩을 보여 준다 (2026-09-04, 사장님 지적)
|
|
* 예전엔 여기서 /sites 로 튕겼다. 그러면 **로그인한 사장님은 랜딩·요금에 갈 길이 없다** —
|
|
* 로고가 '/' 를 걸고 있어도 눌리는 순간 목록으로 돌아온다.
|
|
* "로그인 직후엔 내 사이트로" 는 로그인·가입 화면이 직접 보낸다(LoginPage homePath).
|
|
*/
|
|
void user;
|
|
return <LandingPage />;
|
|
}
|
|
|
|
export const router = createBrowserRouter([
|
|
{path: '/login', element: <LoginPage homePath="/sites" />},
|
|
// 로그인 화면의 [회원가입] 이 여기로 온다. 이 줄이 없으면 링크는 있고 목적지만 404 다.
|
|
{path: '/signup', element: <SignupPage />},
|
|
|
|
// 비로그인 = 랜딩, 로그인 = 내 사이트. Home 이 그걸 가른다.
|
|
{path: '/', element: <Home />},
|
|
|
|
// 로그인 전 화면. ★ 랜딩과 같은 껍데기(MarketingShell)를 쓴다 — 사이드바 없는 문서형이다.
|
|
{path: '/pricing', element: <PricingPage />},
|
|
{path: '/showcase', element: <ShowcasePage />},
|
|
|
|
// 로그인한 사장님의 홈. 만든 사이트를 열고 고치는 자리다.
|
|
{
|
|
path: '/sites',
|
|
element: (
|
|
<RequireAuth>
|
|
<SitesPage />
|
|
</RequireAuth>
|
|
),
|
|
},
|
|
{
|
|
path: '/account',
|
|
element: (
|
|
<RequireAuth>
|
|
<AccountPage />
|
|
</RequireAuth>
|
|
),
|
|
},
|
|
|
|
/**
|
|
* 빌더는 로그인 화면을 앞에 세우지 않는다 — 위저드를 열자마자 로그인부터 만나면
|
|
* 만들어 보기도 전에 막힌다.
|
|
*
|
|
* 대신 세션은 조용히 확보한다 — VITE_AUTO_LOGIN_ID·PW 가 주입돼 있으면 useAutoLogin() 이
|
|
* 그 계정으로 붙고, 없으면 서버가 필요한 순간(2단계 검색)에만 알린다.
|
|
*
|
|
* 에디터(6단계)는 전체 화면이 필요해 AppShell 을 스스로 끄고 켠다 — BuilderPage 참조.
|
|
*/
|
|
{path: '/builder', element: <BuilderPage />},
|
|
|
|
/**
|
|
* ★ 내부 운영 화면(/places, /local-content, /seo)은 여기 없다 — 최상단 `admin/` 앱으로 나갔다.
|
|
* 라우트 가드는 화면을 가리지 **번들은 못 가린다**: 한 앱이면 내부 라우트 이름과 코드가
|
|
* 사장님 브라우저에 그대로 내려간다. `UserRole.DEVELOPER` 주석의
|
|
* "고객사에 존재를 노출하지 않는다"를 지키려면 번들 자체가 갈라져야 한다.
|
|
* (ARCHITECTURE.md 4절)
|
|
*/
|
|
|
|
// 개발 빌드에서만 열리는 컴포넌트 쇼케이스. 운영 번들에서는 라우트 자체가 없다.
|
|
...(import.meta.env.DEV ? [{path: '/dev/showcase', element: <DevShowcasePage />}] : []),
|
|
|
|
{path: '*', element: <NotFoundPage />},
|
|
]);
|