Telepasi

검색하기 전에 통하다

SvelteKit SSR·CSR·Prerender 실무 가이드|Telepasi.com 기준으로 고르는 법

강병우
2시간 전 👁️ 3

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. 새 페이지 고르는 체크리스트

  1. 검색·공유로 들어오나? → Yes면 SSR 또는 Prerender
  2. 데이터가 자주/요청마다 바뀌나? → Yes면 SSR, No면 Prerender 후보
  3. 로그인한 나만 보나? / 추천·설정 도구인가? → CSR
  4. 첫 HTML에 넣을 값이 서버·클라이언트에서 같은가? → No면 browser / onMount로 분리
  5. DB·비밀키·쿠키가 필요한가? → +page.server.js
  6. 관리자인가? → 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. 자주 하는 실수 네 가지

  1. window / document / localStorage를 초기 렌더(또는 모듈 최상단)에서 사용
  2. Math.random(), Date.now()를 첫 마크업에 넣기 → Hydration mismatch
  3. 전 페이지 SSR 또는 전 페이지 CSR
  4. +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
  1. 첫 방문: 서버 load → HTML에 데이터 직렬화 → Hydration
  2. 이후 브라우저에서 페이지 이동: +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)

로딩 중...