
공식 문서에 따르면 Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하는 최고의 방법을 제공한다고 되어있다.
즉 외부에 존재하는 DOM 노드가 React App DOM 계층 안에 존재하는 것처럼 연결을 해주는 포탈 기능을 제공합니다.
이 말은, 컴포넌트들의 상하 관계, 구조를 가지고 있는 문서인 DOM 상에서, 자식 컴포넌트를 부모 컴포넌트 바깥에 있는 다른 컴포넌트에 전달할 수 있다는 뜻이다.
Portal은 리액트 프로젝트에서 컴포넌트를 렌더링하게 될 때, UI 를 어디에 렌더링 시킬지 DOM 을 사전에 선택하여 부모 컴포넌트의 바깥에 렌더링 할 수 있게 해주는 기능입니다.
➡️ React 의 tree 구조에 따라서 부모 컴포넌트가 렌더링 되면 자식 컴포넌트도 그 영향을 받아서 같이 렌더링이 된다. 그래서 자식 컴포넌트 같은 경우는 쓸데없는 렌더링이 일어날 수도 있고 부모 컴포넌트의 스타일링에 제약을 받아 z-index 와 같은 후처리를 해야한다는 점도 있다.
이러한 DOM tree 상의 부모-자식 컴포넌트 간의 제약에서 Portal을 통해 독립적인 구조와 부모-자식 관계를 동시에 유지할 수 있기 때문에 Portal을 사용하는 것이다.
➡️ 또 다른 이유로는 modal이 독립적이지 않고 부모의 DOM노드 안에서 렌더링 되는 것이 의미적인 관점이나 간결한 HTMl 구조를 갖췄는지의 관점으로 보면 별로 좋지 않기 때문이다.
왜냐, 기본적으로 모달은 페이지 위에 표시되는 오버레이이기 때문이다. 모달은 전체페이지에 대한 오버레이이기 때문에 당연히 다른 모든 것의 위에 있다. 그래서 모달이 만약 다른 HTML 코드 안에 중첩되어 있다면, 기술적으로는 스타일링 덕분에 작동할지 몰라도 좋은 코드가 아니고 좋은 구조 또한 아니게 된다.
리액트 개념에서 오버레이 내용이 있는 모달이 깊게 중첩되면 안 되는 문제를 해결할 수 있게끔 한 방법이 React Portal인 것이다.
import { createPortal } from 'react-dom';
public/index.html
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<div id="modal"></div>
</body>
<div id="root"></div> 하단에 새로운 DOM 하나를 만들어줍니다
export default function Modal({ children }) {
return createPortal(
<dialog>{children}</dialog>,
document.getElementById("modal-root")
);
}
Modal의 children은 React 컴포넌트가 다른 컴포넌트를 내부에 포함할 수 있는 React의 특징을 이용합니다. 이는 모달 내부에 어떤 내용을 넣고 싶은지를 결정할 때 사용됩니다.
Portal의 대상 지정: createPortal의 두 번째 인자로 document.getElementById("modal-root")를 사용합니다.
이는 HTML 문서에서 ID가 modal-root인 요소를 찾아 그 위치에 모달 컴포넌트를 렌더링하라는 지시입니다.
이 요소는 일반적으로 메인 앱 컴포넌트의 계층 외부에 위치합니다.
나중에 우리가 이 컴포넌트를 사용하면 우리가 원하는 JSX 를 id="modal" 을 가진 DOM 엘리먼트 에 렌더링을 할 수 있게 됩니다.
<Modal>
<p>This is modal content</p>
</Modal>
Modal 컴포넌트는 <p>This is modal content</p>라는 문단을 자식으로 받습니다. 이 문단은 모달 창 내부에 렌더링되어 사용자에게 보여집니다.
DOM 계층 분리: createPortal을 사용하면, React 컴포넌트 계층과는 별개로 DOM 계층에서 원하는 위치에 컴포넌트를 렌더링할 수 있습니다. 이는 특히 모달, 툴팁, 알림과 같은 UI 요소에 유용합니다.
이벤트 버블링: 포털을 사용해도 React 이벤트는 포털 바깥으로 버블링됩니다. 즉, 모달 내의 이벤트가 React 컴포넌트 트리 상의 부모 컴포넌트로 전달될 수 있습니다.
https://velog.io/@codns1223/React-React-Portal%EC%9D%B4%EB%9E%80-React-Portal%EB%A1%9C-modal-%EA%B5%AC%ED%98%84#react-portal%EC%9D%B4%EB%9E%80
https://heeeming.tistory.com/entry/React-React-Portal-%EA%B8%B0%EC%88%A0%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-Modal%EC%9D%84-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%8B%A4%EC%8A%B5%ED%8C%8C%EC%9D%BC-%ED%95%98%EB%8B%A8-%EC%B2%A8%EB%B6%80
https://velog.io/@velopert/react-portals