티스토리 뷰

Vue&React

[React] Modal with Portal

U_pic 2022. 5. 10. 00:59

프런트엔드 프로젝트를 진행하면서 모달을 사용할 때가 너무나도 많다.

 

Modal의 사용성에 대해서 알아보던 중, Portal이라는 개념을 사용해서 모달을 사용하는 경우를 알게 되었다.

 

그래서 Portal이 어떤 것이고, 실제로 Portal을 활용해서 Modal까지 만들어 보았다.

 

Portal

Portal을 들었을 때, 처음 드는 생각은 약간 순간이동 이런 건가? 하는 생각이 들었다.

React 공식 문서에 따르면 Portal이란 상위 DOM 계층 외부에 존재하는 DOM 노드로 자식 컴포넌트를 랜더링 시키는 방법이라고 정의되어있다.

ReactDOM.createPortal(child, container)

첫 번째 인자로는 Child (자식 컴포넌트) 요소가 들어가고, 두 번째 인자로는 자식 컴포넌트가 담길 상위 외부 DOM 요소가 담긴다.

 

Portal을 사용하는 이유

Portal을 사용하면 어떤 이점이 있을까.

일반적으로 우리가 Modal을 제작하는 방법은 모달의 on/off 상태를 관리하고, 그에 따라서 현재 컴포넌트 위에 모달이 화면을 덮게 되는 형식으로 제작한다.

 

같은 tree에 위치하게 되기 때문에 모달이 화면을 덮고 있더라도 다른 컴포넌트의 영향을 받게 된다.

뿐만 아니라  모달의 상위 컴포넌트까지 모두 덮는 스타일링이 필요하기 때문에 z-index 부분에서도 신경을 써야 한다.

 

정리하자면 Portal을 사용하면 우리가 원하는 곳에서 컴포넌트가 랜더링 될 수 있기 때문에, 현재 컴포넌트와 다른 트리에 컴포넌트를 등장시킴으로써 서로 영향을 최대한 받지 않도록 하기 위해서 사용한다.

 

실습

공식문서의 용법으로 실제 Portal을 통해 modal을 만들어보자

먼저 4가지 file에 코드라인을 작성해 줄 것이다.

index.html

ModalWithPortal.tsx

Potal.tsx

Modal.tsx

 

index.html

index, html에서는 모달을 띄워줄 상위 DOM 요소의 위치를 미리 결정해 둔다.

<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <div id="portal"></div>
</body>

기존 컴포넌트들이 위치할 root 이외에 portal이라는 공간을 지정해준다.

 

Modal.tsx

모달 컴포넌트는 기존에 만들던 대로 제작해 주었다.

import React from 'react';

//import styled-component code
import * as S from './style';

interface Props {
  onClose: () => void;
}

function Modal({ onClose }: Props) {
  return (
    <S.ModalBackground>
      <S.ModalWrapper>
        <p>Modal is Open!!!</p>
        <button onClick={() => onClose()}>OK</button>
      </S.ModalWrapper>
    </S.ModalBackground>
  );
}

export default Modal;

 

Portal.tsx

import { createPortal } from 'react-dom';

interface Props {
  isOpen: boolean;
}

const MordalPortal: React.FC<Props> = ({ isOpen, children }) => {
  if (!isOpen) return null;

  return createPortal(
    <div>
      <div>{children}</div>
    </div>,
    document.querySelector('#portal') as Element,
  );
};

export default MordalPortal;

위에서 설명한 용법에 따라서 자식 요소(첫 번째 인자)를 index.html에서 작성해놓은 portal에서(2번째 인자) 랜더링 되도록 한다.

 

ModalWithPortal.tsx

import React, { useState } from 'react';

import * as S from './style';
import ModalPortal from './Portal';
import Modal from './Modal';

function ModalWithPortal() {
  const [modalIsShow, setModalIsShow] = useState<boolean>(false);

  const modalControl = () => {
    setModalIsShow(!modalIsShow);
  };

  return (
    <S.PortalWrapper>
      <ModalPortal isOpen={modalIsShow}>
        <Modal onClose={modalControl} />
      </ModalPortal>
      <S.ModalButton onClick={modalControl}>Modal On</S.ModalButton>
    </S.PortalWrapper>
  );
}

export default ModalWithPortal;

마지막으로 모달을 사용하는 곳에서 앞서 선언한 Portal 컴포넌트로 Modal을 감싸줌으로써 Portal의 첫 번째 인자인 자식 요소가 Modal을 향하게끔 하면 된다.

 

위와 같이 코드를 작성하면 모달을 클릭했을 때 다음과 같이 결과를 확인할 수 있다.

 

#Reference

 

React | Portal을 이용한 Modal 구현

한국인에게 portal이란.. 닥터스트레인지의 마법진같은 포탈만 떠오를 수 있다. 이미 충분히 익숙한 다른 곳으로 이동시킨다는 개념을 이어받아, 컴포넌트를 부모 컴포넌트의 바깥에 렌더링해

dev-bomdong.tistory.com

 

[React] Portal (포탈), Modal 구현하기

회사에서 선배와 React로 모달 구현에 대해 고민하다가 알게된 내용이었다. 우리 회사 프로젝트는 Vue를 사용하기 때문에, 플러그인(this.$modal) 메서드를 통해 루트에 모달 컴포넌트를 주입, 삭제해

abangpa1ace.tistory.com

 

'Vue&React' 카테고리의 다른 글

[React] React.FC<>  (0) 2022.07.11
[React] Redux-Thunk로 비동기로직 처리하기  (0) 2022.05.16
Context API  (0) 2022.04.27
Vuex - (2)  (0) 2022.04.26
Vuex - (1)  (0) 2022.04.26
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함