CSR SSR SSG 뽀개기
목표
- CSR/SSR/SSG의 개념과 동작 방식을 알아보자
- 각각의 장단점을 알아보자
- (심화) 성능 지표로 렌더링 방식을 비교해보자
CSR
개념
- Client-side Rendering
- 브라우저에서 JavaScript를 실행해 모든 DOM을 렌더링한다.
- SPA(Single Page Application)로 동작한다.
동작 방식
- 첫 페이지 로드 시 클라이언트는 CDN에서 script, style 태그만이 포함된 HTML 페이지를 로드받는다. (유저 입장에서는 빈 페이지가 보이게 된다)
<html>
<head>
...
</head>
<body>
<div id="root">
<!-- 이 시점엔 내부 컨텐츠가 비어있어요. -->
</div>
</body>
</html>
- 로드된 HTML 페이지의 script 태그 내용 기반으로 JavaScript 번들을 불러온다.
- 불러온 번들을 브라우저에서 실행해 컨텐츠가 로드된다.
- 데이터 fetch 또한 브라우저에서 실행된다.
- 유저가 새로운 페이지로 이동하면 새로운 레이아웃이 바로 렌더링된다. (2번 과정에서 모든 JavaScript 번들을 불러왔기 때문)
- 이후의 데이터 fetch 도 브라우저에서 실행된다.
사용 예시
- React, Vue, Angular 등의 기본 동작
(Full) SSR
개념
- Server-side Rendering
- 서버에서 페이지 로직과 렌더링을 모두 수행 후, 브라우저에 정적 HTML을 전달한다.
동작 방식
- 클라이언트가 서버에 첫 번째 페이지를 요청한다.
- 서버에서 필요한 데이터를 fetch 후, 컨텐츠를 렌더링한다.
- 클라이언트에서는 컨텐츠가 모두 렌더링된 정적 HTML을 전달받는다.
- 유저가 새로운 페이지로 이동하면 1-3번까지의 과정을 다시 반복한다.
Universal Rendering
개념
- SSR + CSR
- 유저의 첫 요청 시에만 SSR을 수행하고, hydration 과정을 거친 후엔 CSR처럼 동작한다.
- hydration: 브라우저의 앱 상태를 페이지 렌더링 중 서버에서 생성한 상태와 동기화하는 작업
- 요즘 문서들에서는 SSR = Universal Rendering 으로 설명하는 케이스가 많은 것 같다.
동작 방식
- 클라이언트가 서버에 첫 번째 페이지를 요청한다.
- 서버에서 필요한 데이터를 fetch 후, 컨텐츠를 렌더링한다.
- 클라이언트에서는 컨텐츠가 모두 렌더링된 정적 HTML을 전달받는다. (SSR 동작 방식의 1-3번과 동일)
- 추가적으로, JavaScript 번들을 fetch해 hydration 과정을 거친다. (이 과정을 거치기 전까진, JavaScript 동작을 사용할 수 없다.)
- hydration 이후, 유저는 페이지와 상호작용할 수 있으며 앱은 SPA처럼 동작한다.
- 새로운 페이지로 이동하면 새로운 레이아웃이 바로 렌더링된다.
예시
- Next.js, Nuxt.js, Angular Universal
SSG
개념
- Static Site Generation
- 빌드 시점에 페이지 컨텐츠를 모두 미리 생성해둔다.
동작 방식
- 빌드 시점에 렌더링 및 데이터 fetch를 모두 완료해 모든 페이지가 정적인 상태로 존재한다.
- 클라이언트는 CDN으로부터 미리 렌더링된 페이지를 즉시 전송한다.
- 유저가 새 페이지로 이동해도 2번의 동작을 반복한다.
예시
- Gatsby, Next.js(getStaticProps, getStaticPaths)
ISR
- Incremental Static Regeneration
- SSG의 기본 동작과 동일하지만, 일정 시점마다 데이터를 다시 fetch한다.
- Next.js 에서는 getStaticProps 의 revalidate 속성을 사용할 수 있다.
장단점
- 앱의 모든 정적 리소스를 불러와야 하는 CSR에 비해, 현재 페이지에 대한 리소스만 불러오면 되는 SSR이 첫 번째 페이지 로드가 일반적으로 빠르다.
- 그러나 다른 페이지로 이동 시 SSR은 서버에서 새롭게 리소스를 불러와야 하는데, CSR은 SPA, 즉 여러 페이지처럼 보이지만 실상 하나의 앱이기 때문에 추가 리소스를 불러올 필요 없이 바로 페이지가 렌더링된다.
- CSR에는 SEO(검색 엔진 최적화) 이슈가 존재한다. JavaScript 분석 능력이 없는 크롤러 봇들은 초기 로딩 시 빈 HTML 컨텐츠가 로드되는 CSR 환경에서 정보를 잘 인식하지 못한다.
- SSR, CSR 각각의 단점을 해소하고 장점은 챙기고 싶어 나오게 된 방식이 Universal Rendering이다.
- 하지만 Universal Rendering 에도 hydration 과정 전까지는 페이지와 상호작용할 수 없다는 단점이 존재한다.
- SSG는 첫 번째 페이지 로드 시에도, 다른 페이지로 이동 시에도 빠르다는 장점이 있지만 빌드 시점에 데이터 fetch 가 이루어지기 때문에 항상 최신 데이터를 유지해야 하는 페이지에선 사용할 수 없다.
이런 상황, 이런 방식
- Next.js 등의 라이브러리에서는 페이지에 따라 적절한 렌더링 방식을 사용하도록 권장 하고 있다.
- 정적인 데이터만 보여주는 페이지(개발 블로그, 랜딩 페이지): SSG
- SEO가 필요하거나 초기 렌더링 속도가 중요한 페이지: SSR(Universal Rendering)
- SEO가 필요없고 유저 인터렉션이 많은 페이지(관리자 대시보드): CSR
성능 지표로 렌더링 방식 비교
용어
- FCP(First Contentful Paint): 로드부터 첫 컨텐츠 렌더링까지의 시간
- TTI(Time to Interactive): 로드부터 인터렉션이 가능해질때까지의 시간
- TBT(Total Blocking Time): FCP ↔ TTI (첫 컨텐츠 렌더링 시점부터 인터렉션 가능해질때까지의 시간)
- INP(Interaction to Next Paint): 사용자 인터렉션에 대한 전반적인 속도 측정
- TTFB(Time to First Byte): 링크 클릭 후 첫 컨텐츠 렌더링까지의 시간
비교
- CSR: FCP 👎, FCP <<< TTI (TBT, INP 👎), TTFB 👍
- SSR: FCP 👍, FCP = TTI (TBT, INP 👍), TTFB 👎
- Universal Rendering: FCP 👍, TTI >>> FCP (TBT, INP 👎), TTFB 👍