--- title: prerenderToNodeStream --- `prerenderToNodeStream`은 [Node.js Stream](https://nodejs.org/api/stream.html)을 사용하여 React 트리를 정적 HTML 문자열로 렌더링합니다. ```js const {prelude} = await prerenderToNodeStream(reactNode, options?) ``` 이 API는 Node.js 전용입니다. Deno나 최신 엣지 런타임처럼 [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API)를 지원하는 환경에서는 [`prerender`](/reference/react-dom/static/prerender)를 사용해야 합니다. --- ## 레퍼런스 {/*reference*/} ### `prerenderToNodeStream(reactNode, options?)` {/*prerender*/} `prerenderToNodeStream`을 호출해 앱을 정적 HTML로 렌더링합니다. ```js import { prerenderToNodeStream } from 'react-dom/static'; // 라우트 핸들러 문법은 사용하는 백엔드 프레임워크에 따라 다릅니다. app.use('/', async (request, response) => { const { prelude } = await prerenderToNodeStream(, { bootstrapScripts: ['/main.js'], }); response.setHeader('Content-Type', 'text/plain'); prelude.pipe(response); }); ``` 클라이언트에서 [`hydrateRoot`](/reference/react-dom/client/hydrateRoot)를 호출해 서버에서 생성된 HTML을 인터랙티브하게 만듭니다. [아래에서 더 많은 예시를 확인하세요.](#usage) #### 매개변수 {/*parameters*/} * `reactNode`: HTML로 렌더링할 React 노드입니다. 예를 들어 ``과 같은 JSX 노드가 해당됩니다. 전체 문서를 나타내야 하므로, App 컴포넌트는 `` 태그를 렌더링해야 합니다. * **optional** `options`: 정적 생성 옵션을 가진 객체입니다. * **optional** `bootstrapScriptContent`: 지정될 경우, 이 문자열이 인라인 ` ``` 클라이언트에서 부트스트랩 스크립트는 [`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에 이벤트 리스너를 연결해 인터랙티브하게 만듭니다. #### 빌드 출력에서 CSS 및 JS 에셋 경로 읽기 {/*reading-css-and-js-asset-paths-from-the-build-output*/} 최종 에셋 URL(예: JavaScript와 CSS 파일)은 빌드 후 종종 해시Hash가 추가됩니다. 예를 들어 `styles.css` 대신 `styles.123456.css`와 같은 파일명이 될 수 있습니다. 정적 에셋 파일명에 해시를 추가하면 동일한 에셋이라도 빌드마다 다른 파일명을 갖게 됩니다. 이는 정적 에셋에 대해 장기 캐싱을 안전하게 활성화할 수 있게 해줍니다. 특정 이름을 가진 파일은 그 내용이 절대 변경되지 않기 때문입니다. 그러나 빌드가 끝난 후에야 에셋 URL을 알 수 있다면, 이를 소스 코드에 넣을 방법이 없습니다. 예를 들어 앞서처럼 JSX에 `"/styles.css"`를 하드코딩하면 동작하지 않습니다. 소스 코드에 에셋 경로를 넣지 않으려면, 루트 컴포넌트가 Prop으로 전달된 맵에서 실제 파일명을 읽어올 수 있습니다. ```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' }; app.use('/', async (request, response) => { const { prelude } = await prerenderToNodeStream(, { bootstrapScripts: [assetMap['/main.js']] }); response.setHeader('Content-Type', 'text/html'); prelude.pipe(response); }); ``` 서버에서 ``을 렌더링하고 있으므로, Hydration 오류를 피하려면 클라이언트에서도 `assetMap`과 함께 렌더링해야 합니다. 다음과 같이 `assetMap`을 직렬화해 클라이언트로 전달할 수 있습니다. ```js {9-10} // 이 JSON은 빌드 도구에서 가져와야 합니다. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' }; app.use('/', async (request, response) => { const { prelude } = await prerenderToNodeStream(, { // 주의: 이 데이터는 사용자가 생성한 것이 아니므로 `stringify()`해도 안전합니다. bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`, bootstrapScripts: [assetMap['/main.js']], }); response.setHeader('Content-Type', 'text/html'); prelude.pipe(response); }); ``` 위 예시에서 `bootstrapScriptContent` 옵션은 클라이언트에서 전역 변수 `window.assetMap`을 설정하는 인라인 `