렌더링 & 프레임워크
SPA란 무엇인가 — 페이지 전환을 브라우저가 맡는다는 것
한 줄 요약
SPA는 첫 로드 시 HTML/JS 번들만 받고, 이후 페이지 전환을 서버 요청 없이 브라우저 JS로 처리하는 방식. SvelteKit 기본은 "첫 응답은 SSR, 이후 전환은 SPA"인 하이브리드다.
왜 알아야 했나
- SvelteKit을 쓰면서 "이거 항상 SPA 아닌가?"라는 의문이 생김. 라우팅 전환 시
<html>이 깜빡이지 않고, 네트워크 탭에 HTML 요청도 없었기 때문. - 실제로는 전환 동작만 SPA와 같았던 것이고, 첫 응답이 SSR이냐 빈 껍데기냐가 핵심 구분점이라는 걸 알게 됨.
- streaming SSR 페이지에서 JS를 끄니 데이터가 화면에 안 나오는 현상을 만나면서 progressive enhancement의 동작 원리까지 파고들게 됨.
핵심 개념
1. SPA vs MPA
- MPA (전통적): 링크 클릭마다 서버에 새 HTML 요청 → 전체 페이지 새로고침(깜빡임).
- SPA: 처음에 JS 앱 전체를 받고, 이후 전환은 서버에 HTML 재요청 없이 JS가 화면 일부만 교체(필요한 데이터는 JSON 등으로). 깜빡임 없음.
2. SvelteKit은 하이브리드 (SSR + SPA)
| 첫 진입 / 새로고침 시 서버 응답 | 이후 페이지 전환 (JS 켜짐) | |
|---|---|---|
| SvelteKit 기본 | 완성된 HTML (SSR) | SPA처럼 JS 라우팅 |
| 순수 SPA (adapter-static fallback, ssr=false) | 빈 HTML 껍데기 | SPA처럼 JS 라우팅 |
- 전환 동작만 보면 둘 다 같아서 구분 안 됨. 차이는 오직 "첫 응답에 콘텐츠가 들어있느냐".
3. 구분이 중요한 이유
- SEO / 링크 미리보기: 크롤러·공유 미리보기(카톡/트위터)는 첫 응답 HTML만 봄. SSR은 콘텐츠가 보이고, 순수 SPA는 빈 화면.
- 초기 렌더링 속도: SSR은 HTML이 바로 보이고 JS가 뒤에서 hydration. SPA는 JS 다 받고 실행해야 첫 화면이 뜸.
4. Progressive Enhancement (점진적 향상)
SvelteKit의 설계 철학. JS는 필수가 아니라 경험을 향상시키는 레이어.
- SvelteKit이 만든 링크는 결국 평범한
<a href="...">. - JS 켜짐: 클라이언트 라우터가 클릭을 가로채(
preventDefault) JS로 전환 → SPA 방식, 깜빡임 없음. - JS 꺼짐: 가로챌 JS가 없으니 브라우저가
<a>기본 동작 실행 → 서버에 새 HTML 요청, 전체 새로고침 → MPA 방식, 깜빡임 있음. - 즉 JS 꺼도 SSR 덕분에 콘텐츠 표시 + 링크 이동은 그대로 동작한다 (단, 전환은 SPA가 아닌 브라우저 기본 방식).
5. Streaming SSR의 예외
- load 함수가 promise를
await안 하고 그대로 반환하면 streaming. - 서버는 HTML 뼈대를 먼저 보내고, promise가 resolve되는 대로 HTML 맨 아래에
<script>__sveltekit_dev.resolve(...)</script>형태로 데이터를 흘려보냄. - 이 데이터를 화면에 꽂는 건 클라이언트 JS(
__sveltekit_dev.resolve). JS 끄면 데이터는 HTML 하단에 텍스트로 도착해 있지만 화면엔 안 그려짐. - progressive enhancement가 적용되되, streaming한 부분만 JS(향상 레이어)에 의존한다.
| await (non-streaming) | streaming (promise 반환) | |
|---|---|---|
| 첫 HTML 응답 | 데이터까지 완성된 HTML | 뼈대만, 데이터는 빈 채로 |
| 데이터 도착 방식 | HTML 안에 이미 박혀있음 | 맨 아래 script로 나중에 흘려보냄 |
| JS 끄면 | 콘텐츠 보임 | 빈 화면 |
예시 / 코드
순수 SPA 모드로 만들기 (adapter-static)
// svelte.config.js
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter({
fallback: 'index.html' // SPA의 핵심: 어떤 경로든 index.html 하나 반환
})
}
};
// src/routes/+layout.js
export const ssr = false; // 서버 렌더링 끔
export const prerender = false;
fallback: 'index.html': 서버는 모든 경로 요청에 빈 index.html을 돌려주고, 그 안의 JS가 현재 URL을 보고 화면을 그림. 서버는 라우팅에 관여 안 함.
Streaming (JS 꺼지면 데이터 안 보임)
// +page.js 또는 +page.server.js
export function load() {
return {
posts: fetchPosts() // await 안 함 → streaming
};
}
Streaming 끄기 (JS 없어도 데이터 보이게)
export async function load() {
return {
posts: await fetchPosts() // await → HTML에 박혀서 나감
};
}
- 트레이드오프: 느린 데이터를 기다리느라 첫 응답이 그만큼 늦어짐.
헷갈렸던 점
- "라우팅할 때 HTML이 안 깜빡이고 네트워크에 HTML 요청도 없으니 SvelteKit은 항상 SPA 아닌가?" → 전환 동작만 SPA와 같을 뿐, 첫 응답은 SSR이라 하이브리드다.
- "JS를 꺼도 라우팅이 되네?" → 그건 SPA 라우팅이 아니라 브라우저의
<a>기본 동작(새 HTML 요청, 전체 새로고침). SSR이 각 경로를 제대로 렌더링해주기 때문에 가능. - "streaming 페이지에서 JS 끄니 데이터가 화면 맨 아래 텍스트로만 보이고 화면엔 안 나옴" → 버그 아님. streaming 데이터를 화면에 꽂는
resolve함수가 JS라서, JS 끄면 안 그려지는 정상 동작.
실제 테스트 결과 ✅
- JS 끈 상태에서 라우팅 → HTML을 새로 불러옴 (network 탭에 새 document 요청), 화면 깜빡임 발생. → 브라우저 기본 동작(MPA 방식)임을 확인.
- JS 다시 켠 상태 → 첫 진입(또는 새로고침)에서만 HTML을 불러오고, 이후 전환은 HTML 요청 없이 JS로 처리. → SPA 방식임을 확인.
- streaming SSR 페이지 + JS 끄고 새로고침 →
__sveltekit_dev.resolve(...)데이터가 HTML 맨 아래에 붙어서 오긴 하는데 화면엔 아무것도 안 나옴. → streaming 데이터는 JS 의존임을 확인.
구분 체크리스트
| 확인 항목 | SPA 전환 (JS 켜짐) | 브라우저 전환 (JS 꺼짐) |
|---|---|---|
| HTML document 요청 | 없음 (데이터만) | 있음 (새 HTML) |
| 화면 깜빡임 | 없음 | 있음 |
<html> 태그 | 안 깜빡임 | 깜빡임(문서 교체) |
빠른 SSR vs 순수SPA 구분법: 크롬 개발자도구 → ⚙️ → Disable JavaScript 체크 후 새로고침. SSR이면 콘텐츠 보임, 순수 SPA면 빈 화면.
실무 적용
- 순수 SPA가 적합: 로그인 후 쓰는 관리자 대시보드/내부 툴(SEO 불필요), 백엔드 API가 따로 있고 프론트만 정적 호스팅하고 싶을 때.
- SSR(하이브리드) 권장: SEO·공유 미리보기가 중요한 블로그/마케팅/커머스.
- streaming 사용 시 주의: 느린 데이터로 첫 화면을 막고 싶지 않을 때 유용하지만, 크롤러·JS 비활성 환경에서 그 데이터가 꼭 보여야 한다면
await로 전환할 것. - 관련 작업: [[SvelteKit Adapter]]
더 공부할 것
- Hydration이 정확히 어떻게 일어나는지 (서버 HTML + 클라이언트 JS 결합)
-
ssr/prerender/csr페이지 옵션 각각의 동작 차이 - adapter-static fallback SPA에서 SEO 보완 방법 (prerender 가능한 페이지만 미리 렌더링)
- streaming SSR에서 로딩 UI(
{#await}) 처리 패턴 - progressive enhancement form actions (
use:enhance)
🔗 참고
- SvelteKit 공식 문서: Page options (ssr/csr/prerender)
- SvelteKit 공식 문서: Loading data (streaming with promises)
- SvelteKit 공식 문서: Adapters (adapter-static, adapter-node)