앞에 쓰는 말
React, Vue 등 모던한 프론트엔드 프레임워크의 기치 아래, CSR(Client-Side Rendering) 모드가 사람들의 마음에 깊이 침투해 있습니다:
CSR (Client-Side Rendering) - rendering an app in a browser, generally using the DOM.
프론트엔드 부분은 거의 모두 클라이언트 측에서 동적으로 렌더링됩니다 (클라이언트 측에서 JS 코드를 실행하고 동적으로 DOM 구조를 생성). 예를 들어:
<!DOCTYPE html>
<html>
<head>
<title>My Awesome Web App</title>
<meta charset="utf-8">
</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>
클라이언트 측 로직이 무거울수록, 초기화 시 실행해야 할 JS 가 많아지고, 첫 화면 성능이 느려지므로, 더 많은 렌더링 모드 탐색이 나타났습니다:
-
SSR(Server-Side Rendering): 서버 측 렌더링. 서버 측에서 Web 애플리케이션을 HTML 로 렌더링
-
Rehydration: 이차 렌더링. 서버 측에서 렌더링된 HTML DOM 구조와 데이터를 재이용하여, 클라이언트 측에서 JS 렌더링을 "웜스타트"
-
Prerendering: 프리렌더링. 컴파일 시 클라이언트 애플리케이션을 실행하여 초기 상태를 캡처해 정적 HTML 생성
一.CSR
CSR(Client-side rendering), 즉 클라이언트 측 렌더링은, JS 로直接在 브라우저에서 페이지를 렌더링하는 것을指하며, 데이터 요청, 뷰 템플릿, 라우팅을 포함한 모든 로직이 클라이언트 측에서 처리됩니다:
Client-side rendering (CSR) means rendering pages directly in the browser using JavaScript. All logic, data fetching, templating and routing are handled on the client rather than the server.
렌더링 플로우는 아래 그림과 같습니다:

P.S.그 중에 나타난 FCP 와 TTI 는 두 가지 중요한 성능 지표입니다:
-
FCP(First Contentful Paint): 사용자가 요청한 콘텐츠가 화면에서 가시되는 시점
-
TTI(Time To Interactive): 페이지가 인터랙티브해지는 시점
주요 결점은 애플리케이션의 업데이트 이터레이션에 따라, 클라이언트 측에서 실행하는 JS 코드량이 점점 많아지는 것으로, 사전의 서드파티 라이브러리/프레임워크, polyfill 등이 일정 정도로 첫 화면 성능을 늦추며, (중低端) 모바일 장비에서 특히 현저합니다
Code splitting, lazy-load 등 최적화 조치로 일부 완화할 수 있지만, 최적화 공간은 비교적 제한적이며, 근본적으로 문제를 해결하는 데 도움이 되지 않습니다
이때, 렌더링 모드를 바꾸는 것만이 더 많은 가능성을 창조할 수 있습니다
二.SSR
SSR(Server-Side Rendering) 은 특히 새로운 개념이 아니며, 전후단分层 이전의 긴 기간 동안에는 서버 측 렌더링이 주류였습니다 (JSP, PHP). 서버 측에서 완전한 HTML 페이지를 생성:
Server rendering generates the full HTML for a page on the server in response to navigation.
클라이언트 측의 이차 요청 데이터의 네트워크 오버헤드와, 뷰 템플릿을 렌더링하는 성능 부담을 덜어줍니다. CSR 과 비교하여, 그 FCP, TTI 는 일반적으로 더 빠릅니다:

