createRoot를 사용하면 브라우저 DOM 노드 안에 React 컴포넌트를 표시하는 루트를 만들 수 있음.
const root = createRoot(domNode, options?)
createRoot(domNode, options?)createRoot를 호출하여 브라우저 DOM 요소 안에 콘텐츠를 표시하기 위한 React 루트를 생성할 수 있음.
import { createRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = createRoot(domNode);
React는 domNode에 대한 루트를 생성하고 그 안에 있는 DOM을 관리함. 루트를 생성한 후에는 root.render를 호출하여 그 안에 React 컴포넌트를 표시해야 함:
root.render(<App />);
React로만 빌드된 앱에는 일반적으로 루트 컴포넌트에 대한 createRoot 호출이 하나만 있음. 일부에 React의 "스프링클"을 사용하는 페이지에는 필요한 만큼의 별도의 루트가 있을 수 있음.
domNode: DOM 요소. React는 이 DOM 요소에 대한 루트를 생성하고 렌더링된 React 콘텐츠를 표시하기 위한 render와 같은 함수를 루트에서 호출할 수 있도록 함.
options (optional): React 루트에 대한 옵션이 있는 객체.
onRecoverableError (optional): React가 오류로부터 자동으로 복구할 때 호출되는 callback.identifierPrefix (optional): React가 useId에 의해 생성된 ID에 사용하는 문자열 접두사. 같은 페이지에서 여러 루트를 사용할 때 충돌을 피하는 데 유용함.render와 unmount 두 가지 메서드가 있는 객체를 반환함.
앱이 서버 렌더링되는 경우 createRoot() 사용은 지원되지 않음. 대신 hydrateRoot()를 사용할 것.
앱에 createRoot 호출이 하나만 있을 수 있음. 프레임워크를 사용하는 경우 프레임워크가 이 호출을 대신 수행할 수 있음.
컴포넌트의 자식이 아닌 DOM 트리의 다른 부분(예: 모달 또는 툴팁)에 JSX 조각을 렌더링하려는 경우 createRoot 대신 createPortal을 사용할 것.
root.render(reactNode)root.render를 호출하여 JSX 조각("React 노드")을 React 루트의 브라우저 DOM 노드에 표시할 수 있음.
root.render(<App />);
React는 루트에 <App />을 표시하고 그 안에 있는 DOM을 관리함.
reactNode: 표시할 React 노드. 일반적으로 <App />과 같은 JSX 조각이지만, createElement()로 생성된 React 엘리먼트, 문자열, 숫자, null 또는 undefined를 전달할 수도 있음.undefined를 반환함.
root.render를 처음 호출하면 React는 React 컴포넌트를 렌더링하기 전에 React 루트 내부의 모든 기존 HTML 콘텐츠를 지움.
루트의 DOM 노드에 서버에서 또는 빌드 중에 React에 의해 생성된 HTML이 포함된 경우, 기존 HTML에 이벤트 핸들러를 붙여주는 hydrateRoot()를 사용할 것.
동일한 루트에서 render를 두 번 이상 호출하면 React는 전달한 최신 JSX를 반영하기 위해 필요에 따라 DOM을 업데이트함. React는 이전에 렌더링된 트리와 "비교"해서 재사용할 수 있는 부분과 다시 만들어야 하는 부분을 결정함. 동일한 루트에서 render를 다시 호출하는 것은 루트 컴포넌트에서 set 함수를 호출하는 것과 유사함: React는 불필요한 DOM 업데이트를 지양함.
root.unmount()root.unmount를 호출하여 React 루트 내부에서 렌더링된 트리를 파괴할 수 있음.
root.unmount();
React로 완전히 빌드된 앱에는 일반적으로 root.unmount 호출이 없음.
이 함수는 React 루트의 DOM 노드(또는 그 조상 노드)가 다른 코드에 의해 DOM에서 제거될 수 있는 경우에 주로 유용함. 예를 들어, DOM에서 비활성 탭을 제거하는 jQuery 탭 패널이 있다면, 탭이 제거될 때 그 안에 있는 모든 것(내부의 React 루트를 포함)도 DOM에서 제거됨. 이 경우 root.unmount를 호출하여 제거된 루트의 콘텐츠 관리를 "중지"하도록 React에 지시해야 함. 그렇지 않으면 제거된 루트 내부의 컴포넌트가 구독과 같은 전역 리소스를 정리하고 확보해야 하는 것을 모름.
root.unmount를 호출하면 루트의 모든 컴포넌트가 unmount 되고, 트리의 이벤트 핸들러나 state를 제거하는 것을 포함하여 루트 DOM 노드에서 React가 "분리"됨.
어떤 parameter도 허용하지 않음.
undefined를 반환함.
root.unmount를 호출하면 트리의 모든 컴포넌트가 unmount 되고 루트 DOM 노드에서 React가 "분리"됨.
root.unmount를 호출하고 나면 같은 루트에서 root.render를 다시 호출할 수 없음. Unmount 된 루트에서 root.render를 호출하려고 하면 "Cannot update an unmounted root" 오류가 발생함. 그러나 해당 노드의 이전 루트가 unmount된 후 동일한 DOM 노드에 대한 새 루트를 만들 수 있음.
앱이 완전히 React로만 빌드된 경우, 전체 앱에 대해 단일 루트를 생성할 것.
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
일반적으로 이 코드는 시작할 때 한 번만 실행하면 됨. 이 코드는:
앱이 완전히 React로만 빌드된 경우, 루트를 더 만들거나 root.render를 다시 호출할 필요가 없음.
이 시점부터 React는 전체 앱의 DOM을 관리함. 컴포넌트를 더 추가하려면, App 컴포넌트 안에 중첩할 것. UI를 업데이트해야 할 때, 각 컴포넌트는 state를 사용하여 이를 수행할 수 있음. 모달이나 툴팁과 같은 추가 콘텐츠를 DOM 노드 외부에 표시해야 하는 경우, portal을 사용하여 렌더링할 것.
Note
HTML이 비어 있으면 앱의 JavaScript 코드가 로드되고 실행될 때까지 사용자에게 빈 페이지가 표시됨:
<div id="root"></div>이는 매우 느리게 느껴질 수 있음! 이 문제를 해결하기 위해 서버에서, 또는 빌드 중에 컴포넌트로부터 초기 HTML을 생성할 수 음. 그러면 방문자는 JavaScript 코드가 로드되기 전에 텍스트를 읽고, 이미지를 보고, 링크를 클릭할 수 있음. 이 최적화를 기본적으로 수행하는 프레임워크를 사용하는 것이 좋음. 실행 시점에 따라 이를 server-side rendering(SSR) 또는 static site generation(SSG)이라고 함.
Pitfall
server rendering 또는 static generation을 사용하는 앱은
createRoot대신hydrateRoot를 호출해야 함. 그러면 React는 DOM 노드를 파괴하고 다시 생성하는 대신 HTML에서 hydrate(재사용)함.
페이지가 React로만 완전히 빌드되지 않은 경우, createRoot를 여러 번 호출하여 React가 관리하는 각 최상위 UI 조각에 대한 루트를 생성할 수 있음. root.render를 호출하여 각 루트에 다른 콘텐츠를 표시할 수 있음.
다음 예제에서는 두 개의 서로 다른 React 컴포넌트가 index.html 파일에 정의된 두 개의 DOM 노드에 렌더링됨:
// index.html
<!DOCTYPE html>
<html>
<head><title>My app</title></head>
<body>
<nav id="navigation"></nav>
<main>
<p>This paragraph is not rendered by React (open index.html to verify).</p>
<section id="comments"></section>
</main>
</body>
</html>
// index.js
import './styles.css';
import { createRoot } from 'react-dom/client';
import { Comments, Navigation } from './Components.js';
const navDomNode = document.getElementById('navigation');
const navRoot = createRoot(navDomNode);
navRoot.render(<Navigation />);
const commentDomNode = document.getElementById('comments');
const commentRoot = createRoot(commentDomNode);
commentRoot.render(<Comments />);
document.createElement()를 사용하여 새 DOM 노드를 생성하고 문서에 수동으로 추가할 수도 있음.
const domNode = document.createElement('div');
const root = createRoot(domNode);
root.render(<Comment />);
document.body.appendChild(domNode); // You can add it anywhere in the document
DOM 노드에서 React 트리를 제거하고 이 트리가 사용하는 모든 리소스를 정리하려면 root.unmount를 호출할 것.
root.unmount();
이 기능은 주로 다른 프레임워크로 작성된 앱 내부에 React 컴포넌트가 있는 경우에 유용함.
같은 루트에서 render를 두 번 이상 호출할 수 있음. 컴포넌트 트리 구조가 이전에 렌더링된 것과 일치하는 한, React는 state를 유지함. 다음 예제에서 counter가 계속 증가하고 input에 타이핑한 내용이 계속 남아있는 것은, 매초마다 반복되는 render 호출로 인한 업데이트가 파괴적이지 않다는 것을 의미함:
// index.js
import { createRoot } from 'react-dom/client';
import './styles.css';
import App from './App.js';
const root = createRoot(document.getElementById('root'));
let i = 0;
setInterval(() => {
root.render(<App counter={i} />);
i++;
}, 1000);
export default function App({counter}) {
return (
<>
<h1>Hello, world! {counter}</h1>
<input placeholder="Type something here" />
</>
);
}
render를 여러 번 호출하는 경우는 드뭄. 일반적으로 컴포넌트가 대신 state를 업데이트함.
앱을 실제로 루트에 렌더링하는 것을 잊지 않았는지 확인할 것:
import { createRoot } from 'react-dom/client';
import App from './App.js';
const root = createRoot(document.getElementById('root'));
root.render(<App />); // ✅
그렇게 할 때까지는 아무것도 표시되지 않음.
이 오류는 createRoot에 전달하는 것이 DOM 노드가 아님을 의미함.
무슨 일이 발생했는지 확실하지 않다면 로깅을 해볼 것:
const domNode = document.getElementById('root');
console.log(domNode); // ???
const root = createRoot(domNode);
root.render(<App />);
예를 들어, domNode가 null이면 getElementById가 null을 반환했다는 뜻임. 이는 호출 시점에 문서에 지정된 ID를 가진 노드가 없는 경우에 발생함. 가능한 몇 가지 원인이 있ㅇ음:
<script> 태그는 HTML에서 그 뒤에 나타나는 DOM 노드를 "볼" 수 없음.이 오류가 발생하는 또 다른 일반적인 경우는 createRoot(domNode) 대신 createRoot(<App />)를 작성하는 경우.
이 오류는 root.render에 전달하는 것이 React 컴포넌트가 아님을 의미함.
이 오류는 root.render를 <Component /> 대신 Component로 호출할 때 발생할 수 있음:
// 🚩 Wrong: App is a function, not a Component.
root.render(App);
// ✅ Correct: <App /> is a component.
root.render(<App />);
또는 root.render에 함수를 호출한 결과 대신 함수를 전달했을 때에도 발생할 수 있음:
// 🚩 Wrong: createApp is a function, not a component.
root.render(createApp);
// ✅ Correct: call createApp to return a component.
root.render(createApp());
앱이 서버 렌더링되고 React로 생성된 초기 HTML을 포함하는 경우, 루트를 생성하고 root.render를 호출하면 해당 HTML이 모두 삭제되고 모든 DOM 노드가 처음부터 다시 생성되는 것을 알 수 있음. 이렇게 하면 속도가 느려지고 포커스와 스크롤 위치가 초기화되며 다른 사용자 입력이 손실될 수 있음.
서버에서 렌더링된 앱은 createRoot 대신 hydrateRoot를 사용해야 함:
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(
document.getElementById('root'),
<App />
);
API가 다르다는 점에 유의할 것. 특히, 일반적으로 더이상 root.render 호출이 없음.