React Portal은 컴포넌트를 기존의 부모 컴포넌트의 DOM 계층구조가 아닌, 다른 위치의 DOM 노드로 렌더링할 수 있게 해주는 기능입니다.
DOM의 스타일 구조가 컴퓨터에 의해 이해되어 렌더링된다는 것은, 브라우저가 HTML과 CSS를 해석하여 픽셀 단위로 화면에 표시하는 과정을 말합니다. 이때, React Portal을 사용하면, 원하는 위치에 컴포넌트를 '직접' 렌더링할 수 있어서, 렌더링 트리에서 부모 컴포넌트 바깥에 렌더링하는 것을 가능하게 합니다. 이는 레이아웃이나 스타일링에 영향을 주는 중첩된 DOM 구조에서 벗어나 원하는 위치에 직접 UI를 배치할 수 있는 유연성을 제공합니다.
1.모달, 팝업, 툴팁 구현 용이성: 이러한 UI 요소들은 종종 애플리케이션의 다른 부분과 겹쳐서 표시됩니다. Portal을 사용하면, 이러한 요소들을 문서의 최상위에 렌더링할 수 있어, z-index와 같은 CSS 스타일링 문제를 쉽게 해결할 수 있습니다.
2.이벤트 버블링: Portal 내부에서 발생한 이벤트는 React 컴포넌트 트리에서 Portal을 사용하는 컴포넌트까지 버블링됩니다. 이는 Portal을 사용하더라도 React의 이벤트 시스템을 그대로 활용할 수 있음을 의미합니다.
3.부모 컴포넌트와의 독립성: 부모 컴포넌트의 CSS 스타일링이나 레이아웃에 영향을 받지 않고, 독립적인 UI 부분을 렌더링할 수 있습니다. 이는 복잡한 스타일링이나 레이아웃이 있는 애플리케이션에서 UI 충돌을 방지하는 데 도움이 됩니다.
4.성능 최적화: 특정 상황에서 Portal을 사용하면 불필요한 렌더링을 방지하고 성능을 향상시킬 수 있습니다. 예를 들어, 페이지의 깊은 곳에 위치한 컴포넌트에서 대규모 업데이트가 발생할 경우, Portal을 통해 이를 독립적으로 관리함으로써 전체 페이지 렌더링을 방지할 수 있습니다.
5.유연성: Portal을 사용하면 어떤 DOM 노드로든 컴포넌트를 렌더링할 수 있어, 애플리케이션의 구조와 상관없이 UI를 유연하게 구성할 수 있습니다. 이는 대규모 애플리케이션 또는 특정 레이아웃 요구 사항이 있는 애플리케이션에서 특히 유용합니다.
-> 리액트 포탈을 사용하여 모달을 생성함으로써 , 재사용성을 높히고 DOM-tree 상의 부포-자식 컴포넌트가 독립적으로 구조를 유지하도록 하였다.또한 모달생성시 발생 할 수 있는 오버레이문제를 해결하기 위해 적용했다.
1)create portal: ReactDom.createPortal 메소드를 사용하여 컴포넌트를 "포털한다(portal)" 하여 React 컴포넌트 트리의 현재 위치와는 다른, children으로 받은 컨텐츠를 모달로서 페이지의 특정 위치(DOM 요소 el 내부)에 렌더링하는 역할을 한다.
import ReactDom from "react-dom";
export const ModalPortal = ({children}) => {
const el = document.getElementById("modal");
return ReactDom.createPortal(children,el);
};
2)모달 컴포넌트 생성
import React from "react";
import * as S from "./style/MyModal.style"
export const MyModal = ({message, onClose ,onConfirm}) => {
return (
<S.MyModal>
<S.Mask onClick={onClose}></S.Mask>
<S.ModalBody>
<S.ModalContent >
<p>{message}</p>
<button onClick={onClose}>취소</button>
<button onClick={onConfirm}>확인</button>
</S.ModalContent>
</S.ModalBody>
</S.MyModal>
);
};
3)페이지에 적용 : modal info 상태 변수 정의후 모달이 보여질시 표시되어질 정보를 정의하고 , 댓글삭제및 게시글 삭제 메소드 함수를 생성 후 삼항 연산자를이용하여 둘중의 하나가 표시되고 실행되도록 코드를 정리하였다.
const [modalInfo, setModalInfo] = useState({ show: false, type: '', message: '', action: null });
const showModal = (type, message, action) => {
setModalInfo({ show: true, type, message, action });
};
const closeModal = () => {
setModalInfo({ ...modalInfo, show: false });
};
const handleDelete = () => {
showModal('deletePost', '정말로 이 게시글을 삭제하시겠습니까?', fetchPostDelete);
};
const handleCommentDelete = (commentId) => {
const action = () => fetchCommentDelete(commentId);
showModal('deleteComment', '정말로 댓글을 삭제하시겠습니까?', action);
};
{modalInfo.show && (
<ModalPortal>
<MyModal
title={modalInfo.type === 'deleteComment' ? '댓글 삭제' : '게시글 삭제'}
message={modalInfo.message}
onConfirm={() => {
modalInfo.action && modalInfo.action();
closeModal();
}}
onClose={closeModal}
/>
</ModalPortal>
참고 블로그 : 박히밍 개발 블로그