React18부터 추가된 Streaming SSR을 찾아보면서 기존 렌더링 방식(CSR, SSR)도 복습할겸 공부한 내용을 정리해보고자 한다.
웹 성능 지표(Web Performance Metrics)
웹사이트나 웹 애플리케이션의 로딩 속도와 사용자 경험을 측정하는 데 사용되는 다양한 측정 기준입니다.
- TTFB(Time To First Byte) : 클라이언트(브라우저)가 서버에 요청을 보낸 후 첫 번째 바이트를 받기까지의 시간을 의미한다. 즉, 요청을 보낸후 다시 응답을 받기 시작한 시간이다.
- FCP(First Contentful Paint) : 페이지가 로드되기 시작한 시점부터 컨텐츠의 일부가 화면에 렌더링될 때까지의 시간이다. 요청에 대한 응답을 받기 전에는 유저가 빈 화면을 보고 있기 때문에 FCP는 중요한 지표로 여겨진다.
- LCP(Largest Contentful Paint) : 화면 내에서 가장 큰 컨텐츠 요소가 렌더링되는 시점이다. 일반적으로 점진적으로 데이터를 받아오는 순간마다 LCP가 변경되는데, 마지막으로 가장 큰 컨텐츠가 그려진 시간을 기준으로 한다.
- TTI(Time To Interactive) : 페이지가 완전히 상호작용 가능한 상태가 되는 시점이다. 화면을 그리기만 해서는 상호작용이 불가능하기 때문에 이 시간을 단축하는 것 역시 중요한 지표로 여겨진다.
클라이언트 사이드 렌더링(Client Side Rendering, CSR)
CSR은 클라이언트 사이드(브라우저)에서 렌더링을 수행하기 위한 방식으로 서버로부터 기본적인 HTML을 받아오고 프로젝트 전체에 사용하는 JS 번들파일을 받아와 이를 활용해 렌더링하는 방식이다.
CSR 방식은 최초에 전체에서 사용하는 JS 번들파일을 가지고 와서 사용하기 때문에 이후부터는 클라이언트 측에서 라우팅과 렌더링을 수행하여 화면전환이 매끄럽게 느껴진다. 하지만 모든 JS 번들파일을 가지고 오는 것은 파일의 크기가 크기 때문에 사용자의 네트워크 상황에 따라 컨텐츠를 보는데까지 시간이 오래 걸릴 수 있다.
그럼에도 불구하고 CSR은 클라이언트 사이드에서 JS를 통해 컨텐츠를 렌더링하기 때문에 그려진 이후에는 빠르게 상호작용이 가능하기 때문에 비교적 빠른 TTI(Time To Interactive)를 가진다.
하지만, 잘 알려진 단점으로 SEO 최적화 문제가 있다. 서버는 특정 경로에 대한 요청이 들어오면 해당 경로에 존재하는 정적 파일을 반환하려고 시도한다. 그러나 CSR 방식으로 구현된 프로젝트의 경우, 브라우저에서 JS를 통해 가상의 라우팅을 생성할 뿐, 실제 서버에는 해당 경로에 정적 파일이 존재하지 않는다.
이로 인해 서버는 경로에 맞는 파일을 찾지 못하고, 결국 Fallback 라우트를 통해 루트 경로(/)의 index.html 파일을 반환하게 된다. 따라서, 검색 엔진이 어떤 경로로 접근하더라도 동일한 HTML 파일이 반환되므로, 경로별로 적절한 메타태그를 제공할 수 없어 SEO 최적화가 어렵다.
동작 순서
- 클라이언트(브라우저)가 서버로 HTML 요청
- 서버가 기본 HTML(뼈대) + JS 번들 제공
- 브라우저가 HTML을 렌더링하고 JS 다운로드
- JS 실행 후 가상 DOM 생성 및 렌더링 시작
- 클라이언트가 필요한 데이터를 서버에 추가 요청 (API 호출)
- UI 업데이트 및 사용자 상호작용 처리
서버 사이드 렌더링(Server Side Rendering, SSR)
SSR은 서버 사이드에서 렌더링을 수행하기 위한 방식이다. 이 방식은 화면에 그려주는데에 필요한 서버 요청까지 모두 실행시켜 완성된 HTML을 만들고 이를 클라이언트에 넘겨주고 클라이언트에서는 DOM에 JS를 연결을 수행한다. 즉, 이벤트 핸들러와 상태를 주입해주는 동작만 담당하는 방식이다. 클라이언트 측에서 DOM과 JS를 연결하는 과정을 Hydration이라고 한다.
Hydration(수화)
서버 사이드 렌더링(SSR) 과정에서 클라이언트 측에서 DOM과 JS를 연결해 HTML을 인터랙티브하게 만드는 과정
(이벤트 핸들러 연결 및 상태 주입)
SSR은 JS 번들파일을 한번에 불러오지 않아서 초기 로딩 속도는 CSR보다 빠를 수 있다. 하지만 기본적으로 처음 화면을 보여주는 속도인 FCP는 CSR보다 느릴 수 있다. CSR은 껍데기 HTML만 반환하는 반면, SSR은 완성된 HTML을 만들어서 제공하기 때문에 FCP가 느릴 수밖에 없다. 다만 처음 화면이 보이자마자 가장 큰 콘텐츠도 함께 보이기 때문에 LCP는 FCP와 거의 동일선상에 있다.
SSR은 서버에서 API를 수행하고 완성된 HTML을 클라이언트에 제공한 후, 추가적으로 JS 파일을 받아 완성된 HTML에 이벤트 핸들러와 상태를 주입하는 Hydration 과정이 필요하다. 이 때문에 유저가 실제로 상호작용할 수 있는 시점은 Hydration이 완료된 이후가 되며, 이 과정이 길어질수록 TTI는 느려진다.
1. 느린 TTFB (Time To First Byte) : SSR은 API 서버에서 데이터를 받아오고 이를 바탕으로 HTML을 생성한 뒤, 브라우저로 전송하는 방식이다. 이 과정에서 클라이언트가 서버로 요청을 보낸 뒤 데이터를 받아오기까지 시간이 더 걸리므로, TTFB가 느릴 수밖에 없다.
2. 느린 TTI (Time To Interaction) : CSR과 달리 SSR은 서버에서 API 요청과 HTML 생성을 먼저 수행하고, 그 후 클라이언트는 내려받은 HTML에 이벤트 핸들러와 상태를 주입하는 Hydration 과정을 거친다. 이 때문에 실제 상호작용이 가능한 TTI 시점이 늦어지며, 그로 인해 사용자 경험이 저하될 수 있다.
스트리밍 서버 사이드 렌더링(Streaming SSR)
Streaming SSR은 기존 SSR 방식의 개선된 형태로, 클라이언트의 요청을 받은 후 서버가 API 서버에 요청을 보내고 데이터를 받아 HTML을 완성하는 과정에서, HTML을 모두 준비하기 전에 부분적으로 HTML을 클라이언트로 전달하는 방식이다. 서버는 HTML을 Stream 형태로 변환하여, 모든 데이터를 다 처리하기 전에 클라이언트에게 HTML 정보를 점진적으로 내려주기 시작한다.
Streaming SSR 방식은 TTFB(Time To First Byte)를 개선하여, 클라이언트가 서버로부터 더 빠르게 HTML을 전달받을 수 있다. 스트림을 통해 HTML을 부분적으로 전송하기 때문에, 먼저 의미 있는 내용이 화면에 빠르게 표시된다. 이로 인해 유저는 더 빠르게 화면을 볼 수 있게 된다.
하지만, 여전히 HTML 스트림을 모두 내려받은 후 Hydration 과정이 필요하기 때문에, TTI(Time To Interactive)는 개선되지 않았다. 이를 해결하기 위해서는 Progressive Hydration(점진적 수화) 방식이 필요하다. 점진적 수화는 Hydration을 더 빠르게 처리할 수 있도록 돕고, 사용자 상호작용을 더 빨리 가능하게 만든다.
Progressive Hydration(점진적 수화)
Progressive Hydration은 점진적으로 Hydration을 진행하는 방식이다. HTML을 Stream 형태로 내려주고, 의미 있는 HTML 단위가 완성될 때마다 해당 HTML을 사용하는 JS도 함께 클라이언트로 전송하여, 먼저 Hydration을 수행하도록 한다. 이렇게 하면 모든 HTML이 완성되기 전에 부분적으로 완성된 HTML에 대해 상호작용이 가능해져, 결과적으로 느린 TTI(Time to Interactive)를 개선할 수 있다.

