React.CreatePortal 을 사용한 모달창 만들기

D uuu·2023년 11월 7일

React

목록 보기
2/10

우선 모달이란, 모달이 띄어지면 포커스와 제어권을 독점하여 기존 화면을 제어할 수 없는 기능을 뜻한다. 따라서 모달은 항상 최상위권에 위치해야 하고, 모달이 열려있을때는 기존 화면을 움직이거나(스크롤) 제어가 불가능해야 한다.

여기서 핵심은 최상위권에 위치한다는 것이다. 지난 글에서 내가 만든 모달도 물론 최상위에 위치해 있지만, 그건 css 면에서 봤을때나 그렇다. z-index 값을 높게 주어 말그대로 배경 화면보다 위에 띄어놨기 때문이다.

하지만 컴포넌트 트리 관점에서 본다면 모달이 과연 최상단에 위치해 있을까? 지난 글 속 구조라면 그렇지 않다. 저번 방법은 모달이 언제나 최상위에 보여지는 것을 보장하지 못한다. 만약 자식 컴포넌트에서 모달 컴포넌트를 불러온다면 이 모달 컴포넌트는 부모 컴포넌트 스타일의 영향을 받을 수 있기 때문이다.

예를들어 자식 컴포넌트의 z-index 값을 아무리 크게 주더라도 부모의 z-index 값을 따르게 된다. 따라서 모달이 기존 화면보다 위에 위치하지 않게 되고, 경우에 따라서는 부모 컴포넌트, 자식컴포넌트의 z-index 값을 모두 수정해야 하니 번거롭고 모달을 만들때마다 매번 신경써야 하는 부분이 생긴다.


출처 : 위키백과 트리구조

6번에서 modal 을 불러와 사용한다면, 부모 컴포넌트인 7번의 스타일의 영향을 받게 된다. 7번은 또 7번의 부모 컴포넌트인 2번의 영향을 받게 된다. 따라서 modal 은 6번에서 사용하지만 7, 2번의 스타일(z-index)에도 신경을 써야 한다는 단점이 있다.



공식 홈페이지에서는 React.createPortal 에 대해 이렇게 설명한다.

Portal 은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링 하는 최고의 방법을 제공합니다.

위와 같은 문제는 React.createPortal 을 이용해 해결할 수 있다. 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링 하여 더이상 부모 컴포넌트의 스타일에 영향을 받지 않고 독립적으로 존재하도록 하는 것이다. 이게 무슨말인고 하니?


출처 : 위키백과 닥터 스트레인지

<index.html>

  <div id="overlays"></div>
  <div id="root"></div>

닥터스트레인지에서 포탈을 통해 다른 연결된 곳으로 이동하는 것과 비슷하다. index.html 의 DOM 계층 구조인 root 위에 새로운 DOM 을 하나 생성하고, Modal 컴포넌트에서 getElementById 로 HTMLElement 를 불러와 모달을 여기서 렌더링 되도록 설정해주면 된다.

const portalElement = document.getElementById("overlays") as HTMLElement;

const Modal = ({ children, onClose }: TModal) => {
  return (
    <>
      {ReactDOM.createPortal(<BackDrop onClose={onClose} />, portalElement)}
      {ReactDOM.createPortal(
        <ModalOverlay>{children}</ModalOverlay>,
        portalElement
      )}
    </>
  );
};

export default Modal;

이렇게 했을때의 이점은?

🔹 modal 이 독립적인 곳에서 렌더링 되므로 더이상 부모 컴포넌트의 스타일을 신경쓰지 않아도 된다. 즉, 부모-자식 컴포넌트간의 제약에서 Portal 을 통해 독립적인 부모-자식 관계를 유지할 수 있다.

🔹다시 맨 상단으로 돌아가서 모달이란 최상위권에 위치해야 한다. 각각의 컴포넌트에서 모달 컴포넌트를 불러와 사용한다면 그 컴포넌트에서는 z-index 값을 주어 상위에 위치할지언정 전체적인 구조면에서 봤을때는 그렇지 않다. 모달은 결국 전체페이지에 대한 overlay 이므로 구조적으로 모달은 최상위에 위치해 있다고 명시적으로 나타내줄 수 있다.

느낀점..

처음에는 index.html 에 새로운 Element 를 생성하면서 모달을 만들어야 하나 '굳이..?' 라는 생각이 들었다. 그런데 createPortal 를 직접 적용해보니 모달을 불러오는 컴포넌트 외에 다른 요소(부모 컴포넌트) 에는 신경을 쓰지 않아도 되니 매우 편리했다.

그래서 현재 프로젝트에서는 createPortal 로 수정하였다. 기존의 방법으로도 모달이 잘 동작은 하나, 프로젝트가 점점 커질수록 모달이 중첩 될 가능성이 있기에 수정을 하는 편이 낫겠다는 판단이 섰다.

실제로 회사에서는 어떤 방식으로 모달을 만들고 사용하고 있는지 너무 궁금하다.🤔

profile
배우고 느낀 걸 기록하는 공간

0개의 댓글