웹에서의 렌더링
용어
렌더링 방법
- 서버 측 렌더링(SSR): 클라이언트 측 또는 유니버설 앱을 서버에서 HTML로 렌더링합니다.
- 클라이언트 측 렌더링(CSR): JavaScript를 통해 브라우저에서 앱을 렌더링하여 DOM을 수정합니다.
- Rehydration: 서버에서 렌더링된 HTML의 DOM 트리와 데이터를 재사용할 수 있도록 클라이언트에서 JavaScript 뷰를 booting up 합니다.
- Prerendering: 빌드 시점에 클라이언트 측 애플리케이션을 실행하여 초기 상태를 정적 HTML로 캡처합니다.
성능지표
- 첫 데이터 도달 시간(Time to first byte | TTFB): 링크를 클릭한 후 콘텐츠의 첫 데이터가 들어올 때까지의 시간으로 볼 수 있습니다.
- 첫 번째 콘텐츠 표시 시간(First contentful paint | FCP): 요청된 콘텐츠(글 본문 등)가 표시되는 시간입니다.
- 다음 페인트로의 상호작용(Interaction to Next Paint | INP): 페이지가 사용자 입력에 일관되게 빠르게 반응하는지 평가하는 대표적인 지표로 볼 수 있습니다.
- 총 차단 시간(Total Blocking Time | TBT): 페이지 로드 중 메인 스레드가 차단된 시간을 계산하는 INP의 프록시 지표입니다.
Server-side rendering
서버 측 렌더링은 탐색에 대한 응답으로 서버에서 페이지의 전체 HTML을 생성합니다. 이렇게 하면 브라우저에서 응답을 받기 전에 처리되므로 클라이언트에서 데이터 가져오기 및 서식 지정에 대한 추가 작업을 피할 수 있습니다.
서버 측 렌더링은 일반적으로 빠르게 콘텐츠를 표시합니다. 페이지 로직과 렌더링을 서버에서 실행하면 클라이언트에 많은 자바스크립트를 전송하지 않아도 됩니다. 이는 페이지 로드 중에 메인 스레드가 자주 차단되지 않기 때문에 페이지의 총 차단 시간(Total Blocking Time | TBT)을 줄이는 데 도움이 되며, 이는 또한 낮은 다음 페인트로의 상호작용(Interaction to Next Paint | INP)으로 이어질 수 있습니다. 메인 스레드가 덜 자주 차단되면 사용자 상호 작용이 더 빨리 실행될 수 있습니다.

서버 측 렌더링을 사용하면 사용자가 사이트를 사용하기 전에 CPU에 바인딩된 자바스크립트가 실행될 때까지 기다릴 가능성이 줄어듭니다. third-party JS를 피할 수 없는 경우에도 서버 측 렌더링을 사용하여 자체 퍼스트파티 JavaScript 비용을 줄이면 나머지 부분에 더 많은 예산을 확보할 수 있습니다. 그러나 서버에서 페이지를 생성하는 데 시간이 걸리므로 첫 데이터 도달 시간(Time to first byte | TTFB)이 높아질 수 있다는 점입니다.
Static rendering
Static rendering은 빌드 시점에 발생합니다. Static rendering은 각 URL에 대해 별도의 HTML 파일을 미리 생성하는 것을 의미하고 빠른 첫 번째 콘텐츠 표시 시간(First contentful paint | FCP)과 낮은 총 차단 시간(Total Blocking Time | TBT) 및 다음 페인트로의 상호작용(Interaction to Next Paint | INP)을 제공합니다.

- 참고: Next.js 또는 Nuxt와 같이 널리 사용되는 프레임워크의 추상화 버전은 정적 렌더링과 서버 측 렌더링을 모두 제공합니다. 이를 통해 개발자는 페이지에 대해 정적 렌더링을 선택하거나 요청에 따라 동적으로 생성해야 하는 페이지에 서버 측 렌더링을 사용할 수 있습니다.
정적 렌더링의 단점 중 하나는 가능한 모든 URL에 대해 개별 HTML 파일을 생성해야 한다는 점입니다. 이는 URL이 어떻게 될지 미리 예측할 수 없거나 고유 페이지 수가 많은 사이트의 경우 어렵거나 실행 불가능할 수 있습니다.
Server-side rendering VS static rendering
서버 측 렌더링은 동적 특성으로 인해 상당한 컴퓨팅 오버헤드 비용이 발생할 수 있습니다. 많은 서버 측 렌더링 솔루션은 조기에 Flush되지 않거나 첫 데이터 도달 시간(Time to first byte | TTFB)을 지연시키거나 전송되는 데이터를 두 배로 늘릴 수 있습니다(예: 클라이언트에서 JavaScript가 사용하는 인라인 상태).
서버 측 렌더링을 수행하려면 컴포넌트 캐싱, 메모리 소비 관리, 메모화 기법 적용 및 기타 문제를 해결하기 위한 솔루션을 찾거나 구축해야 할 수 있습니다. 일반적으로 동일한 애플리케이션을 클라이언트에서 한 번, 서버에서 한 번 등 여러 번 처리/다시 빌드하게 됩니다. 서버 측 렌더링을 통해 무언가를 더 빨리 표시할 수 있다고 해서 갑자기 해야 할 일이 줄어드는 것은 아닙니다. 서버에서 생성된 HTML 응답이 클라이언트에 도착한 후 클라이언트에서 해야 할 일이 많으면 여전히 웹사이트의 총 차단 시간(Total Blocking Time | TBT) 및 다음 페인트로의 상호작용(Interaction to Next Paint | INP)이 높아질 수 있습니다.
서버 측 렌더링은 각 URL에 대해 주문형 HTML을 생성하지만 정적으로 렌더링된 콘텐츠를 제공하는 것보다 속도가 느릴 수 있습니다. 추가 작업을 수행할 수 있다면 서버 측 렌더링과 HTML 캐싱을 함께 사용하면 서버 렌더링 시간을 크게 줄일 수 있습니다. 서버 측 렌더링의 장점은 정적 렌더링보다 더 많은 개인화 데이터를 가져올 수 있다는 점입니다.
Client-side rendering
클라이언트 측 렌더링은 브라우저에서 자바스크립트로 페이지를 직접 렌더링하는 것을 의미합니다. 모든 로직, 데이터 가져오기, 템플릿 지정 및 라우팅은 서버가 아닌 클라이언트에서 처리됩니다. 결과적으로 더 많은 데이터가 서버에서 사용자의 디바이스로 전달되지만, 여기에는 몇 가지 단점이 있습니다.
클라이언트 측 렌더링은 모바일 디바이스에서 빠른 속도를 유지하기가 어려울 수 있습니다. 최소한의 작업만 수행하면 클라이언트 측 렌더링은 순수 서버 측 렌더링의 성능에 근접하여 자바스크립트 자원을 유지하면서 가능한 한 적은 왕복 횟수로 화면을 보여줄 수 있습니다. 중요한 스크립트와 데이터는 더 빨리 작동하도록 하는 를 사용하여 더 빨리 전달할 수 있습니다.

