FE/리뷰
[모던 리액트 딥다이브] 4.2장 SSR을 위한 리액트 API
따봉치치
2024. 8. 3. 16:39
728x90
리액트 API
- 리액트는 애플리케이션을 서버에서 렌더링할 수 있는 API도 제공함
- react-dom/server.js에서 확인 할 수 있음
renderToString
- 인수로 넘겨받은 리액트 컴포넌트를 렌더링해 HTML 문자열로 반환하는 함수
- 서버 사이드 렌더링을 구현하는 데 가장 기초적인 API
- 먼저 완성된 HTML을 서버에서 제공할 수 있으므로 초기 렌더링에서 뛰어난 성능일 보임
import React, { useEffect } from "react"
import ReactDOMServer from 'react-dom/server'
function ChilrenComponent({fruits} : {fruits: Array<string>}) {
useEffect(() => {
console.log(fruits);
},[fruits]);
function handleClick() {
console.log('hello');
}
return (
<ul>
{fruits.map((fruit) => (
<li key={fruit} onClick={handleClick}>
{fruit}
</li>
))}
</ul>
)
}
function SampleComponent() {
return (
<>
<div>hello</div>
<ChilrenComponent fruits={['apple','banana','peach']}/>
</>
)
}
const result = ReactDOMServer.renderToString(
React.createElement('div',{id:'root'}, <SampleComponent />),
)
// 위 result가 반환한 문자열
<div id="root" data-reactroot="">
<div>hello</div>
<ul>
<li>apple</li>
<li>banana</li>
<li>peach</li>
</ul>
</div>
- 렌더링은 루트 컴포넌트인 <div id="root" />에서 수행됨
- ChildrenComponent에 있는 useEffect와 같은 훅과 handleClick과 같은 이벤트 핸들러는 결과물에 포함되지 않음
- renderToString은 인수로 주어진 리액트 컴포넌트를 기준으로 빠르게 브라우저가 렌더링할 수 있는 HTML을 제공하는 데 목적이 있는 함수일 뿐
- 클라이언트에서 실행되는 자바스크립트 코드를 포함시키거나 렌더링하는 역할까지 해주지는 않음
- data-reactroot
- 리액트 컴포넌트의 루트 엘리먼트가 무엇인지 식별하는 역할
- 이후에 자바스크립트를 실행하기 위한 hydrate 함수에서 루트를 식별하는 기준점
renderToStaticMarkup
- renerToString과 유사한 함수
- 리액트 컴포넌트를 기준으로 HTML 문자열을 만듦
- 루트 요소에 추가한 data-reactroot와 같은 리액트에서만 사용하는 추가적인 DOM 속성을 만들지 않음
- 결과물인 HTML의 크기를 아주 약간이라도 줄일 수 있음
- 클라이언트에서는 리액트에서 제공하는 useEffect와 같은 브라우저 API를 절대로 실행할 수 없음
- 리액트의 이벤트 리스너가 필요 없는 완전히 순수한 HTML을 만들 때만 사용됨
...
const result = ReactDOMServer.renderToStaticMarkup(
React.createElement('div',{id:'root'}, <SampleComponent />),
)
// 위 result가 반환한 문자열
<div id="root">
<div>hello</div>
<ul>
<li>apple</li>
<li>banana</li>
<li>peach</li>
</ul>
</div>
- 순수한 HTML 문자열이 반환
renderToNodeStream
- renderToString과 결과물이 완전히 같지만 차이점이 있음
- 브라우저에서 사용하는 것이 완전히 불가능함 => 완전히 Node.js 환경에 의존하고 있음
- 결과물 타입이 string이 아닌 Node.js의 ReadableStream임 => 브라우저가 원하는 결과물인 string을 얻기 위해서는 추가적인 처리가 필요함
- renderToString의 결과물이 매우 크면 큰 문자열을 한 번에 메모리에 올려두고 응답을 수행해야 해서 Node.js가 실행되는 서버에 큰 부담이 될 수 있음
- 대신 스트림을 활용하면 데이터를 청크 단위로 분리해 순차적으로 처리할 수 있음
- 대부분의 널리 알려진 리액트 서버 사이드 렌더링 프레임워크는 모두 renderToString 대신 renderToNodeStream을 채택하고 있음ㅇ
스트림 : 동영상과 같이 큰 데이터를 다룰 때 데이터를 청크(chunck, 작은 단위)로 분할해 조금씩 가져오는 방식
renderToStaticNodeStream
- renderToNodeStream과 제공하는 결과물은 동일하나 리액트 자바스크립트에 필요한 리액트 속성이 제공되지 않음
- hydrate를 할 필요가 없는 순수 HTML 결과물이 필요할 때 사용하는 메서드
hydrate
- hydrate 함수는 두 개의 함수 renderToString, renderToNodeStream으로 생성된 HTML 콘텐츠에 자바스크립트 핸들러나 이벤트를 붙이는 역할
- 즉, 정적으로 생성된 HTML에 이벤트와 핸들러를 붙여 완전한 웹페이지 결과물을 만듦
- render와의 차이점
- hydrate는 기본적으로 이미 렌더링된 HTML이 있다는 가정하에 작업이 수행되고, 이 렌더링된 HTML을 기준으로 이벤트를 붙이는 작업만 실행됨
- 즉, hydrate로 넘겨준 두 번째 인수에는 이미 renderToString 등으로 렌더링된 정적인 HTML 정보가 반드시 담겨 있어야 함
// containerId를 가리키는 element는 서버에서 렌더링된 HTML의 특정 위치를 의미함
const element = document.getElementById(containerId)
// 해당 element를 기준으로 리액트 이벤트 핸들러를 붙임
ReactDOM.hydrate(<App/>, element);
render
- 주로 create-react-app으로 생성된 프로젝트의 index.jsx에서 찾아 볼 수 있음
- render 함수는 컴포넌트와 HTML의 요소를 인수로 받음
- HTML의 요소에 해당 컴포넌트를 렌더링하며, 여기에 이벤트 핸들러를 붙이는 작업까지 모두 한 번에 수행함
- 클라이언트에서만 실행되는, 렌더링과 이벤트 핸들러 추가 등 리액트를 기반으로 한 온전한 웹페이지를 만드는 데 필요한 모든 작업을 수행함
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
728x90