원문: https://react.dev/reference/react-dom/client/hydrateRoot
hydrateRoot
hydrateRoot
는 서버에서 미리 생성된 HTML이 있는 브라우저 DOM 노드에 React 컴포넌트를 표시할 수 있게 해줍니다.
const root = hydrateRoot(domNode, reactNode, options?)
- 레퍼런스
- hydrateRoot(domNode, reactNode, options?)
- root.render(reactNode)
- root.unmount()
- 사용법
- 서버 렌더링된 HTML 하이드레이션하기
- 전체 문서 하이드레이션하기
- 불가피한 하이드레이션 불일치 에러 무시하기
- 클라이언트와 서버 내용이 다를 때 처리
- 하이드레이션된 루트 컴포넌트 업데이트하기
- 프로덕션 환경에서 에러 로깅하기
- 문제 해결
- root.render에 두 번째 인자를 전달했을 때의 에러
레퍼런스
hydrateRoot(domNode, reactNode, options?)
서버에서 미리 렌더링된 HTML이 있는 DOM 요소에 React를 "연결"하려면 hydrateRoot
를 호출하세요.
import { hydrateRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = hydrateRoot(domNode, <App />);
React는 해당 domNode
내부의 기존 HTML에 "붙어서" 그 이후의 DOM 관리를 takeover합니다. 대부분의 앱에서는 루트 컴포넌트에 대해 한 번만 호출합니다.
파라미터
domNode
: 서버에서 렌더링된 루트 DOM 요소reactNode
: 기존 HTML을 렌더링할 때 사용한 React 노드(JSX 등)options
(선택): 객체. 루트에 대한 옵션입니다.onCaughtError
: Error Boundary에서 에러를 잡았을 때 호출되는 콜백onUncaughtError
: Error Boundary에서 잡히지 않은 에러가 발생했을 때 호출되는 콜백onRecoverableError
: React가 자동으로 복구 가능한 에러를 처리할 때 호출되는 콜백identifierPrefix
: 여러 루트에서 useId로 생성되는 ID의 접두사(서버와 동일해야 함)
반환값
hydrateRoot
는 render
와 unmount
메서드를 가진 객체를 반환합니다.
주의사항
hydrateRoot()
는 서버에서 렌더링된 내용과 클라이언트에서 렌더링된 내용이 동일해야 합니다. 불일치는 버그로 간주하고 반드시 수정해야 합니다.- 개발 모드에서는 불일치에 대해 경고가 표시됩니다. 성능상 이유로 모든 마크업을 검증하지는 않습니다.
- 대부분의 앱에서는 한 번만 호출합니다. 프레임워크를 사용한다면 프레임워크가 대신 호출할 수 있습니다.
- 클라이언트에서만 렌더링되는 앱에는
hydrateRoot()
를 사용하지 마세요. 이 경우createRoot()
를 사용하세요.
root.render(reactNode)
하이드레이션된 루트의 React 컴포넌트를 업데이트하려면 root.render
를 호출하세요.
root.render(<App />);
파라미터
reactNode
: 업데이트할 React 노드(JSX 등)
반환값
root.render
는 아무것도 반환하지 않습니다.
주의사항
- 하이드레이션이 끝나기 전에
root.render
를 호출하면 기존 서버 렌더링된 HTML이 모두 지워지고 클라이언트 렌더링으로 전환됩니다.
root.unmount()
root.unmount
를 호출하면 해당 루트의 React 트리를 DOM에서 제거하고, 관련 리소스를 정리합니다.
root.unmount();
반환값
root.unmount
는 아무것도 반환하지 않습니다.
주의사항
- 호출 시 트리 전체가 언마운트되고 React가 해당 DOM 노드에서 완전히 분리됩니다.
- 한 번 언마운트하면 다시
root.render
를 호출할 수 없습니다. 호출 시 "Cannot update an unmounted root" 에러가 발생합니다.
사용법
서버 렌더링된 HTML 하이드레이션하기
앱의 HTML이 서버에서 생성된 경우, 클라이언트에서 반드시 하이드레이션해야 합니다.
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(
document.getElementById('root'),
<App />
);
이렇게 하면 서버에서 생성된 HTML에 React의 로직이 "붙어서" 완전히 인터랙티브한 앱이 됩니다.
주의사항
hydrateRoot
에 전달하는 React 트리는 서버에서 렌더링한 결과와 동일한 출력을 내야 합니다. 불일치가 있으면 사용자 경험이 깨질 수 있습니다.
전체 문서 하이드레이션하기
React로 전체 문서를 JSX로 렌더링할 수도 있습니다.
function App() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles.css"></link>
<title>My app</title>
</head>
<body>
<Router />
</body>
</html>
);
}
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(document, <App />);
불가피한 하이드레이션 불일치 에러 무시하기
서버와 클라이언트에서 어쩔 수 없이 다른 값(예: 타임스탬프 등)이 렌더링되는 경우, 해당 요소에 suppressHydrationWarning={true}
를 추가해 경고를 무시할 수 있습니다.
export default function App() {
return (
<h1 suppressHydrationWarning={true}>
Current Date: {new Date().toLocaleDateString()}
</h1>
);
}
이 속성은 한 단계만 적용되며, 꼭 필요한 경우에만 사용하세요. React는 불일치한 텍스트 내용을 패치하지 않습니다.
클라이언트와 서버 내용이 다를 때 처리
의도적으로 서버와 클라이언트에서 다른 내용을 렌더링해야 한다면, Effect에서 상태를 변경해 두 번 렌더링하는 방식을 사용할 수 있습니다.
import { useState, useEffect } from "react";
export default function App() {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
return (
<h1>
{isClient ? 'Is Client' : 'Is Server'}
</h1>
);
}
이 방식은 하이드레이션 속도를 늦출 수 있으니 사용자 경험에 주의하세요.
하이드레이션된 루트 컴포넌트 업데이트하기
하이드레이션이 끝난 후에는 root.render
로 루트 컴포넌트를 업데이트할 수 있습니다. 하지만 일반적으로는 컴포넌트 내부에서 상태를 업데이트하는 방식이 더 흔합니다.
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
const root = hydrateRoot(
document.getElementById('root'),
<App counter={0} />
);
let i = 0;
setInterval(() => {
root.render(<App counter={i} />);
i++;
}, 1000);
프로덕션 환경에서 에러 로깅하기
옵션으로 에러 핸들러를 지정해 에러 로깅 시스템을 구현할 수 있습니다.
import { hydrateRoot } from "react-dom/client";
import App from "./App.js";
import { reportCaughtError } from "./reportError";
const container = document.getElementById("root");
hydrateRoot(container, <App />, {
onCaughtError: (error, errorInfo) => {
if (error.message !== "Known error") {
reportCaughtError({
error,
componentStack: errorInfo.componentStack,
});
}
},
});
문제 해결
root.render에 두 번째 인자를 전달했을 때의 에러
root.render
는 한 개의 인자만 받습니다. 옵션은 hydrateRoot
에 전달해야 합니다.
// 🚩 잘못된 예시
root.render(App, {onUncaughtError});
// ✅ 올바른 예시
const root = hydrateRoot(container, <App />, {onUncaughtError});
출처: https://react.dev/reference/react-dom/client/hydrateRoot