※ Portal : 문
createPortal
을 사용하면 일부 자식을 DOM의 다른 부분에 렌더링할 수 있습니다.<div> <SomeComponent /> {createPortal(children, domNode, key?)} </div>
참조
createPortal(children, domNode, key?)
포털을 만들려면
createPortal
을 호출해서 JSX와 렌더링될 DOM 노드를 전달하세요.import { createPortal } from 'react-dom'; // ... <div> <p>This child is placed in the parent div.</p> {createPortal( <p>This child is placed in the document body.</p>, // JSX document.body // 렌더링될 DOM Node )} </div>
포털은 DOM 노드의 물리적 배치만 변경합니다. 다른 모든 면에서 포털에 렌더링하는 JSX는 이를 렌더링하는 React 컴포넌트의 자식 노드 역할을 합니다.
Example
Context → 자식은 부모 트리가 제공하는 컨텍스트에 접근할 수 있다.
Bubbling → 이벤트는 React 트리에 따라 자식에서 부모로 버블링 된다.
Props
children
:
JSX 조각 (예:
<div />
나 <SomeComponent />
)Fragment (
<>...</>
) 문자열이나 숫자, 또는 이들의 배열..
⇒ React로 렌더링할 수 있는 모든 것.
domNode
:
document.getElementById()
가 반환하는 것과 같은 일부 DOM 노드. 노드는 이미 존재하고 있어야 합니다.
업데이트 중에 다른 DOM 노드를 전달하면 포털 콘텐츠가 다시 생성됩니다.
- (Optional)
key
: 포털의 키로 사용할 고유 문자열 또는 숫자
반환값
createPortal
은 JSX에 포함되거나 React 컴포넌트에서 반환될 수 있는 React 노드를 반환합니다. React가 렌더링 출력물에서 이를 발견하면, 제공된
children
을 제공된 domNode
안에 배치합니다.주의사항
포털의 이벤트(event)는 DOM 트리가 아닌 React 트리에 따라 전파됩니다.
예를 들어, 포털 내부를 클릭했을 때 포털이
<div onClick>
으로 감싸져 있으면 해당 onClick
핸들러 이벤트가 실행됩니다. 이로 인해 문제가 발생한다면, 포털 내부에서 이벤트 전파를 중지하거나 포털 자체를 React 트리에서 위로 옮기세요.사용법
포털을 사용하면 컴포넌트가 일부 자식을 DOM의 다른 위치로 렌더링할 수 있습니다. 이를 통해 컴포넌트의 일부가 어떤 컨테이너에 있든 그 컨테이너에서 “탈출”(마치 닥터스트레인지 포탈처럼? )할 수 있습니다.
example
모달, 툴팁 등..
1. DOM의 다른 부분으로 렌더링하기
포털을 생성하려면
createPortal
의 결과를 일부 JSX와 함께 렌더링하고 포털이 있어야 할 DOM 노드를 지정합니다React는 사용자가 전달한 JSX에 대한 DOM 노드를 사용자가 제공한 DOM 노드 안에 배치합니다.
포털이 없다면 두 번째
<p>
는 부모 <div>
안에 배치되겠지만, 포털은 이를 document.body
로 “텔레포트” 시킵니다:import { createPortal } from 'react-dom'; export default function MyComponent() { return ( <div style={{ border: '2px solid black' }}> <p>This child is placed in the parent div.</p> {createPortal( <p>This child is placed in the document body.</p>, document.body )} </div> ); }
“두 번째
<p>
가 <body>
에 바로 배치된 것을 확인할 수 있습니다”
포털은 DOM 노드의 물리적 배치만 변경합니다. 다른 모든 면에서 포털에 렌더링하는 JSX는 이를 렌더링하는 React 컴포넌트의 자식 노드 역할을 합니다.
자식은 부모 트리가 제공하는 컨텍스트에 접근할 수 있으며 이벤트는 여전히 React 트리에 따라 자식에서 부모로 버블링 됩니다.
2. 포털로 모달 렌더링하기
모달을 불러오는 컴포넌트가
overflow: hidden
또는 모달을 방해하는 다른 스타일이 있는 컨테이너 안에 있더라도, 포털을 사용하여 나머지 페이지 위에 떠 있는 모달을 만들 수 있습니다.
이 예제에서는 두 컨테이너에 모달을 방해하는 스타일이 있지만, 포털에 렌더링된 모달은 DOM에서 부모 JSX 요소에 포함되지 않기 때문에 영향을 받지 않습니다. (물리적 이동 → 부모 스타일에 영향을 받지 않음)
함정
포털을 사용할 때 앱에 접근할 수 있는지를 확인하는 것이 중요합니다. 예를 들어, 사용자가 포털 안팎으로 자연스럽게 초점을 이동할 수 있도록 키보드 포커스를 관리해야 할 수 있습니다.
모달을 만들 때는 WAI-ARIA 모달 제작 사례를 따르세요. 커뮤니티 패키지를 사용하는 경우 해당 패키지가 접근 가능한지, 가이드라인을 따르고 있는지 확인하세요.
3. React 컴포넌트를 비 React 서버 마크업으로 렌더링하기
포털은 React 루트가 React로 빌드되지 않은 정적 페이지 또는 서버 렌더링 페이지의 일부일 때 유용할 수 있습니다.
예를 들어,
페이지가 Rails와 같은 서버 프레임워크로 빌드된 경우,
사이드바와 같은 정적 영역 내에 상호작용 가능한 영역을 만들 수 있습니다. 여러 개의 개별 React 루트를 사용하는 것과 비교하여,
포털을 사용하면 app의 일부가 DOM의 다른 부분에 렌더링되더라도 app을 공유 state를 가진 단일 React 트리? 로 처리할 수 있습니다.
import { createPortal } from 'react-dom'; const sidebarContentEl = document.getElementById('sidebar-content'); export default function App() { return ( <> <MainContent /> {createPortal( <SidebarContent />, sidebarContentEl )} // 앱의 일부 </> ); } function MainContent() { return <p>This part is rendered by React</p>; } function SidebarContent() { return <p>This part is also rendered by React!</p>; }
4. React 컴포넌트를 비 React DOM 노드로 렌더링하기
포털을 사용해 React 외부에서 관리되는 DOM 노드의 콘텐츠를 관리할 수도 있습니다.
예를 들어,
React가 아닌 맵 위젯과 통합하고 팝업 안에 React 콘텐츠를 렌더링하고 싶다고 가정해 봅시다.
이렇게 하려면 렌더링할 DOM 노드를 저장할
popupContainer
state 변수를 선언하세요:const [popupContainer, setPopupContainer] = useState(null);
third-party 위젯을 만들 때 위젯이 반환하는 DOM 노드를 저장하여 렌더링할 수 있도록 합니다:
useEffect(() => { if (mapRef.current === null) { const map = createMapWidget(containerRef.current); mapRef.current = map; const popupDiv = addPopupToMapWidget(map); setPopupContainer(popupDiv); } }, []);
이렇게 하면
createPortal
을 사용하여 React 콘텐츠가 사용 가능해지면 popupContainer
로 렌더링할 수 있습니다:return ( <div style={{ width: 250, height: 250 }} ref={containerRef}> {popupContainer !== null && createPortal( <p>Hello from React!</p>, popupContainer )} </div> );
