--- title: renderToReadableStream --- `renderToReadableStream`는 [Readable Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)을 이용해 React 트리를 그립니다. ```js const stream = await renderToReadableStream(reactNode, options?) ``` 이 API는 [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API)에 의존합니다. Node.js의 경우, [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream)을 대신 사용하세요. --- ## 레퍼런스 {/*reference*/} ### `renderToReadableStream(reactNode, options?)` {/*rendertoreadablestream*/} `renderToReadableStream`을 호출하여 React 트리를 HTML로 [Readable Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)에 렌더링합니다. ```js import { renderToReadableStream } from 'react-dom/server'; async function handler(request) { const stream = await renderToReadableStream(, { bootstrapScripts: ['/main.js'] }); return new Response(stream, { headers: { 'content-type': 'text/html' }, }); } ``` 클라이언트에서, [`hydrateRoot`](/reference/react-dom/client/hydrateRoot)를 호출해 서버에서 생성된 HTML을 상호작용 가능하도록 만듭니다. [아래에서 더 많은 예시를 확인하세요.](#usage) #### 매개변수 {/*parameters*/} * `reactNode`: 사용자가 HTML로 렌더링하고 하고자하는 React node입니다. ``같은 JSX 요소가 그 예시입니다. reactNode 인자는 문서 전체를 표현할 수 있는 것이어야하며, 따라서 `App` 컴포넌트는 ``에 렌더링됩니다. * **optional** `options`: 스트리밍 옵션을 지정할 수 있는 객체입니다. * **optional** `bootstrapScriptContent`: 지정될 경우, 해당 문자열은 ` ``` 클라이언트에선, 추가된 bootstrap 스크립트는 [`hydrateRoot`를 호출해 `document` 전체를 hydrate 해야합니다](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document). ```js [[1, 4, ""]] import { hydrateRoot } from 'react-dom/client'; import App from './App.js'; hydrateRoot(document, ); ``` 이 과정은 서버에서 렌더링된 HTML에 이벤트 리스너들을 붙이고, HTML을 상호작용 가능하게 만듭니다. #### 빌드 결과물에서 CSS와 JS의 경로 읽어오기 {/*reading-css-and-js-asset-paths-from-the-build-output*/} JS와 CSS같은 최종 에셋들에 대한 URL들은 종종 빌드 후에 해시됩니다. 예를 들어, `styles.css` 대신 `styles.123456.css`와 같은 형태로 끝날 수 있습니다. 에셋들의 파일명을 해시하는 것은 모든 빌드의 결과물이 각각 다른 파일명을 가지도록 보장합니다. 이는 정적 에셋들에 대한 장기 캐싱을 안전하게 활성화할 수 있도록 해줍니다. 즉, 특정 이름의 파일 내용은 절대 바뀌지 않는 다는 것을 보장합니다. 하지만, 빌드 후에 에셋들의 URL을 알 수 없다면, 소스 코드에 URL을 넣을 수 없습니다. 예를 들어, JSX에 `"/styles.css"`를 하드코딩하는 것은 작동하지 않습니다. 소스 코드에 URL을 넣지 않으려면, 루트 컴포넌트는 props로 전달된 맵에서 실제 파일명을 읽어야합니다. ```js {1,6} export default function App({ assetMap }) { return ( My app ... ); } ``` 서버에선 ``을 렌더링하고, 에셋 URL들과 함께 `assetMap`을 전달합니다. ```js {1-5,8,9} // 빌드 도구로부터 이 JSON을 얻어야합니다. 예를 들어, 빌드 결과물에서 읽어올 수 있습니다. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' }; async function handler(request) { const stream = await renderToReadableStream(, { bootstrapScripts: [assetMap['/main.js']] }); return new Response(stream, { headers: { 'content-type': 'text/html' }, }); } ``` 서버가 ``를 렌더링한 이후엔, 클라이언트에서도 hydration 오류를 피하기 위해 `assetMap`과 함께 렌더링해야합니다. `assetMap`을 직렬화하고 클라이언트에 전달하기 위해 다음과 같이 할 수 있습니다. ```js {9-10} // 빌드 도구로부터 이 JSON을 얻어야합니다. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' }; async function handler(request) { const stream = await renderToReadableStream(, { // 주의: 이 데이터는 사용자가 생성하지 않았기 때문에 stringify()를 사용해도 안전합니다. bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`, bootstrapScripts: [assetMap['/main.js']], }); return new Response(stream, { headers: { 'content-type': 'text/html' }, }); } ``` 위의 예시에서, `bootstrapScriptContent` 옵션은 클라이언트에서 `window.assetMap` 전역 변수를 설정하는 인라인 `