P.S.한편으로, 서버 측의 네트워크 환경은 클라이언트 측보다 우수하며, 내부 서버 간 통신 경로도 더 짧습니다
페이지 로직 (즉시 데이터 요청 포함) 과 템플릿 렌더링 작업이 서버 측에서 완료되므로, 클라이언트 측의 JS 코드량이 줄어들고, 스트리밍 문서 파싱 (streaming document parsing) 등의 브라우저 최적화 메커니즘도 그 역할을 발휘할 수 있어,低端 장비와 약한 네트워크 상황에서 더 좋은 성능을 발휘합니다. 하지만서버 측에서 페이지를 생성하는 데도 시간이 필요하여, 페이지 콘텐츠 응답 시간 (TTFB, Time to First Byte) 이 느려지는 원인이 됩니다
한 가지 해결책은 스트리밍 SSR, [컴포넌트 레벨 캐시](https://medium.com/ @reactcomponentcaching/speedier-server-side-rendering-in-react-16-with-component-caching-e8aa677929b1), 템플릿화, HTML 캐시 등의 기술로 더욱 최적화하는 것입니다
다른 한 가지 해결책은 렌더링 모드 위에서 탐색을 계속하고, 정적 렌더링 (Static Rendering) 을 채택하는 것입니다
三.Static Rendering
HTML 페이지를 생성하는 작업을 컴파일 시에 두고, 요청이 왔을 때 동적으로 완료할 필요가 없습니다. 각 URL 에 대해 개별적으로 HTML 파일을 사전 생성하고, 더욱이 CDN 을 활용해 액세스를 가속:
Static rendering happens at build-time and offers a fast First Paint, First Contentful Paint and Time To Interactive - assuming the amount of client-side JS is limited. Unlike Server Rendering, it also manages to achieve a consistently fast Time To First Byte, since the HTML for a page doesn't have to be generated on the fly.
렌더링 플로우는 아래 그림과 같습니다:

P.S.SSR 첫 부분의 Server Rendering 렌더링 작업이 Streaming 으로 정적 HTML 파일을 전달하는 것으로 바뀝니다
정적 렌더링도 완벽하지 않으며, 그 중요한 문제는*「정적」*에 있습니다:
-
각 URL 에 대해 개별적으로 HTML 파일을 생성해야 함: 모든 가능한 URL 을 사전에 알 수 없거나, 대량의 다른 페이지가 존재하는 사이트의 경우, 정적 렌더링은 쉽지 않으며,甚至 근본적으로 실행 불가능
-
정적 콘텐츠 지향: 동적, 개인화된 콘텐츠에는 효과가 크지 않음
또한, 정적 렌더링과 비슷한 개념에, 프리렌더링 (Prerendering) 이 있습니다
Prerendering
주요 차이점은, 정적 렌더링으로 얻은 페이지는 이미 인터랙티브하여, 클라이언트 측에서 대량의 JS 코드를 추가로 실행할 필요가 없는 반면, 프리렌더링은 클라이언트 측 렌더링을 거쳐야만 진정으로 인터랙티브해진다는 것입니다:
Static rendered pages are interactive without the need to execute much client-side JS, whereas prerendering improves the First Paint or First Contentful Paint of a Single Page Application that must be booted on the client in order for pages to be truly interactive.
즉, JS 를 비활성화한 후, 정적 렌더링의 페이지는 거의 영향을 받지 않지만, 프리렌더링의 페이지는 하이퍼링크 등의 기본 기능만 남게 됩니다
四.Rehydration
Rehydration 모드는 CSR 과 SSR 을 결합한 것으로, 서버 측에서 기본 내용을 렌더링한 후, 클라이언트 측에서 이차 렌더링 (Rehydration) 을 수행합니다:
Often referred to as Universal Rendering or simply "SSR", this approach attempts to smooth over the trade-offs between Client-Side Rendering and Server Rendering by doing both. Navigation requests like full page loads or reloads are handled by a server that renders the application to HTML, then the JavaScript and data used for rendering is embedded into the resulting document.
예를 들어:

실제 렌더링 플로우는 다음과 같습니다:

주의bundle.js는 여전히전량의 CSR 코드이며, 이러한 코드가 실행 완료되어야 페이지가 진정으로 인터랙티브해집니다. 따��서, 이 모드에서는, FP(First Paint) 는 향상되지만, TTI(Time To Interactive) 는 느려질 수 있습니다. 클라이언트 측에서 이차 렌더링이 완료되기 전에는, 페이지가 사용자 입력에 응답할 수 없기 때문입니다 (JS 코드 실행으로 블록됨)
이차 렌더링으로 인한 인터랙션이 응답하지 않는 문제에 대해, 가능한 최적화 방향은 인크리멘탈 렌더링(예를 들어 React Fiber), 및 프로그레시브 렌더링/부분 렌더링 입니다
Trisomorphic Rendering
[Service Worker](/articles/理解 web-workers/) 도 고려에 넣는다면, 삼자涉及의 렌더링 모드 도 있습니다:
SSR + CSR + ServiceWorker rendering = Trisomorphic Rendering
아래 그림과 같습니다:

먼저 스트리밍 SSR 로 초기 페이지를 렌더링하고, 다음으로 Service Worker 가 라우팅 규칙에 따라, SSR 을 활용해 타겟 HTML 페이지를 렌더링:
It's a technique where you can use streaming server rendering for initial/non-JS navigations, and then have your service worker take on rendering of HTML for navigations after it has been installed. This can keep cached components and templates up to date and enables SPA-style navigations for rendering new views in the same session.
주요 장점은 삼자 간에 템플릿 렌더링과 라우팅 제어 로직을 공유할 수 있다는 것입니다:
This approach works best when you can share the same templating and routing code between the server, client page, and service worker.
五.정리
각 렌더링 모드에는 일정의 장점이 있으며, 그 한계와 결점도 있습니다. 실제 장면에서는多种의 요소 아래에서权衡하여 선택해야 합니다:

아직 댓글이 없습니다