Streaming SSR
Streaming SSR은 서버에서 렌더링된 HTML을 한 번에 완성해서 보내는 방식이 아니라, 준비된 부분부터 점진적으로 스트리밍해서 클라이언트에 전달하는 기술이다. 이를 통해 사용자는 페이지의 중요한 콘텐츠를 더 빠르게 확인할 수 있는 것이다.
기존 SSR은 서버에서 모든 데이터를 처리한 뒤 완전한 HTML을 전송하는 방식인 반면, Streaming SSR은 서버가 데이터를 준비하는 즉시 HTML 조각을 스트림 형태로 보내고, 클라이언트는 이를 실시간으로 렌더링하는 방식이다.
React 18에서는 renderToPipeableStream API를 통해 구현할 수 있으며, 이 API는 서버에서 HTML을 조각 단위로 스트리밍할 수 있도록 지원하는 것이다.
예를 들어, onShellReady 옵션을 사용해 스트림을 응답으로 바로 전송할 수 있는 것이다.
renderToPipeableStream(<App />, {
onShellReady() {
res.setHeader('Content-Type', 'text/html');
stream.pipe(res);
},
});
이 방식의 가장 큰 장점은 초기 로딩 시간을 단축할 수 있다는 점이다. HTML의 일부라도 준비되는 즉시 클라이언트가 렌더링을 시작하므로 TTFB(Time to First Byte)가 개선되는 것이다. 특히 데이터가 많거나 복잡한 대규모 애플리케이션에서 효과적이며, 사용자가 중요한 콘텐츠를 먼저 확인할 수 있어 전반적인 사용자 경험도 향상되는 것이다.
다만, 클라이언트에서 부분적으로 전송된 HTML을 제대로 Hydration할 수 있도록 설계가 필요하고, SEO나 캐싱 정책과의 호환성도 신중히 고려되어야 하는 것이다. 이러한 특징과 장점을 통해 Streaming SSR은 기존 SSR의 한계를 극복하며, 더욱 빠르고 효율적인 웹 페이지 렌더링을 가능하게 만드는 것이다.
스트리밍된 데이터와 리액트의 Hydration 과정에서 발생할 수 있는 문제
스트리밍된 데이터와 리액트의 Hydration 과정에서 발생할 수 있는 주요 문제는 렌더링되는 HTML과 리액트의 상태 불일치이다. HTML과 리액트 상태의 불일치 문제는 스트리밍된 HTML이 서버에서 먼저 클라이언트로 전송되고, 리액트가 실행되기 전까지는 단순히 정적인 상태로 보여지는 데서 시작되는 것이다.
이후 Hydration 과정에서 이 HTML에 리액트의 상태와 이벤트 핸들러가 결합되는 과정에서, 서버와 클라이언트 사이 데이터가 맞지 않으면 문제가 발생하는 것이다. 예를 들어, 서버에서 렌더링된 데이터가 클라이언트의 Hydration 시점에 변경되어 있다면 리액트가 경고를 띄우거나 예상치 못한 UI 동작이 나타나는 것이다. 또한 비동기 데이터 처리를 Suspense로 처리하고 있다면, 데이터가 늦게 로드되어 UI가 달라지는 가능성이 존재하는 것이다.
이러한 불일치 문제를 해결하기 위해서는 서버와 클라이언트에서 동일한 데이터 소스를 사용해야 하는 것이다.
예를 들어, Tanstack Query와 같은 라이브러리를 활용하면 데이터를 동기화하기가 훨씬 수월해지는 것이다.
또한 Suspense와 fallback을 효과적으로 활용하면, 데이터가 아직 준비되지 않았을 때도 안정적인 화면을 보여줄 수 있는 것이다.
이와 같이 하면 사용자 입장에서 데이터가 로드되기 전에 UI가 흔들리는 문제를 줄일 수 있는 것이다.