소개
웹에 컨텐츠를 표현하는 방법은 다양한다. 어떻게 혹은 어디서 데이터를 받아오고 컨텐츠를 렌더링 하는지를 결정하는 것은 앱의 성능을 좌우하는 핵심 포인트가 되었다. 클라이언트 사이드 렌더링, 정적 렌더링, 하이드레이션, 점진적 렌더링, 서버 사이드 렌더링 등 각기 다른 방법을 사용하기 위해 프레임웍 혹은 라이브러리들이 사용된다. 이 때 각 패턴의 영향과 결과를 이해하는 것은 앱에 적합한 방식을 찾기 위해 꼭 필요하다.
크롬 개발팀은 Rehydration 접근 방식보다 정적 렌더링이나 서버 사이드 렌더링을 고려하도록 권장했다. 지난 시간동안 현대의 프레임웍들은 점진적 로딩과 여러 렌더링 기법들을 기본으로 구현하였고. 좋은 성능과 새 기술의 적용 모두 고루고루 발전하였다.
이어지는 섹션에서는 웹 렌더링과 관련되어 웹 렌더링의 성능 요구사항을 측정하는 가이드라인과 각 요구사항을 만족하는 최적의 패턴을 제안한다. 그 다음 각 패턴들을 깊게 살펴보고 구현 방법에 대해서 알아본다. 또 이런 패턴들을 구현해볼 수 있는 Next.js 에 대해서 이야기 해 볼 것이다. 하지만 패턴들과 Next.js에 대해 다루기 전에, 먼저 이런 것들을 고려하기까지 어떻게 되어 왔는지, React 프레임웍과 Next.js 가 만들어진 이유가 무엇인지에 대해 알아보자.
짤막한 웹 렌더링의 역사
웹 기술은 앱의 요구사항을 만족하기 위해 끊임없이 발전해 왔다. 웹 사이트의 기본 빌딩 블록인 HTML, CSS , JavaScript역시 변경되는 요구사항과 브라우저의 발전된 기능을 사용하기 위해 지속적으로 발전해 왔다.
2000년대 초에는 서버에 의해 렌더링되는 HTML페이지들이 대부분이었다. 개발자들은 HTML페이지를 렌더링하기 위해 PHP나 ASP같은 서버사이드 스크립팅 언어를 사용했다. 페이지의 탐색은 온전히 페이지를 새로 불러오는 것에 의존했으며. JavaScript는 그 안에서 특정 요소의 노출 여부나 폼의 활성화 비활성화를 결정하는 정도로만 작게 사용했다.
2006년엔 Ajax로 SPA를 개발하는 것이 알려졌고 Gmail이 유명한 예시가 되었다. Ajax는 개발자들에게 페이지를 새로 불러오지 않고도 동적으로 요청으로 보낼 수 있게 해 주었다. 따라서 SPA는 데스크톱 앱과 유사하게 구현될 수 있었다. 곧 개발자들은 JavaScript를 사용하여 데이터를 받아오고 화면을 그렸다. JavaScript라이브러리와 프레임웍이 만들어져 MVC프레임웍 내의 뷰 레이어를 구현하는데 쓰였다. jQuery, Backbone.js, AngularJS와 같은 클라이언트 사이드 프레임웍들은 JavaScript 기반 코어 기능들을 쉽게 개발할 수 있게 해 주었다.
React는 2013년에 SPA와 모바일 웹 기반으로 유저 인터페이스와 UI컴포넌트들을 만들 수 있는 유연한 프레임웍으로 소개되었다. 2015년부터 2020년까지 데이터플로우 구조 라이브러리 (Redux), CSS프레임웍 (React-Bootstrap), 라우팅 라이브러리, 모바일 앱 프레임웍 (React Native)들을 포함해 React의 생태계가 발전했다. 그러나 순수 클라이언트 사이드 측 렌더링의 단점들이 부각되며 개발자들은 클라이언트나 서버 양쪽에서 최적의 성능을 내기 위해 다양한 시도를 하게 되었다.
렌더링 - 성능의 주요 지표
위에서 이야기한 단점들에 대해 이야기 하기 전에, 각 렌더링 메커니즘의 성능을 어떻게 측정하는지 이해해야 한다. 섹션에서 다룰 각 패턴들의 차이점 비교를 쉽게 하기 위해 먼저 아래 용어들을 알아보자.
약자 | 설명 |
---|---|
TTFB | Time to First Byte - 링크 클릭과 해당 사이트의 첫 바이트를 받기까지의 시간 |
FP | First Paint - 사용자가 어떤 컨텐츠를 보게 되거나 화면에 약간의 픽셀이 그려지기까지의 시간 |
FCP | First Contentful Paint - 페이지 컨텐츠의 일부가 화면에 렌더링될 때 까지의 시간 |
LCP | Largest Contentful Paint - 메인페이지의 컨텐츠가 보여지기까지의 시간. 뷰포트 내에 큰 이미지나 텍스트가 보여짐을 의미 |
TTI | Time to Interactive - 페이지에 인터렉션이 가능해지기까지의 시간. 예를 들면 이벤트 핸들러들이 모두 바인딩되는 등. |
TBT | Total Blocking Time - FCP부터 TTI까지의 시간 |
위 성능 측정을 위한 파라미터에 대해 중요한 사항들을 정리해 보면 아래와 같다
- JavaScript번들 사이즈가 크면 페이지를 로드할 때 FCP와 LCP 시간이 증가하므로, 사용자는 컨텐츠가 화면에 나타나기 전 까지 빈 화면에서 기다리는 시간이 늘어날 것이다.
- JavaScript번들 사이즈가 크면 TTI와 TBT에 영향을 주어. 사용자는 필수적인 JavaScript가 로드되어 이벤트 바인딩이 완료되기까지 아무 인터렉션을 하지 못할 것이다.
- TTFB는 사용자의 요청을 처리하는 서버의 처리 속도와 관련이 있다.
- preload, prefetch, script의 속성 처리 와 같은 테크닉들은 브라우저가 그들을 어떻게 해석하는가에 따라 위의 파라미터들을 다르게 변화시킨다. 이런 기능을 사용하기 전에 위의 속성들에 따라 브라우저가 로딩과 실행 우선순위를 어떻게 부여하는지에 대해 알면 많은 도움이 된다.
이제 이후에 소개할 각 패턴들이 어떻게 렌더링 요구사항을 만족시키는지에 대해서 파라미터를 기준으로 알아볼 수 있게 되었다.
글에서는 아래에 나열된 패턴에 대해서 자세히 알아볼 것이다. Next.js 를 사용하면 아래 패턴들을 모두 구현할 수 있으므로 패턴에 대해 이야기해보기 전에 먼저 React 기반 프레임웍인 Next.js에 대하여 이야기 해 보자.
- SSR
- Static SSR (experimental flag)
- SSR with Regeneration
- CSR with Prerendering (Automatic Static Optimization 라고도 알려져 있다)
- Full CSR
결론
우린 SSR의 본질적인 변형인 네 가지 패턴에 대해서 다루었다. 이 패턴들은 성능 파라미터를 감소시키기 위해 TTFB (Static and Incremental Static Generator), TTI (Progressive Hydration), FCP/FP (Streaming) 같은 기술들을 조합하여 사용한다. 이 패턴들은 React같은 클라이언트 사이드 프레임웍으로 구현할 수 있으며 또한 CSR의 성능 개선을 위해 rehydration 같은 것도 할 수 있다. 따라서 SSR과 CSR의 장점들을 조합하여 원하는 결과를 만들어 낼 수 있다.
요약
패턴들 중에 앱 또는 페이지의 종류에 따라 적합하거나 그렇지 않은 것도 있다. 다음 차트는 이전에 설명한 각 패턴의 주요 내용에 대해 요약 및 비교하며. 각 패턴의 사용 사례도 나타내고 있다.
참조
- Rendering on the web
- Rendering on the web - YouTube
- Next.js Docs
- Next.js fundamentals
- Code Splitting with Dynamic imports in Next.js
- Google Trends
- Client-side vs. server-side rendering
- React.js Docs
- Instant Loading Web Apps with an Application Shell Architecture
- Lazy Loading
- Isomorphic JavaScript
- How to Enable Server- Side Rendering for a React App
- Fetching and Hydrating a Next.js app
- FE jargon you should know
- The case of partial hydration
- Incremental Static Regeneration with Next.js
- What is React Concurrent mode
- Whats new in Server Side Rendering in React 16
- Streaming Server Side Rendering and Caching at Spectrum