Streaming SSR이 가능한 이유?
1. HTTP
HTTP/1.1에서는 청크 전송 인코딩(Transfer-Encoding: chunked) 방식이 존재한다. 이 방식은 Response Header에 Transfer-Encoding: chunked를 설정하면, 대용량 파일을 전송하거나 실시간 데이터 스트리밍에 활용할 수 있습니다. 이를 활용하면 별도의 소켓 방식 없이도 지속적으로 데이터를 스트림 형태로 받을 수 있게 됩니다.
하지만 HTTP에서 제공하는 기능만으로는 스트리밍 방식이 완전히 구현되기 어렵습니다. 이는 데이터를 스트림 형태로 전송하려면 서버 측에서도 이를 지원해야 하기 때문입니다.
2. Stream API(Node.js)
이를 가능하게 하기 위해 Node.js에서는 Stream API를 제공하고 있습니다. (자세한 내용: Stream API)
Stream API를 사용하면 데이터를 작은 청크 단위로 처리하여 ReadableStream 형태로 클라이언트에 전달할 수 있게 됩니다.
References
더 좋은 유저 경험을 위한 Streaming SSR
'프론트엔드' 카테고리의 다른 글
BEM 방법론과 Styled Components 활용법 (0) | 2025.03.10 |
---|---|
[CI/CD] CloudFront 연동하기 (0) | 2024.12.01 |
[CI/CD] Github Actions로 CI/CD 구축하기 (0) | 2024.11.29 |
[CI/CD] CI/CD 알아가기 (0) | 2024.11.28 |
[모던 자바스크립트 Deep Dive] 08장 - 제어문 (0) | 2024.11.22 |