리액트 작업을 진행할때
createPortal 메소드는 리액트의 react-dom 라이브러리에 있는 기능으로, 리액트 애플리케이션의 컴포넌트 트리 외부에 자식 요소를 렌더링하는 데 사용된다.
일반적으로 리액트 컴포넌트는 자신이 속한 컴포넌트 트리 내에서만 렌더링되지만, createPortal을 사용하면 자식 요소를 다른 DOM 요소 내에 렌더링할 수 있다.
보통은 아래와 같은 상황에서 포탈작업을 활용할 수 있다.
모달 또는 팝업 창과 같은 오버레이 컴포넌트: 일반적으로 모달은 애플리케이션의 컴포넌트 트리의 일부가 아니라 별도의 레이어에 표시된다. createPortal을 사용하면 모달 컴포넌트를 애플리케이션의 레이아웃에서 분리하여 렌더링할 수 있다.
z-index 조절이 필요한 경우: createPortal을 사용하면 자식 요소를 다른 DOM 요소의 z-index 값에 따라 렌더링할 수 있다. 이를 통해 오버레이, 드롭다운 메뉴 등과 같은 요소를 화면의 다른 요소 위에 정확하게 배치할 수 있다.
1번의 경우에서 모달과 같은 오버레이 컴포넌트를 사용하게 될때 어떻게 적용되는지 코드로 살펴보면 아래와 같다.
...
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="backdrop-root"></div>
<div id="overlay-root"></div>
<div id="root"></div>
</body>
...
보통은 "root"DOM 요소안에서 모든 앱 컴포넌트들이 렌더링되는 위치이지만 나중에 지정된 모달이나 백드롭에 해당하는 요소를 저위치에서 포탈을 이용해 렌더링되게 하기위해 포인터를 지정한다.
import ReactDOM from "react-dom";
import Button from "./Button";
import Card from "./Card";
import styles from "./Modal.module.css";
// ReactDOM.createPortal을 이용한 포탈만들기
const BackDrop = (props) => {
return <div className={styles["modal-backDrop"]} onClick={props.onToggle} />;
};
const ModalOverlay = (props) => {
return (
<Card className={styles["modal-container"]}>
<div onClick={(event) => event.stopPropagation()}>
<header className={styles["modal-title"]}>
<h2>{props.title}</h2>
</header>
<div className={styles["modal-content"]}>
<p>{props.message}</p>
</div>
</div>
<footer>
<Button type="button" onClick={props.onToggle}>
Okay
</Button>
</footer>
</Card>
);
};
function Modal(props) {
return (
<>
{ReactDOM.createPortal(
<BackDrop onToggle={props.onToggle} />,
document.getElementById("backdrop-root")
)}
{ReactDOM.createPortal(
<ModalOverlay
title={props.title}
message={props.message}
onToggle={props.onToggle}
/>,
document.getElementById("overlay-root")
)}
</>
);
}
export default Modal;
ReactDOM.createPortal()의 첫번째 인자로는 렌더링되어야하는 리엑트노드 즉 컴포넌트를 지정해주고, 두번째인자엔 첫번째인자의 컴포넌트가 실제 렌더링될 DOM위치의 요소를 가리키는 포인터이다. 따라서 실제 DOM요소에 접근하기 위해 브라우저에서 제공하는 API를 적용해준다.
따라서 위 예시의 Modal컴포넌트는 'createPortal'을 사용해 각각 정의해준 'BackDrop'컴포넌트와 'ModalOverlay'컴포넌트들을 각각 'backdrop-root' 과 'overlay-root'라는 DOM요소에 렌더링 하게된다.
즉 모달 컴포넌트가 자체적으로 렌더링되지않고, 별도의 DOM요소 내에 렌더링 되는걸 볼 수 있다.
이렇게 포탈을 이용해 DOM조작을 진행하게 되면 개발자 도구에서 해당 이벤트가 발생할때, 지정해준 컴포넌트가 root요소 바깥 body태그의 직계자식으로 위치하여 렌더링 되는 모습을 확인할 수 있다.
포탈은 주로 DOM 조작이 필요한 상황에서 유용하게 사용이 되고 의미론적으로 더 정확한 HTML코드를 쓸 수 있게 되지만, 남용하거나 오용하는 경우 컴포넌트 구조를 복잡하게 만들 수 있으므로 필요한 경우에만 사용하는 것이 좋을것 같다는 생각이다.