renderToWebStream을 활용한 스트리밍 SSR
renderToWebStream()과 renderToString()의 차이, HTTP 스트리밍, TTFB 개선과 청크 플러시를 다룹니다.
renderToWebStream을 활용한 스트리밍 SSR은(는) CoddyKit의 무료 Vue Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Vue Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Vue Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
renderToString에서 스트리밍으로
기존 SSR은 renderToString을 사용하여 HTML 전체를 메모리에 만든 뒤 단일 바이트도 전송합니다. 브라우저는 전체 페이지가 준비될 때까지 기다립니다.
스트리밍 SSR은 앱이 렌더링되는 대로 HTML을 청크 단위로 전송하므로 브라우저가 더 일찍 파싱을 시작합니다.
renderToWebStream 기초
Vue의 서버 렌더러는 스트리밍 함수를 제공합니다. renderToWebStream은 ReadableStream(Web Streams API)을 반환하고, renderToNodeStream/pipeToNodeWritable은 Node의 stream 인터페이스를 대상으로 합니다.
컴포넌트 트리가 해결될 때마다 각 청크가 플러시됩니다.
import { renderToWebStream } from 'vue/server-renderer'
import { createSSRApp } from 'vue'
import App from './App.vue'
const app = createSSRApp(App)
const stream = renderToWebStream(app)HTTP 응답으로 스트림 연결하기
Node 서버에서는 stream을 응답으로 연결합니다. pipeToNodeWritable을 사용하면 쓰기 가능한 res에 직접 작성하여 생성되는 대로 청크를 플러시할 수 있습니다.
import { pipeToNodeWritable } from 'vue/server-renderer'
server.get('*', (req, res) => {
const app = createSSRApp(App)
res.write('<!DOCTYPE html><html><body><div id="app">')
pipeToNodeWritable(app, {}, res)
})웹 및 엣지 런타임에서 스트리밍하기
엣지 런타임(Workers, Deno, Bun)에서는 웹 stream으로 만든 Response를 직접 반환합니다. Node stream은 사용하지 않습니다.
import { renderToWebStream } from 'vue/server-renderer'
export default async function handler(request) {
const app = createSSRApp(App)
const stream = renderToWebStream(app)
return new Response(stream, {
headers: { 'Content-Type': 'text/html' }
})
}청크가 브라우저에 도달하는 과정
Vue가 노드를 렌더링하면 직렬화된 HTML 문자열을 stream으로 보냅니다. HTTP 계층은 해당 바이트를 즉시 플러시합니다. 브라우저의 점진적 HTML 파서는 응답이 끝나기 전에 보이는 콘텐츠를 렌더링합니다.
그래서 페이지 본문이 아직 스트리밍되는 동안에도 사용자는 헤더와 레이아웃을 볼 수 있습니다.
TTFB: 핵심 지표
TTFB(첫 바이트까지 걸리는 시간)는 첫 번째 응답 바이트가 도착할 때까지 걸리는 시간을 측정합니다.
renderToString을 사용하면 TTFB에 전체 렌더링 시간이 포함됩니다. 스트리밍에서는 첫 청크가 거의 즉시 서버를 떠나므로 TTFB가 크게 낮아지고 사용자가 느끼는 속도가 향상됩니다.
// renderToString: client waits for full HTML
const html = await renderToString(app) // blocks
res.end(fullPage(html))
// streaming: first bytes flush right away
pipeToNodeWritable(app, {}, res) // non-blocking chunks레이아웃 HTML로 stream 감싸기
일반적으로 앱 stream 전에 문서 골격(<head> 및 여는 태그)을 보내고, 뒤에 닫는 태그를 보내야 합니다. 접두부를 작성하고 app stream을 연결한 다음, stream이 끝나면 접미부를 작성합니다.
res.write('<!DOCTYPE html><head><title>App</title></head>')
res.write('<body><div id="app">')
pipeToNodeWritable(app, ctx, {
write: (chunk) => res.write(chunk),
end: () => res.end('</div></body></html>')
})stream 중간의 오류 처리
바이트가 플러시된 후에는 HTTP 상태 코드를 변경할 수 없습니다. onError 옵션 또는 쓰기 가능한 stream의 오류 경로를 통해 렌더링 오류를 처리하세요. 초기에 감지된 치명적인 오류라면 여전히 500을 보낼 수 있도록 스트리밍하지 않는 렌더링을 사용하는 편이 좋습니다.
pipeToNodeWritable(app, ctx, {
write: (c) => res.write(c),
end: () => res.end(suffix),
destroy: (err) => {
console.error('Stream failed:', err)
res.end() // status already sent
}
})스트리밍 후 하이드레이션
스트리밍된 HTML도 클라이언트에서 createSSRApp().mount()를 사용하여 하이드레이션됩니다. Vue는 기존 DOM을 다시 만들지 않고 이벤트 리스너를 연결합니다. 스트리밍은 마크업이 전달되는 방식에만 영향을 주며 하이드레이션 방식은 바꾸지 않습니다.
// client entry
import { createSSRApp } from 'vue'
import App from './App.vue'
createSSRApp(App).mount('#app') // hydrates streamed markup스트리밍과 비동기 데이터
데이터에 의존하는 컴포넌트에서는 스트리밍 전이나 스트리밍 중에 중요한 데이터를 해결하세요. 트리의 일부가 서로 독립적일 때 스트리밍의 장점이 잘 드러납니다. 문서 골격은 즉시 stream으로 전송되고, 더 느린 영역은 데이터 처리가 완료되는 대로 렌더링됩니다.
// fetch critical data, then stream the rest
const app = createSSRApp(App, { initialData })
const stream = renderToWebStream(app)
return new Response(stream, {
headers: { 'Content-Type': 'text/html' }
})스트리밍을 선택할 때
콘텐츠가 많고 TTFB와 사용자가 느끼는 속도가 중요하며 안정적인 문서 골격을 먼저 보낼 수 있는 페이지에는 스트리밍을 사용하세요. 작은 페이지나 최종 상태 코드와 헤더를 완전히 제어해야 하는 경우에는 renderToString을 사용하세요.
빠른 확인
스트리밍 SSR에 대한 이해도를 확인해 보세요.
복습
스트리밍 SSR을 배웠습니다.
renderToWebStream은 ReadableStream을 반환하고, Node에서는pipeToNodeWritable을 사용합니다.- 트리가 렌더링되는 대로 청크가 플러시되므로 브라우저가 점진적으로 파싱합니다.
- 차단 방식의
renderToString보다 TTFB가 낮아집니다. - stream 주위에 문서 골격을 작성하세요. 첫 플러시 이후의 오류는 상태 코드를 변경할 수 없습니다.
createSSRApp().mount()를 사용한 하이드레이션 방식은 바뀌지 않습니다.
자주 묻는 질문
“renderToWebStream을 활용한 스트리밍 SSR” 강의는 무료인가요?
네 — “renderToWebStream을 활용한 스트리밍 SSR” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Vue Academy 강의 전체를 잠금 해제할 수 있습니다. Vue Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“renderToWebStream을 활용한 스트리밍 SSR”에서 뭘 배우나요?
renderToWebStream()과 renderToString()의 차이, HTTP 스트리밍, TTFB 개선과 청크 플러시를 다룹니다. 브라우저에서 직접 실행하는 실습 코드로 Vue Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Vue Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Vue Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“renderToWebStream을 활용한 스트리밍 SSR” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Vue Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Vue Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- Vue Suspense 컴포넌트
- defineAsyncComponent를 활용한 비동기 컴포넌트
- renderToWebStream을 활용한 스트리밍 SSR
- 지연된 Hydration 전략