Portal
이란 ?리액트 포탈(React Portal)
은 컴포넌트의 렌더링 트리를 벗어나 다른 DOM 노드에 렌더링할 수 있는 기능을 제공한다.
일반적으로 리액트 컴포넌트는 부모 컴포넌트의 DOM 계층 구조 내에서 렌더링되기 때문에, 부모 요소의 영향으로 인해 예상치 못한 결과가 발생할 수 있다.
리액트 포탈을 사용하면 컴포넌트를 원하는 DOM 위치에 독립적으로 렌더링할 수 있어 이러한 문제를 해결할 수 있다.
예를 들어, z-index
와 같은 스타일 속성은 부모 컴포넌트의 영향을 받기 쉬운데, 리액트 포탈을 사용하면 부모 컴포넌트의 스타일에 구애받지 않고 원하는 위치에 컴포넌트를 렌더링할 수 있게 된다. 이를 통해 유지보수성이 향상되고 CSS 충돌을 방지할 수 있다.
포탈은 주로 모달, 토스트, 드롭다운 메뉴 등과 같이 상위 DOM 구조에서 독립적으로 존재해야 하는 UI 컴포넌트를 구현할 때 유용하다.
포탈을 사용하려면 ReactDOM.createPortal
함수를 사용한다.
이 함수는 두 개의 인자를 받는데 첫 번째 인자는 렌더링할 리액트 컴포넌트, 두 번째 인자는 해당 컴포넌트를 렌더링할 DOM 노드다.
import { createPortal } from 'react-dom';
function Modal({ children }) {
// 모달을 렌더링할 DOM 노드
const modalRoot = document.getElementById('_modal');
// children을 '_modal' 노드에 렌더링
return createPortal(
children,
modalRoot
);
}
위의 예제에서, Modal 컴포넌트는 #_modal라는 ID를 가진 DOM 요소에 렌더링된다.
이처럼 createPortal
을 사용하면 특정 DOM 노드에 리액트 컴포넌트를 렌더링할 수 있게 된다.
나는 Next.js로 프로젝트를 진행했기 때문에 _document
컴포넌트에 id가 _modal
인 div 요소를 추가했다. 포탈을 통해 이 요소의 위치에 모달이 뜨게 된다.
#_modal이라는 ID를 가진 DOM 노드를 찾아서 selectedElement에 저장했다.
모달 포탈이 mount되었을 경우, 뒤 배경의 스크롤을 방지하기 위해 overflow를 hidden으로 설정하고, 언마운트될 때는 스크롤이 정상적으로 되도록 overflow를 auto로 변경했다.
예제에는 뺐지만 Next.js 로 이대로 구현할 경우 hydration 에러가 나기 때문에
if (typeof window === 'undefined')
를 통해서 서버 렌더링이 아닌 클라이언트 렌더링이 되게끔 해줘야 한다!
또한, 모달의 투명하고 어두운 배경을 설정하기 위해 BackModal이라는 컴포넌트를 만들고 스타일링을 했다. 이 컴포넌트는 onClose라는 모달을 닫는 함수를 prop으로 받아, 배경을 클릭했을 때 모달이 자동으로 닫히도록 설정했다.
개발자 도구를 통해 모달을 열 때마다, 최상단에 설정된 #_modal ID를 가진 div 요소에 포털로 연결된 컴포넌트들이 렌더링되는 것을 확인할 수 있다.
유용한 글 잘 읽었습니다! 이번 프로젝트에도 한 번 사용해보면 좋을 것 같네요 ㅎㅎ