배경
처음 SSR을 접한 건 Next.js의 getServerSideProps였습니다. 서버에서 HTML을 만들어 보내는데, 그동안 사용자는 빈 화면만 보고 있었습니다.
다른 서비스들의 기술 블로그와 발표를 보며 Streaming SSR을 알게 됐습니다. 사내 프로젝트에 적용해 보고 싶었지만 Next.js 14 Pages Router라 방법이 없었고, 그래서 프레임워크 없이 직접 만들어 보기로 했습니다. mugglim/build-your-own-react-streaming-ssr이 출발점이 됐습니다.
다루는 내용
| 제목 | 내용 | |
|---|---|---|
| 1 | renderToString의 한계 | 왜 기존 SSR API로는 스트리밍을 할 수 없는가 |
| 2 | 스트리밍으로 바꾸기 | renderToPipeableStream과 onShellReady |
| 3 | HTML 껍데기 붙이기 | 문자열로 조립할 것인가, React 트리 안에 넣을 것인가 |
| 4 | 스트리밍이 죽어도 티가 나지 않는다 | 출력도 TTFB도 정상인데 화면만 다른 경우 |
| 5 | onShellReady와 onAllReady | 사람을 위한 응답과 기계를 위한 응답 |
| 6 | boundary가 여러 개일 때 | 자리는 선언 순으로, 내용은 완료 순으로 |
| 7 | 정리 | 직접 만들며 알게 된 것들 |
모든 수치는 직접 실행해서 얻은 값입니다.
스트리밍이 죽어도 티가 나지 않는다
4장은 한 줄만 바꾸면 확인할 수 있습니다. 서버가 보내는 조각을 도착하는 대로 흘려보내지 않고 배열에 모았다가 한 번에 보내면, 응답 내용도 TTFB도 그대로인데 화면만 달라집니다. 스트리밍이 사라졌는데 숫자로는 드러나지 않는 것입니다.
그래서 스트리밍이 살아 있는지는 curl의 숫자보다 브라우저 화면으로 확인하는 편이 정확하다고 정리했습니다.
사람을 위한 응답, 기계를 위한 응답
5장은 onShellReady를 onAllReady로 한 줄 바꾸는 실험입니다. 그러면 응답에서 fallback과 교체 스크립트가 사라집니다. 껍데기부터 먼저 보여 주는 쪽은 기다리는 사람을 위한 응답이고, 모든 내용이 준비된 뒤 완성된 HTML을 한 번에 보내는 쪽은 기계를 위한 응답입니다.
따라 해 보기
저장소 코드는 6장 시점 상태입니다. 실행하면 세 영역이 0.5초, 1.5초, 3초에 각각 채워지는데, 가운데가 먼저, 아래가 다음, 맨 위가 마지막입니다. 선언한 순서와 채워지는 순서가 다르다는 6장의 내용이 그대로 보입니다.
curl -N -w '\nTTFB: %{time_starttransfer}s / Total: %{time_total}s\n' localhost:3001
-N(--no-buffer)을 주면 curl이 받은 내용을 도착하는 대로 출력해, 응답이 조각나서 오는 모습을 볼 수 있습니다.
사용한 것
- 실험용 서버: Hono, React 19, tsx
- 설명 페이지: Astro, React islands, MDX