[nextjs] Portal 사용해서 Modal띄우기

HYEJIN·2023년 1월 2일
1

프로젝트 기록

목록 보기
5/6
post-custom-banner

Portal?

React portal을 이용하면 UI 를 어디에 렌더링 시킬지 DOM 을 사전에 선택하여
부모 컴포넌트의 바깥에 렌더링 할 수 있게 해줄 수 있다.
즉, DOM 의 계층구조 시스템에 종속되지 않으면서 컴포넌트를 렌더링 할 수 있게 된다.

portal 을 사용한 이유

프로젝트를 진행하면서 부모 컴포넌트의 스타일링 속성에 제약을 받아서 모달이 가려지는 현상으로 z-index값을 조정해줘야 하는 경우가 있었다.
또 최상위에서 전체 스타일에 width:80%를 스타일을 줬다고 가정한다면,
그 안에 modal 이 들어갈 경우 전체 스타일의 영향을 받을 수 밖에 없기 때문에
portal로 분리하도로 했다.


portal 구현하기

portal로 이동하여 modal이 렌더링 될 위치 넣어주기

export default class MyDocument extends Document {
 //코드 생략... 
  render() {
    return (

      <Html>
        <Head />
        <body>
          <Main />
          <div id="modal-root" />
          <NextScript />
        </body>
      </Html>
    );
  }
}

react에서는 index.html에 넣어주지만,next는 html 파일이 없으므로 _document.tsx파일에
<div id="modal-root" /> 를 추가해주었다.

모달을 띄울 때 modal-root 노드가 portal의 도착지점이 된다.


createPortal

// front/components/modal/ModalPortal.tsx

import React, { ReactElement, useEffect, useState } from "react";
import ReactDom, { createPortal } from "react-dom";

const Portal = ({ children }: { children: ReactElement }) => {
  const [mounted, setMounted] = useState<boolean>(false);

  useEffect(() => {
    setMounted(true);
    return () => setMounted(false);
  }, []);

  if (typeof window === "undefined") return <></>;

  return mounted ? createPortal(children, document.getElementById("modal-root") as HTMLElement) : <></>;
};

export default Portal;

호출

const Login = () => {
  return (
    <LoginWrap>
      <LoginContainer>
		//일부코드 생략...
        <Portal>
          <FindPasswordModal />
        </Portal>
      </LoginContainer>
    </LoginWrap>
  );
};

Portal 의 children은 FindPasswordModal로
모달이 modal-root안에서 랜더링된다.

nextjs에서 사용할 때 주의사항

const [mounted, setMounted] = useState<boolean>(false);

  useEffect(() => {
    setMounted(true);
    return () => setMounted(false);
  }, []);

Next는 별도의 설정을 하지 않는다면 SSR과 CSR을 동시에 진행한다.
SSR 과정에서 document에 접근하려고 하면 접근할 수 없어서 에러가 난다.
따라서 CSR을 마친 후, window객체가 있을때만 동작하게 작성해야 한다.

useEffect()를 통해서 mount되면 createPortal이 작동되도록 구현하였다.

if (typeof window === "undefined") return <></>;
이 코드를 추가하여도 해결된다.

결과

이 전에서는 __next 최상위 루트 안에 포함되어있었다면,
modal-root로 빠져나와 그 안에 포함되어있는 것을 볼 수 있다.

post-custom-banner

0개의 댓글