SPA 는 HTML 한 장. 라우터 없으면 네비게이션 = 전체 리로드. 라우터 있으면 URL 만 바뀌고 뷰가 교체되고, 브라우저 뒤로가기는 사용자가 기대한 대로 동작해.
라우터가 푸는 문제
Vite 앱은 모든 URL 에 index.html 한 장 줘. 브라우저가 main.tsx 로드, <App /> 마운트, 끝. 사용자가 /conversations/abc 링크 클릭하면 브라우저가 전체 리로드 (React 상태 파괴) 하고 서버는 똑같은 index.html 다시 보냄. 매번 시작점.
클라이언트 사이드 라우터가 그 클릭 인터셉트해서 history.pushState() 로 URL 갱신, React 한테 새 URL 기반으로 다른 컴포넌트 트리 렌더하라고 말함. 리로드 없음. 상태 생존. 뒤로가기 동작.
React Router 핵심 5개
<BrowserRouter>가 앱 감싸고 라우터 context 노출.<Routes>+<Route>가 URL → 컴포넌트 매핑 선언.<Link>가 클릭에 리로드 대신pushState호출하는 앵커 렌더.useParams()가 routed 컴포넌트 안에서 동적 URL 세그먼트 읽음.useNavigate()가 프로그래매틱 네비게이션용 함수 반환 (예: form submit 후).
최소 앱
라우트는 플랫 또는 nested 가능. Nested 라우트는 부모 컴포넌트를 레이아웃으로 쓰고 자식이 채울 자리에 <Outlet /> 렌더. 사이드바 + 메인 패널이 페이지 전환에도 마운트된 채 유지되는 패턴 (cwkPippa 채팅 UI 처럼).
NavLink vs Link
NavLink 는 자기 target 이 현재 URL 과 매치되는지 아는 Link. function-as-className 으로 active 상태 노출해서 매칭 로직 직접 안 쓰고 nav bar 의 현재 아이템 하이라이트 가능.
서버 사이드 함정: catch-all rewrite
SPA 배포 후 https://yourapp.com/conversations/abc 같은 URL 직접 진입하면 서버에 도착. 서버는 그 경로에 파일 없으니 404. 해결: asset 아닌 모든 경로를 /index.html 로 rewrite 하도록 호스트 설정. Vercel, Cloudflare Pages, Netlify 다 한 줄 설정 있어. Tauri 앱은 필요 없음 (서버 없음).
대안들
TanStack Router 가 떠오르는 도전자 (타입 안전한 라우트, 통합된 search params). Wouter 가 미니멀리스트 (1.5KB, hook 기반). React Router 가 디폴트. TS 라우트 추론 1급으로 원하면 TanStack, 진짜 KB 깎아야 하면 Wouter. 그 외엔 React Router.