--- id: wiki-2026-0508-client-side-rendering-csr title: Client-Side Rendering (CSR) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [CSR, SPA Rendering] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [rendering, csr, spa, frontend, performance] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: React / Vue / Svelte --- # Client-Side Rendering (CSR) ## 매 한 줄 > **"매 browser 가 매 HTML 을 그린다"**. CSR 은 server 가 빈 shell + JS bundle 만 보내고, browser 가 fetch + render 모두 수행 — 매 SPA 의 default mode, interactive app 에 강하나 매 first paint / SEO 매 weak. ## 매 핵심 ### 매 lifecycle 1. Browser → server: `GET /` → minimal HTML + ` ``` ### Route-based code splitting ```tsx import { lazy, Suspense } from 'react'; import { Routes, Route } from 'react-router'; const Dashboard = lazy(() => import('./Dashboard')); const Settings = lazy(() => import('./Settings')); export default function App() { return ( }> } /> } /> ); } ``` ### Skeleton-first paint (perceived perf) ```tsx function UsersList() { const { data, isLoading } = useQuery({ queryKey: ['users'], queryFn: fetchUsers }); if (isLoading) return ; return ; } ``` ### Pre-fetch on hover (link prefetch) ```tsx queryClient.prefetchQuery({ queryKey: ['dashboardData'], queryFn })} > Dashboard ``` ### Service Worker for offline shell ```ts // sw.ts self.addEventListener('install', (e: any) => { e.waitUntil(caches.open('shell-v1').then((c) => c.addAll(['/', '/main.js', '/main.css']))); }); self.addEventListener('fetch', (e: any) => { e.respondWith(caches.match(e.request).then((r) => r ?? fetch(e.request))); }); ``` ### Bundle budget enforcement ```js // vite.config.ts export default { build: { rollupOptions: { output: { manualChunks: { vendor: ['react', 'react-dom'] }, }, }, chunkSizeWarningLimit: 200, // KB }, }; ``` ### SEO via prerender (when CSR + needed) ```bash # Use prerender for marketing routes only npx prerender-spa-plugin --routes /,/about,/pricing ``` ## 매 결정 기준 | 상황 | Render mode | |---|---| | Auth-walled dashboard | CSR | | Marketing site | SSG or SSR | | Mixed app (e-commerce) | RSC / SSR + islands | | Rich realtime (Figma-like) | CSR + WebSocket | **기본값**: 매 user-app (login wall 뒤) → CSR. 매 public content → SSR/SSG/RSC. ## 🔗 Graph - 부모: [[Rendering-Strategies]] · [[프론트엔드 및 UIUX 표준|Frontend-Architecture]] - 변형: [[React Server Components — 경계 의식]] - Adjacent: [[Core Web Vitals Optimization (INP, LCP, CLS)|Core-Web-Vitals]] · [[Code Splitting]] · [[Hydration]] ## 🤖 LLM 활용 **언제**: app-like UX, auth-protected, heavy client interactivity. **언제 X**: 매 SEO-critical public page, low-end device 가 주 audience. ## ❌ 안티패턴 - **Mega-bundle**: 매 single chunk 5MB → split routes / vendor. - **No skeleton / loading state**: 매 blank screen 매 perceived as broken. - **CSR for blog/docs**: 매 SEO/perf 매 모두 lose — SSG choice. ## 🧪 검증 / 중복 - Verified (web.dev / React docs / Vite docs). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — CSR fundamentals + tradeoffs + patterns |