hyunsu blog

홈노트

MPA·SPA 와 CSR·SSR·SSG 는 서로 다른 축이다

목차 · 5
  1. 01MPA 와 SPA 는 페이지를 옮기는 방식이 다르다
  2. 02CSR·SSR·SSG 는 HTML 을 만드는 곳과 때가 다르다
  3. 03SSR 도 결국 자바스크립트를 보낸다
  4. 04SSG 와 SSR 사이에는 ISR 이 있다
  5. 05배운 것: 축부터 나누고, 병목을 재고 고른다

MPA·SPA 는 페이지를 옮길 때 문서를 새로 받느냐의 구분이고, CSR·SSR·SSG(브라우저·서버·빌드 시점 렌더링)는 HTML 을 어디서·언제 만드느냐의 구분이다. 둘은 서로 다른 축이다. 그래서 "SPA 인데 SSR" 같은 조합이 얼마든지 나오고, 다섯 용어를 한 줄에 세워 비교하면 헷갈릴 수밖에 없다.

01 MPA 와 SPA 는 페이지를 옮기는 방식이 다르다

MPA(Multi-Page Application)는 링크를 누를 때마다 새 HTML 문서를 받아 화면을 통째로 바꾼다. SPA(Single-Page Application)는 MDN 정의대로 문서 하나만 불러온 뒤, 다른 내용을 보여 줄 때 Fetch 같은 자바스크립트 API 로 그 문서의 본문을 고쳐 쓴다. Chrome 의 View Transition 문서도 같은 선을 긋는데, 한 문서 안의 전환은 SPA 에서, 문서와 문서 사이의 전환은 MPA 에서 흔하다고 설명한다. 대신 SPA 에는 대가가 따른다. MDN 은 검색 노출(SEO), 상태 관리, 내비게이션 구현, 성능 모니터링에 드는 품을 꼽는다.

02 CSR·SSR·SSG 는 HTML 을 만드는 곳과 때가 다르다

web.dev 의 Rendering on the Web과 MDN 용어집을 합치면 표 하나로 정리할 수 있다. web.dev 는 SSG 를 정적 렌더링(static rendering)이라고 부른다.

방식만드는 곳·때장점약점
CSR브라우저, JS 실행 시화면 전환에 전체 새로고침이 없다앱이 커질수록 JS 가 늘어 INP 가 나빠질 수 있다
SSR서버, 요청마다받은 HTML 에 내용이 있어 첫 화면이 빨리 뜬다요청마다 만들어 TTFB 가 늘 수 있다
SSG빌드 시점, URL 마다 파일 하나TTFB 가 늘 낮고 CDN 에 올리기 쉽다가능한 URL 을 다 만들어야 하고 개인화에 맞지 않는다

TTFB(Time to First Byte)는 첫 바이트가 오기까지의 시간, INP(Interaction to Next Paint)는 입력 뒤 화면이 다시 그려지기까지의 지연이다. 검색 노출은 CSR 이라고 바로 막히지 않는다. 구글은 상태 코드가 200인 페이지를 렌더링 대기열에 넣고, 헤드리스 크로미움으로 자바스크립트를 실행한 결과를 색인한다. 그래도 MDN 은 소셜 미디어 크롤러가 대개 JS 를 실행하지 못한다고 짚고, 구글 문서도 서버 렌더링이나 사전 렌더링을 여전히 좋은 선택으로 권한다.

03 SSR 도 결국 자바스크립트를 보낸다

SSR 로 받은 HTML 은 화면을 먼저 보여 줄 뿐이다. 컴포넌트가 입력에 반응하려면 클라이언트 스크립트가 돌면서 상태와 상호작용을 붙여야 하고, 이 과정을 하이드레이션(hydration)이라 부른다. web.dev 는 이 구간을 SSR 의 주된 약점으로 든다. 페이지는 준비된 것처럼 보이는데 입력에는 반응하지 않고, 모바일에서는 그 시간이 꽤 길어질 수 있다.

하이드레이션이 있어서 SPA 가 곧 CSR 인 것은 아니다. MDN 은 React·Vue·Svelte 같은 프레임워크로 SSR 을 하는 SPA 를 만들 수 있다고 설명한다. 첫 화면은 서버가 그리고, 그 뒤 이동은 한 문서 안에서 처리하는 식이다.

04 SSG 와 SSR 사이에는 ISR 이 있다

ISR(Incremental Static Regeneration)은 사이트 전체를 다시 빌드하지 않고 정적 페이지를 갱신하는 Next.js 기능이다. 페이지 파일에 갱신 주기를 한 줄 적으면 된다. 공식 문서 예제에서 해당 줄만 옮기면 이렇다.

tsx
// app/blog/[id]/page.tsx
// 요청이 들어오면 최대 60초에 한 번 캐시를 무효화한다
export const revalidate = 60

60초가 지난 뒤 처음 온 요청은 캐시에 있던 옛 페이지를 그대로 받고, Next.js 는 그동안 뒤에서 새 버전을 만든다. 생성에 성공하면 그다음 요청부터 새 페이지가 나간다. 실패하면 마지막으로 성공한 결과를 계속 보여 준다. 다만 정적 내보내기(Static Export)로 배포하면 ISR 을 쓸 수 없다.

05 배운 것: 축부터 나누고, 병목을 재고 고른다

이제 화면을 새로 만들 때 질문을 둘로 나눈다. 페이지 이동을 문서 단위로 할지 먼저 정하고, 그다음 페이지마다 HTML 을 언제 만들지 정한다. 글처럼 URL 을 미리 알 수 있는 페이지는 SSG, 사용자마다 내용이 달라지는 페이지는 SSR, 로그인 뒤 상호작용이 많은 화면은 CSR 을 출발점으로 삼는다.

web.dev 의 결론도 같은 쪽을 가리킨다. 병목부터 측정하고, 정적 렌더링이나 SSR 로 대부분 해결되는지 따져 보라는 것이다. HTML 위주로 보내고 자바스크립트는 조금만 써도 괜찮다고 덧붙인다. Next.js·Nuxt 처럼 두 방식을 다 지원하는 프레임워크라면 한 사이트 안에서도 페이지마다 다르게 고르면 된다.