SPA, SSG, SSR 차이: 사전 렌더링부터 하이브리드 렌더링까지

요즘 프론트엔드(front-end) 개발을 하다 보면 SPA, SSG, SSR처럼 비슷해서 헷갈리는 약어를 심심치 않게 듣게 되는데요. 여기에 사전 렌더링(prerendering), 하이드레이션(hydration), ISR 같은 말까지 더해지면 무엇이 무엇인지 정리가 잘 안 되기 마련입니다. 🤔
이 글에서는 세 가지 렌더링(rendering) 방식을 HTML이 언제 어디서 만들어지는가라는 하나의 기준으로 비교해 보고, 요즘 프레임워크들이 이 방식들을 어떻게 섞어 쓰는지까지 살펴보겠습니다.
렌더링 시점으로 보면 한 번에 정리된다
세 방식의 차이는 결국 사용자에게 보여 줄 HTML을 언제, 어디서 만드느냐입니다.
| 방식 | HTML을 만드는 곳 | HTML을 만드는 시점 |
|---|---|---|
| SPA | 브라우저 | 페이지를 열 때 |
| SSG | 빌드 서버 | 배포 전 빌드할 때 |
| SSR | 애플리케이션 서버 | 요청이 들어올 때 |
이 중 SSG처럼 요청이 오기 전에 HTML을 미리 만들어 두는 것을 사전 렌더링이라고 부릅니다. 요즘 프레임워크 문서에서는 SSG라는 말보다 이 표현을 더 자주 볼 수 있는데요. 사이트 전체를 정적으로 만드는 도구라기보다, 페이지 단위로 고를 수 있는 렌더링 옵션 중 하나로 다루기 때문입니다.
SPA: 브라우저에서 렌더링하기
SPA(Single Page Application)는 직역하면 단일 웹페이지로 돌아가는 애플리케이션입니다. 이름만 보면 페이지 하나짜리 간단한 웹사이트로 오해하기 쉬운데요. 실제로는 오히려 전형적인 웹사이트 모델로는 구현하기 어려운 섬세하고 역동적인 웹 애플리케이션인 경우가 많습니다. “와! 이런 서비스를 어떻게 웹으로 구현했지?”라는 인상을 받았다면 SPA일 확률이 높습니다.
SPA에서 서버는 텅 빈 HTML 문서 하나와 JavaScript 코드만 내려주고, 그 뒤의 일은 모두 브라우저가 처리합니다. 사용자가 버튼을 누르면 API를 호출해 데이터를 가져오고, 그 데이터로 HTML 요소를 만들어 화면에 그리는 과정이 전부 JavaScript로 브라우저에서 일어납니다. 그래서 SPA는 페이지 소스 보기를 하면 <body> 아래에 <div> 하나만 허전하게 있는 경우가 많습니다. 😅
<body>
<div id="root"></div>
</body>
모든 렌더링이 클라이언트에서 일어나기 때문에 이 방식을 CSR(Client-Side Rendering)이라고도 합니다. 뒤에서 볼 SSR(Server-Side Rendering)과 반대되는 개념입니다.
SPA는 React, Svelte, Vue처럼 요즘 많이 쓰는 대부분의 JavaScript 라이브러리로 만들 수 있습니다. 처음에 JavaScript를 한 번 내려받으면 그 뒤로는 화면 전체를 새로 받을 필요가 없어서, 페이지마다 끊기는 느낌 없이 네이티브(native) 애플리케이션 같은 사용자 경험을 줄 수 있습니다. 웹에서 자주 쓰는 지도 서비스를 떠올리면 이해가 쉽습니다.
다만 JavaScript에 지나치게 의존한다는 점이 약점입니다. 내려받아야 하는 코드가 많아서 네트워크 상태에 따라 첫 화면이 늦게 뜰 수 있고, JavaScript가 실행되지 않는 환경에서는 아무것도 보이지 않습니다.
검색 엔진 최적화(SEO)도 고민거리인데요. Google은 JavaScript를 실행해서 SPA 페이지도 읽을 수 있지만, 모든 크롤러가 그렇지는 않습니다. 특히 요즘 늘어난 AI 크롤러가 문제입니다. Vercel이 2024년 12월에 발표한 AI 크롤러 분석에 따르면 GPTBot, ClaudeBot을 비롯한 주요 AI 크롤러는 JavaScript를 렌더링하지 않았습니다. Google 인프라를 쓰는 Gemini와 브라우저 기반으로 수집하는 AppleBot 정도만 예외였습니다. ChatGPT나 Claude 같은 AI 서비스에서 내 콘텐츠가 인용되길 바란다면, 내용이 HTML에 처음부터 들어 있어야 한다는 뜻입니다.
SSG: 빌드할 때 렌더링하기
SSG는 원래 정적 사이트 생성기(Static Site Generator)라는 도구를 가리키는 말이었는데, 요즘은 그 도구가 하는 일인 정적 사이트 생성(Static Site Generation), 즉 빌드할 때 HTML을 미리 만들어 두는 렌더링 방식을 가리키는 경우가 많습니다. 누가 접속하든 똑같은 내용을 보여 주는 페이지에 잘 맞아서 블로그, 문서 사이트, 제품 소개 페이지처럼 내용이 자주 바뀌지 않는 사이트에 많이 쓰입니다.
대표적인 도구로는 Astro, Hugo, Jekyll, Eleventy 등이 있습니다. 한때 React 진영의 대표 주자였던 Gatsby는 Netlify에 인수된 뒤로 개발 활동이 크게 줄어서, 새 프로젝트라면 다른 도구를 고르는 편이 좋습니다. 블로그를 직접 만들어 보고 싶다면 정적 사이트 생성기로 블로그 시작하기도 참고해 보세요.
SSG로 만든 사이트는 모든 페이지를 미리 만들어 두고, 요청이 오면 만들어 둔 HTML 파일을 그대로 응답합니다. 요청할 때마다 할 일이 없으니 매우 빠르고, CDN(Content Delivery Network)에 올려 사용자와 가까운 곳에서 바로 내려줄 수도 있습니다. 페이지마다 완성된 HTML이 있으니 검색 엔진과 AI 크롤러가 읽기에도 가장 좋은 구조입니다.
빌드할 때 데이터를 가져오는 방식이라 헤드리스 CMS(headless CMS)와도 궁합이 좋은데요. 콘텐츠 담당자가 CMS에 글을 쓰면, 빌드할 때 API로 콘텐츠를 모두 읽어 와 페이지로 만들어 냅니다.
가장 큰 약점은 내용이 바뀔 때마다 다시 빌드해야 한다는 점입니다. 사이트가 커질수록 빌드 시간이 길어지고, 바뀐 내용이 반영되기까지 오래 걸립니다. 이 문제를 풀려고 나온 것이 ISR(Incremental Static Regeneration)입니다. Next.js에서 시작된 방식으로, 페이지를 미리 만들어 두되 정해 둔 시간이 지나면 요청이 들어왔을 때 그 페이지만 백그라운드에서 다시 만들어 교체합니다. 전체를 다시 빌드하지 않고도 정적 페이지를 최신으로 유지할 수 있는 거죠.
SSR: 요청할 때 렌더링하기
SSR(Server-Side Rendering)은 말 그대로 서버에서 페이지를 렌더링합니다. SSG와 다른 점은 빌드할 때 미리 만들어 두는 게 아니라, 요청이 들어올 때마다 그 자리에서 HTML을 만든다는 것입니다.
그래서 바뀐 데이터가 즉시 반영되고, 로그인한 사용자마다 다른 화면을 보여 주기도 쉽습니다. 데이터가 수시로 바뀌고 개인화가 필요한 쇼핑몰이나 소셜 서비스, 대시보드에 잘 맞는 방식입니다.
요즘의 SSR을 이해하려면 하이드레이션이라는 개념을 알아야 합니다. 서버가 만든 HTML은 처음에는 보기만 할 수 있는 정적인 화면입니다. 브라우저가 이어서 JavaScript를 내려받아 이 HTML에 이벤트 핸들러와 상태를 연결해야 버튼이 눌리고 화면이 반응하는데, 이 과정을 하이드레이션이라고 합니다. 즉 요즘 프레임워크의 SSR은 첫 화면은 서버가 빠르게 그려 주고, 그 뒤로는 SPA처럼 동작하는 방식입니다. 서버가 HTML을 다 만들 때까지 기다리지 않고 준비된 부분부터 조각조각 보내는 스트리밍(streaming) 렌더링도 이제는 대부분의 프레임워크가 지원합니다.
SSR은 요청마다 렌더링을 해야 하니 애플리케이션 서버가 필요하고, 정적 파일만 내려주는 것보다는 응답이 느릴 수 있습니다. 예전에는 이게 직접 서버를 운영할 수 있는 중대형 서비스의 이야기였는데요. 요즘은 Cloudflare Workers 같은 서버리스 플랫폼에서 사용자와 가까운 곳에 서버 코드를 띄울 수 있어서, 개인 프로젝트에서도 SSR을 부담 없이 쓰는 경우가 많아졌습니다.
SSR은 보통 UI 라이브러리 위에 얹은 메타 프레임워크로 구현합니다. React 기반으로는 Next.js와 TanStack Start, 그리고 Remix를 흡수한 React Router v7이 있고, Svelte 기반의 SvelteKit, Vue 기반의 Nuxt가 대표적입니다.
요즘은 섞어 쓴다: 하이브리드 렌더링
여기까지 보면 세 방식 중 하나를 골라야 할 것 같지만, 요즘 프레임워크는 대부분 섞어 쓰는 하이브리드(hybrid) 렌더링을 기본으로 합니다. 섞는 단위에 따라 크게 세 가지로 나눠 볼 수 있습니다.
우선 페이지 단위로 섞는 방식입니다. 소개 페이지와 블로그 글은 사전 렌더링하고, 로그인한 사용자의 대시보드는 SSR로 두는 식인데요. SvelteKit은 페이지 파일에서 한 줄로 고를 수 있습니다.
export const prerender = true;
Astro와 TanStack Start도 페이지마다 사전 렌더링할지 고를 수 있습니다. 이렇게 섞어 쓰면 사전 렌더링된 페이지는 정적 파일로, 나머지는 서버 코드로 처리되기 때문에 호스팅 플랫폼 설정과 맞물리는 부분이 생기는데요. URL 끝 슬래시를 예로 이 문제를 트레일링 슬래시 글에서 직접 확인해 봤습니다. 같은 이유로 서버 미들웨어에 넣은 인증 검사나 응답 헤더, 리디렉션 규칙도 사전 렌더링된 페이지에는 적용되지 않을 수 있어서, 이런 처리는 플랫폼 설정에 따로 적어 줘야 합니다. 리디렉션 규칙을 플랫폼 파일로 관리하는 방법은 _redirects 글에서 정리했습니다.
다음은 한 페이지 안에서 컴포넌트 단위로 섞는 방식입니다. Astro의 아일랜드(islands) 아키텍처가 대표적인데요. 페이지 대부분은 JavaScript 없는 정적 HTML로 두고, 상호작용이 필요한 컴포넌트만 골라 하이드레이션합니다.
---
import Counter from "../components/Counter.jsx";
---
<h1>정적인 제목</h1>
<Counter client:visible />
client:visible은 컴포넌트가 화면에 보일 때 JavaScript를 내려받아 하이드레이션하라는 뜻입니다. 반대로 server:defer를 붙이면 그 컴포넌트만 나중에 서버에서 렌더링하는 서버 아일랜드(server islands)가 됩니다. 페이지 전체는 정적으로 빠르게 내려주고, 사용자 아바타처럼 개인화된 작은 영역만 따로 채우는 거죠.
마지막으로 정적인 껍데기와 동적인 내용을 한 응답 안에서 섞는 방식입니다. Next.js의 부분 사전 렌더링(Partial Prerendering, 이하 PPR)인데요. 빌드할 때 페이지의 정적인 부분을 HTML 껍데기로 미리 만들어 두고, 요청이 오면 그 껍데기를 즉시 보낸 뒤 동적인 부분만 렌더링해서 스트리밍으로 채웁니다. Next.js는 이 방식을 캐시 컴포넌트(Cache Components) 기능의 기본 동작으로 삼고 있습니다.
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
cacheComponents: true,
};
export default nextConfig;
결국 SPA, SSG, SSR은 이제 프레임워크를 고르는 기준이라기보다, 한 애플리케이션 안에서 페이지나 컴포넌트마다 고르는 옵션에 가까워졌습니다.
무엇을 고를까?
그럼 내 프로젝트에서는 어떻게 고르면 좋을까요? 기준은 페이지마다 “내용이 누구에게나 같은가”, “얼마나 자주 바뀌는가”입니다.
| 페이지 성격 | 추천 방식 |
|---|---|
| 누구에게나 같고 거의 안 바뀜 (블로그, 문서, 소개 페이지) | SSG |
| 누구에게나 같지만 자주 바뀜 (상품 목록, 뉴스) | ISR 또는 SSR |
| 사용자마다 다름 (대시보드, 장바구니) | SSR |
| 검색 노출이 필요 없고 상호작용이 많음 (관리자 도구, 에디터) | SPA |
블로그나 문서처럼 콘텐츠가 중심인 사이트라면 SSG가 성능과 SEO 모두에서 가장 유리합니다. 개인화가 필요한 서비스라도 로그인 전에 보이는 랜딩 페이지는 SSG로 미리 만들어 두고, 로그인 뒤의 화면만 SSR이나 SPA로 두는 식으로 섞으면 됩니다. 검색 엔진이나 AI 서비스에 노출되어야 하는 페이지라면, 어떤 방식을 고르든 HTML에 내용이 처음부터 들어 있는지 꼭 확인하세요.
마치며
PHP, ASP, JSP로 웹을 만들던 시절에는 서버에서 HTML을 만드는 게 당연했습니다. 그러다 AngularJS, React, Vue가 등장하면서 브라우저에서 렌더링하는 SPA가 한동안 대세였고, 다시 SSR과 사전 렌더링이 주목받고 있습니다. 웹 개발도 유행이 참 돌고 돈다는 생각이 들지 않으신가요? 😄
그래도 예전과 크게 달라진 점이 있는데요. 이제는 한 가지 방식을 고수할 필요 없이, 페이지마다 또는 컴포넌트마다 가장 알맞은 방식을 고를 수 있게 되었다는 겁니다. 선택지가 늘어난 만큼 고민할 것도 많아졌지만, HTML을 언제 어디서 만드는가라는 기준 하나만 잡고 있으면 새로운 용어가 나와도 어렵지 않게 자리를 찾을 수 있을 겁니다.
렌더링 방식별 개념을 더 자세히 보고 싶다면 web.dev의 Rendering on the Web을 참고하세요.
This work is licensed under CC BY 4.0