클라이언트 측 렌더링의 주요 단점은 애플리케이션이 성장함에 따라 필요한 JavaScript의 양이 증가하는 경향이 있으며, 이는 페이지의 다음 페인트로의 상호작용(Interaction to Next Paint | INP)에 부정적인 영향을 미칠 수 있다는 점입니다. 특히 처리 능력을 놓고 경쟁하며 페이지의 콘텐츠를 렌더링하기 전에 처리해야 하는 새로운 JavaScript Library, Polyfill 및 다른 코드들이 추가되면 문제가 더욱 심각해집니다.
대용량 JavaScript 번들에 의존하는 클라이언트 측 렌더링을 사용하는 경험은 페이지 로드 중 총 차단 시간(Total Blocking Time | TBT) 및 다음 페인트로의 상호작용(Interaction to Next Paint | INP)을 낮추기 위해 적극적인 코드 분할을 고려해야 하며, "필요할 때 필요한 것만 제공하는" Lazy Load를 반드시 사용해야 합니다.
rehydration를 통한 Server-side rendering과 Client-side rendering의 결합
이 접근 방식은 클라이언트 측 렌더링과 서버 측 렌더링을 모두 수행함으로써 클라이언트 측 렌더링과 서버 측 렌더링 간의 절충점을 해결하려고 도입 되었습니다. full page loads 또는 reloads와 같은 탐색 요청은 애플리케이션을 HTML로 렌더링하는 서버에서 처리한 다음 렌더링에 사용된 JavaScript와 데이터를 결과 문서에 embedded 합니다. 페이지 로드시 서버 측 렌더링과 마찬가지로 빠른 첫 번째 콘텐츠 표시 시간(First contentful paint | FCP)을 달성한 다음 rehydration라는 기술을 사용하여 클라이언트에서 다시 렌더링하여 "pick up"합니다. 이는 효과적인 솔루션이지만 성능에 상당한 단점이 있을 수 있습니다.
rehydration 기능을 사용한 서버 측 렌더링의 가장 큰 단점은 첫 번째 콘텐츠 표시 시간(First contentful paint | FCP)을 개선하더라도 총 차단 시간(Total Blocking Time | TBT) 및 다음 페인트로의 상호작용(Interaction to Next Paint | INP)에 상당한 부정적인 영향을 미칠 수 있다는 것입니다. 서버 측 렌더링 페이지는 로드되고 대화형인 것처럼 보이지만 실제로는 컴포넌트에 대한 클라이언트 측 스크립트가 실행되고 이벤트 핸들러가 첨부될 때까지 입력에 응답할 수 없습니다. 모바일에서는 몇 초 또는 몇 분이 걸릴 수 있습니다.
rehydration
서버가 HTML 렌더링에 사용한 모든 데이터를 다시 요청할 필요 없이 클라이언트 측 JavaScript가 서버가 중단한 부분을 정확하게 "pick up"할 수 있도록 현재 서버 측 렌더링 솔루션은 일반적으로 UI의 데이터 종속성으로부터의 응답을 스크립트 태그로 문서에 직렬화합니다. 결과 HTML 문서에는 높은 수준의 중복이 포함됩니다:

보시다시피 서버는 탐색 요청에 대한 응답으로 애플리케이션의 UI에 대한 설명을 반환하지만, 해당 UI를 구성하는 데 사용된 소스 데이터와 클라이언트에서 부팅되는 UI 구현의 전체 사본도 반환합니다. 가 로드 및 실행을 완료한 후에야 이 UI는 대화형 UI가 됩니다.bundle.js

마무리
server-client spectrum:
- 첫 데이터 도달 시간(Time to first byte | TTFB)
- 첫 번째 콘텐츠 표시 시간(First contentful paint | FCP)
- 다음 페인트로의 상호작용(Interaction to Next Paint | INP)
- 총 차단 시간(Total Blocking Time | TBT)
