예를 들어 다음 사진처럼 팝업창을 띄웠다.
이 팝업창을 띄운 페이지의 구성 조직도를 보자면
정보 입력 페이지 요소 안에 ➡
팝업창이 띄워져 있을 떄 입력창 접근을 막기 위한 어두운 커튼
+ 팝업창
이렇게 입력 페이지 안에 팝업창과 관련된 요소들이 포함되어 있다.
사용자 입장에서 생각해보면 이 팝업창은 모든 요소 위에 존재한다. 구조적으로 모든 요소 위에 존재해야될 팝업창이 정보 입력 페이지 안의 요소로 존재한다.
또 추후에 side drawer, dialog 같은 기능같이 오버레이 관련 컴포넌트를 제작하면 이와같은 방법으로는 예기치 못한 문제를 일으킬 수 있다고 한다.
이런 문제를 해결하고자 React 에서는 Portal 이라는 기능으로 오버레이처럼 항상 위에 존재해야 하는 컴포넌트를 body, 루트 div 최상위 위치 등 여러 곳으로 옮길 수 있게 한다.
Backdrop은 팝업창 뒤에 존재하여 그 뒤에 있는 요소들에 접근하는 것을 막고 팝업창을 더 돋보이게 하는 컴포넌트이다.
const ErrorModal = (props) => {
return <Backdrop onClick={props.onConfirm} />;
};
위 코드는 렌더링되면 CSS 스타일링 덕분에 자신의 역할을 할 수 있지만 자신의 역할을 할 수 있더라도 다른 요소 안에 존재하기 때문에 예측하지 못한 오류가 생길 수 있다.
이 backdrop요소에 Portal을 적용해보자.
// index.html
<body>
<div id="backdrop-root"></div>
<div id="root"></div>
</body>
index.html은 React 컴포넌트를 읽으면서 렌더링하는 root 요소가 있는 곳이다.
index.html의 root div 옆에 "backdrop-root"를 id로 가진 div를 만들었다.
다음은 Portal을 적용하여 작성한 코드이다.
import ReactDom from 'react-dom';
// portal 준비
const PortalBackdrop = (props) => {
return <Backdrop onClick={props.onConfirm} />;
};
const ErrorModal = (props) => {
return (
<React.Fragment>
{ReactDom.createPortal(
<PortalBackdrop onConfirm={props.onConfirm} />,
document.getElementById('backdrop-root')
)}
</React.Fragment>
);
};
Portal을 사용하는 방법은 다음과 같다.
ReactDom.createPortal(리액트 노드, 렌더링 할 위치(포인터))
리액트 노드는 렌더링 되어야 할 요소인 Backdrop을 return 하는 PortalBackdrop이다.
렌더링 할 위치(포인터)는 index.html에 추가한 "backdrop-root"를 id로 가진 div이다.
위에서 만든 레이아웃(backdrop)이 나오기 전에는 backdrop-root div가 비어있다가
레이아웃이 적용된 후에는 backdrop-root div 안에 레이아웃으로 이동했다.