[Next] React Portal로 커스텀 모달 구현하기

Jinny·2024년 11월 28일
0

React

목록 보기
24/24
post-thumbnail

배경

프로젝트에서 모달을 전역 상태로 관리함과 동시에 React Potral을 적용해
재사용 가능한 모달 레이아웃을 구현하였다.

React Potral이란?

React Portal은 일부 자식을 다른 DOM으로 렌더링할 때 사용한다. Portal 없이 컴포넌트, 페이지를 개발하면 계층적으로 컴포넌트를 구성하기 때문에 부모 자식 관계에 영향을 많이 받는다.
모달을 Portal로 적용하는 이유는 모달을 다른 DOM 노드로 렌더링하면 부모 컴포넌트의 DOM 구조와 영향을 최소화하기 위해서이다.

createPortal(children, domNode, key?)
portal을 생성하려면 createPortal을 호출하여 JSX와 DOM 노드를 전달한다.

  • children: React로 렌더링되는 <div/>와 같은 JSX
  • domNode: document.getElementById() 반환되는 것과 같은 일부 DOM 노드이다. 단, 노드는 이미 존재해야 한다. 업데이트 중에 다른 DOM 노드를 전달하면 portal 콘텐츠가 다시 만들어진다.
  • key(option) : portal의 키로 사용할 고유한 문자열 또는 숫자
    ➡️ createPortal은 JSX에 포함하거나 React 컴포넌트에서 반환할 수 있는 React 노드를 반환한다. React가 렌더링 출력에서 해당 노드를 발견하면, 제공된 domNode 안에 제공된 자식들을 배치한다.

portal일 될 부분을 정해준다. page router의 경우, _document.tsx 에서 portal을 정의했지만 app router는 app\layout.tsx에서 정의한다.

// app\layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang='ko'>
      <body>
          <div id='modal'></div>
          {children}
      </body>
    </html>
  );
}

이전에 portal 부분을 <div id='modal'/>로 정의했으므로 해당 요소를 찾아 Portal 컴포넌트를 생성한다.

import ReactDOM from 'react-dom';

type Props = { children: React.ReactNode };

export default function ModalPortal({ children }: Props) {
  const element = document.getElementById('modal') as HTMLElement;
  return ReactDOM.createPortal(children, element);
}

모달 레이아웃을 재사용하기 위해 이전에 생성한 ModalPortal 컴포넌트로 감싸주고 content를 통해 원하는 내용이 담기 컴포넌트를 반환해줄 수 있도록 작성했다.

'use client';

import ModalPortal from './ModalPortal';
import CloseBtn from '@/app/mypage/_svg/CloseBtn';
import { useModalStore } from '@/store/modal';

export default function Modal() {
  const { isOpen, content, closeModal } = useModalStore();
  if (!isOpen) return null;

  return (
    <ModalPortal>
      <div>
        <div className='fixed top-0 left-0 w-full h-full bg-[rgba(0, 0, 0, 0.5)] z-[3] overflow-hidden'>
          <div className='fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-2xl p-4 shadow-md z-[4] overflow-hidden'>
            <div className='flex flex-col p-2 w-[520px]'>
              {content}
              <button className='absolute top-2 right-2' onClick={closeModal}>
                <CloseBtn />
              </button>
            </div>
          </div>
        </div>
      </div>
    </ModalPortal>
  );
}

이제 모달이 열리면 지정한 포탈인 id = modal 요소인 노드에서 Modal 컴포넌트가 위치하고 있는 것을 확인할 수 있다.

🔗 Reference

React-createPortal
Modal Portal 적용 예시

0개의 댓글