Streaming SRR로 전환: renderToString에서 renderToPipeableStream으로

심지은·2025년 8월 12일

Streaming SSR 이란?

서버 사이드 렌더링(SSR)은 서버에서 HTML을 생성하여 브라우저로 전송하는 방식이다.
기존 SSR은 서버에서 모든 데이터와 UI가 준비된 HTML을 한번에 전송한다. 이 방식은 단순하지만, 데이터 양이 많아질수록 TTFB(Time to First Byte)가 길어지고, 사용자가 첫 화면을 보기까지 시간이 오래 걸린다.
Streaming SSR은 다르다.
서버에서 준비된 HTML 조각부터 바로 전송하고, 나머지는 준비되는 대로 순차적으로 스트리밍을 한다.
즉, 사용자는 페이지의 핵심 콘텐츠를 빨리 볼 수 있고, 데이터가 많은 페이지에서도 초기 로딩 속도가 향상된다.
React 18부터는 renderToPipeableStream API를 통해 이 방식을 쉽게 구현할 수 있다.

renderToPipableStream 소개

React 18의 renderToPipeableStream 은 서버에서 HTML을 스트리밍 방식으로 전송할 수 있는 API이다.
Node.js 환경에서 HTTP 응답 스크림(res)에 바로 HTML을 흘려보낼 수 있다.

import { renderToPipeableStream } from 'react-dom/server';
import App from './App';

app.get('/', (req, res) => {
  const stream = renderToPipeableStream(<App />, {
    onShellReady() {
      res.setHeader('Content-Type', 'text/html');
      stream.pipe(res); // 준비된 HTML부터 바로 전송
    },
    onError(err) {
      console.error(err);
    }
  });
});
  • onShellReady : 첫 HTML이 준비되면 즉시 전송 시작
  • onAllReady : 모든 콘텐츠가 준비된 시점
  • onError : 렌더링 중 발생한 에러 처리

renderToString -> renderToPipeableStream 전환 이점

(1) 초기 콘텐트 노출 시간 단축

  • 기존 renderToString은 서버에서 HTML전체를 완성한 후에야 전송 가능
  • renderToPipeableStream은 완전한 페이지가 아니더라도 우선 핵심 UI부터 브라우저에 전달
  • 사용자가 "로딩중" 화면이 아닌 실제 콘텐츠를 더 빨리 보게 됨

(2) 서버처리 부하 분산

  • 대규모 컴포넌트 트리를 한번에 렌더링하는 대신 스트림 단위로 처리
  • 서버 메모리 사용량과 CPU 부하가 완화될 수 있음

(3) React Suspense와 궁함

  • 비동기 데이터가 늦게 도착해도, 나머지 준비된 부분은 즉시 전송 가능
  • 중요하지 않은 콘텐츠는 뒤늦게 로드하며 UI를 점진적으로 완성

Hydration 문제와 해결 방법

(1) Hydration 이란?
Hydration은 서버에서 전송된 HTML에 React의 상태와 이벤트 핸들러를 결합해 완전한 인터랙티브 페이지로 만드는 과정이다.
Streaming SSR에서는 HTML이 조각 단위로 도착하기 때문에, 클라이언트와 서버의 UI 상태가 어긋나는 경우가 발생할 수 있다.

(2) 발생 가능한 문제

  • HTML과 React상태 불일치 : 서버에서 렌더링된 HTML과 클라이언트에서 계산한 결과가 다르면 React가 경고를 띄우거나 UI가 깜빡임
  • Suspense 비동기 데이터 지연 : 일부 데이터가 늦게 로드되면, 초기 렌더링 시점과 Hydration 시점의 UI가 달라질 가능성
  • 사용자 입력 손실 : Hydration 전에 사용자가 입력한 값이 React로 연결되지 전에 초기 상태로 덮어써지는 문제

(3) 해결 방법

  • 동일한 데이터 소스 유지 : 서버와 클라이언트 모두 동일한 데이터를 기반으로 렌더링하도록 API 응답을 공유한다. TanStack Query 같은 라이브러리로 서버상태 동기화
  • Suspense Fallback 최적화 : 핵심 콘텐츠에 대한 fallback UI를 최소화하고, 데이터 준비 전 UI 흔들림을 줄임
  • 서버-클라이언트 상태 동기화 전략 : 중요한 UI 상태(예: 로그인 여부 등등)는 SSR 시점에 함께 직렬화하여

마무리

renderToPipeableStream은 단순한 API변경이 아니라, SSR경험을 한 단계 끌어올리는 전략이라고 생각한다.
대규모 페이지나 초기 로딩 시간이 중요한 서비스에서 특히 효과를 발휘한다.
다만, Hydration 불일치 문제를 예방하고 데이터 동기화 전략을 잘 세운다면 성능과 사용자 경험을 모두 잡을 수 있을것이다.

profile
기억보단 기록을 좋아하는 프론트엔드개발자, 조이입니다 :)

0개의 댓글