SvelteKit의 장점은 페이지마다 렌더링 방식을 나눌 수 있다는 점입니다. 전 페이지를 SSR로 밀거나 SPA처럼 CSR로만 가면 SEO와 성능 중 하나를 잃기 쉽습니다.
이 글은 개념 나열보다 Telepasi.com처럼 게시글·금융 상품·추천 도구·관리자가 한 사이트에 섞일 때 무엇을 고를지에 초점을 둡니다. 동작 원리·Hydration 함정·Core Web Vitals 상세는 부록에 모았습니다.
한눈에 보기
| 방식 | HTML이 만들어지는 때 | Telepasi에서 |
|---|---|---|
| SSR | 요청마다 서버 | 메인, 게시글, 대출·예금, 검색·트렌드 |
| CSR | 브라우저에서 JS + API | 관리자, 개인화 UI, 음악·카드 추천 도구 |
| Prerender | npm run build 때 |
약관, 개인정보, FAQ, 회사소개 |
SvelteKit 기본 흐름은 SSR로 HTML을 준 뒤, JS가 붙으면서 Hydration으로 클릭이 살아나는 것입니다. 그다음 페이지 이동은 SPA처럼 빠릅니다.
1. 세 가지 방식, 언제 쓰는가
SSR — “URL로 바로 읽어야 하는 페이지”
서버가 데이터까지 넣어 HTML을 보냅니다. 검색엔진·공유 미리보기가 본문을 바로 읽을 수 있고, 첫 화면이 비지 않습니다.
→ 메인, 게시글 상세, 대출/예금 목록·상품, 공지, 트렌드, (가능하면) 검색 결과
CSR — “로그인 후, 또는 나만이 조작하는 화면”
거의 빈 HTML → JS → API → 그리기. 인터랙션·권한·실시간 UI에 맞고, SEO 가치가 낮습니다.
→ /admin/*, 마이페이지, 음악 추천·카드 추천(입력 후 API로 결과가 나오는 도구)
도구 랜딩은 title/description으로 SEO만 챙기고, 결과 본문까지 SSR할 필요는 없습니다. 개인화·취향·localStorage와 맞물리면 CSR이 자연스럽습니다.
Prerender — “거의 안 바뀌는 문서”
빌드 때 HTML을 만들어 둡니다. 런타임 Node 부담이 적고 CDN에 올리기 좋습니다.
→ 이용약관, 개인정보, FAQ, 회사소개·미션·연혁
자주 CMS로 바꾸면 SSR이거나, 배포 시 rebuild로 prerender를 갱신합니다.
SSR vs Prerender (한 줄)
| SSR | Prerender | |
|---|---|---|
| 생성 시점 | 요청마다 | 빌드 때 |
| 데이터 | 최신 가능 | 빌드 스냅샷 |
| 예 | 게시판, 상품 | 약관, FAQ |
2. Telepasi.com에 맞춘 혼합 전략
[공개 · SEO] 메인 · 게시판 · 검색 · 트렌드 · 대출/예금 → SSR (+ 캐시)
[공개 · 고정] 약관 · 개인정보 · FAQ · 회사 → Prerender
[도구 · 개인화] 음악 추천 · 카드 추천 → CSR (랜딩 메타만)
[비공개] /admin · 로그인 후 설정 → CSR
| 페이지 | 추천 | SEO | 비고 |
|---|---|---|---|
메인 / |
SSR | 최상 | 최신 글·공통 콘텐츠 |
| 게시글 상세 | SSR | 최상 | title / description / OG |
| 대출·예금 목록·상품 | SSR | 높음 | 상품명 키워드 |
| 검색·트렌드 | SSR | 중간 | 검색 결과는 서버 HTML이 유리 |
| 공지 | SSR | 중간 | |
| 약관 / 개인정보 / FAQ / 회사 | Prerender | 중간 | 변경 시 재배포 |
| 음악·카드 추천 | CSR | 랜딩만 | 결과는 클라이언트 |
| 로그인·가입 | SSR 껍데기 + CSR 로직 | 낮음 | |
관리자 /admin |
CSR | 없음 | noindex 권장(필수는 아님) |
원칙
- URL 링크로 바로 읽는 콘텐츠 → SSR 또는 Prerender
- 나만이 돌리는 도구·관리 → CSR
- 전 사이트를 한 방식으로 통일하지 말 것
3. 새 페이지 고르는 체크리스트
- 검색·공유로 들어오나? → Yes면 SSR 또는 Prerender
- 데이터가 자주/요청마다 바뀌나? → Yes면 SSR, No면 Prerender 후보
- 로그인한 나만 보나? / 추천·설정 도구인가? → CSR
- 첫 HTML에 넣을 값이 서버·클라이언트에서 같은가? → No면
browser/onMount로 분리 - DB·비밀키·쿠키가 필요한가? →
+page.server.js - 관리자인가? → SEO 불필요, CSR 패턴이면 충분
4. SEO·성능, 본문에서 챙길 핵심
색인과 체감은 렌더링 선택과 한 세트입니다. 자세한 CWV·캐시·localStorage 설명은 부록 D·E를 참고하세요.
크롤링·메타
- 색인할 페이지는 첫 HTML에 본문 (빈 껍데기 CSR만으로는 부족)
- 관리자·개인 영역은
noindex검토 - 게시글·상품: 고유 title/description, canonical, OG, (필요 시) JSON-LD
sitemap.xml에 공개 허브·게시글·상품 반영,/admin·API 제외
체감 (Core Web Vitals)
| 지표 | Good | Telepasi에서 짧게 |
|---|---|---|
| LCP | ≤ 2.5초 | SSR로 본문 먼저, LCP 이미지 우선·가벼운 포맷 |
| INP | ≤ 200ms | Hydration·초기화 JS 가볍게 |
| CLS | ≤ 0.1 | 이미지 크기 예약, 스켈레톤, 폰트 덜컥임 완화 |
개인화·캐시
- 누구에게나 같은 내용만 공유 캐시(
s-maxage)에 올리기 - 이름·취향·localStorage 상태는 SSR HTML에 넣지 말고 mount 후 반영
5. 자주 하는 실수 네 가지
window/document/localStorage를 초기 렌더(또는 모듈 최상단)에서 사용Math.random(),Date.now()를 첫 마크업에 넣기 → Hydration mismatch- 전 페이지 SSR 또는 전 페이지 CSR
+page.server.js/+page.js/+layout.server.js역할을 섞어 쓰기
이 네 가지만 지켜도 SSR 장애의 대부분을 줄일 수 있습니다.
마무리
Telepasi에는 검색에 태울 공개 콘텐츠, 거의 고정인 정책 문서, 입력형 추천 도구, 관리자가 함께 있습니다.
- 공개·색인 → SSR
- 고정 문서 → Prerender
- 도구·개인·관리 → CSR
URL이 하는 일에 맞춰 고르면 됩니다. 아래 부록은 “왜 그렇게 동작하는지”가 필요할 때 읽으면 됩니다.
부록
부록 A. SSR·CSR·Prerender 동작 스케치
SSR
브라우저 → HTML 요청
→ Node(SvelteKit)가 load + HTML 생성
→ 채워진 HTML 표시
→ JS 로드 후 Hydration
// +page.server.js
const posts = await db.from('posts').select(...);
return { posts };
장점: 첫 화면·SEO·OG 확정에 유리.
CSR
빈(또는 얇은) HTML → JS → API → 화면
장점: 인터랙션·개인화. 단점: 초기 로딩·SEO 부담.
SvelteKit에서 강제 CSR 예:
export const ssr = false;
또는 SSR 셸 + onMount에서 API.
Prerender
npm run build → build/에 HTML → 정적 서빙
export const prerender = true;
부록 B. Hydration과 Mismatch
Hydration: 서버가 만들어 보낸 HTML에 JS를 붙여 이벤트·상태를 연결하는 과정.
서버 HTML:
<button>좋아요 15</button>
브라우저는 바로 보이지만, JS가 오기 전에는 클릭이 안 될 수 있습니다. 붙은 뒤에야 “Button 컴포넌트”로 살아납니다.
Mismatch: 서버 HTML ≠ 브라우저 첫 렌더.
<p>{Math.random()}</p>
| 피해야 할 것 | 이유 |
|---|---|
Date.now(), locale 시각 |
서버/브라우저 시각 차이 |
window / localStorage |
SSR에 없음 |
Math.random() |
매 실행 다름 |
import { browser } from '$app/environment';
if (browser) { /* window 등 */ }
<script>
import { onMount } from 'svelte';
onMount(() => { /* 브라우저만 */ });
</script>
초기 마크업 값은 서버·브라우저가 같아야 하고, 달라도 되는 값은 mount 뒤로.
부록 C. +page.server.js vs +page.js와 load
| 파일 | 실행 | 할 일 |
|---|---|---|
+page.server.js |
서버만 | DB, 비밀키, 쿠키, 권한 |
+page.js |
서버 + 브라우저 | 공개 API, 범용 fetch, 가공 |
+layout.server.js |
서버 | 공통 세션 등 |
+page.svelte |
서버에서 한 번 렌더 후, 브라우저에서도 동작 | UI |
- 첫 방문: 서버
load→ HTML에 데이터 직렬화 → Hydration - 이후 브라우저에서 페이지 이동:
+page.js의 load는 브라우저에서도 돌고,+page.server.js는 서버로 요청
load({ fetch })의 fetch는 서버에서 상대 URL·쿠키·내부 요청을 맞춰 줍니다. load 안에서는 인자 fetch를 쓰는 것이 안전합니다.
부록 D. Core Web Vitals 요약
구글이 UX를 측정하는 3지표이며 SEO에도 영향을 줍니다. CrUX 기준 **상위 75%**가 Good인지로 평가합니다. Lighthouse·DevTools·Search Console로 확인합니다.
| 지표 | 의미 | Good |
|---|---|---|
| LCP | 가장 큰 콘텐츠가 보이는 시간 | ≤ 2.5초 |
| INP | 클릭 등 → 다음 페인트 지연 (FID 대체, 2024.3) | ≤ 200ms |
| CLS | 레이아웃이 밀리는 누적 | ≤ 0.1 |
LCP: fetchpriority="high", WebP/AVIF, 블로킹 리소스·TTFB 줄이기, SSR로 본문 먼저.
INP: Long Task 분할, 무거운 DOM·리스너 줄이기, Hydration 직후 초기화 가볍게.
CLS: width/height·aspect-ratio, 광고·동적 영역 스켈레톤, font-display: swap.
Telepasi: LCP 아닌 이미지만 lazy, 공개 라우트에 관리 번들 섞지 않기, 홈 위젯은 SSR 뼈대 + 무거운 플레이어는 onMount, 상품·인기글은 short TTL 캐시로 TTFB 보조.
부록 E. 개인화 SSR 캐시와 localStorage
공유 캐시에 개인 HTML을 올리지 말 것. A용 “안녕하세요 ○○님” HTML이 s-maxage에 들어가면 B에게 노출될 수 있습니다.
| 데이터 | 공유 캐시 SSR |
|---|---|
| 게시글·상품처럼 공통 | OK |
| 이름·취향·최근 본 목록 | 금지 또는 private + 브라우저에서 로드 |
localStorage: SSR에서 없음. 첫 마크업은 중립값 → onMount에서 읽고 교체.
<script>
import { onMount } from 'svelte';
/** @type {string} */
let theme = $state('light');
onMount(() => {
theme = localStorage.getItem('theme') ?? 'light';
});
</script>
공개 내용은 SSR, 브라우저만의 상태는 mount 이후.
부록 F. 더 공부하면 좋은 주제
load()실행 순서와 invalidation- SSR 데이터가 HTML에 실려 브라우저로 가는 과정
- 어댑터(Node, Vercel, Cloudflare)와 prerender 조합


주파수 소통방 (0)
로딩 중...