Portals

수경, Sugyeong·2022년 2월 24일
0

React

목록 보기
4/5
post-thumbnail
post-custom-banner

1. Portals 를 사용하는 이유 🤔

작은 규모의 리액트 앱에서 작업하다보면 Portals 의 필요성이 크게 느껴지지 않을 수 있다. 하지만 점점 앱의 규모가 커지다보면 Component 의 구조가 복잡해짐에 따라 중첩 등의 문제가 발생할 수 있다.

공식 문서에 따르면 Portals 을 주로 사용하는 환경은 부모 컴포넌트에 overflow: hidden 이나 z-index 가 있는 경우지만, 다이얼로그, 호버카드 그리고 툴팁 등 시각적으로 자식 컴포넌트를 해당 컨테이너에서 튀어나오게 보여주기 위해서 필요하기도 하다.

유저 이름 및 나이를 관리하는 미니 프로젝트를 작업 중에 있다. 유효성 검사를 통해 조건에 맞지 않는 값을 입력하거나 아예 입력하지 않을 시 Error Modal 이 나타나게 만들어져있다.

여기서 이 Modal 이 예로 들기 딱 좋은 형태이다! 렌더링 되고 있는 실제 DOM을 살펴보자면 Error Modal 이 트리거 될 때 root div 내에서 Backdrop, Card, Modal Overlay 등과 관련된 Modal div 들이 form 을 감싸고 있는 Card 요소 옆에서 렌더링 된다.


<Modal 트리거 전>


<Modal 트리거 후>


하지만 만약 해당 Modal 이 실행되는 Component 가 앱의 최상단과 가깝지 않고 다른 Component 와 깊게 중첩되어 있다면 BackdropModal div 또한 DOM 내의 다른 콘텐츠와 깊게 중첩될 수 있다.

이를 해소하기 위해서는 아래의 이미지와 같이 ModalBackdrop 이 실행되는 위치를 body 바로 아래로 위치시켜서 body 의 직접적인 자식으로 만들고 나머지 앱을 포함하는 root div 옆에 배치하면 된다.


ModalBackdrop 생성 위치 변경

Portals 을 통해서 위와 같이 만들 수 있다.


2. Portals 사용하는 방법

Portals 을 사용하기 위해서는 2가지를 기억 해야한다.

1. Component 를 이식할 위치가 필요하다.

2. 해당 위치에 Portal 이 필요함을 Component 에 알려주어야 한다.


1) public/index.html 로 이동


2) body 내부에 <div> 요소를 작성하고 id 를 추가 (이식 해야하는 다양한 Component 에 대한 여러 root 들을 만들 수 있음)

  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <!-- 이곳에 이식 해야하는 다양한 Component 에 대한 여러 root 들을 만들 수 있다. -->
    <div id="backdrop-root"></div>
    <div id="overlay-root"></div>
    <div id="root"></div>
  </body>

3) src/ErrorModal.js 에서 BackdropModalOverlay 각각의 Componenet 를 생성
(한 파일에서 2개의 Component 를 생성하였는데 그 이유는 2가지 기능이 함께 사용 중이며, Portal 로 다루기 간편하기 때문)

import Card from "./Card";
import Button from "./Button";
import classes from "./ErrorModal.module.css";

const Backdrop = (props) => {
  return <div className={classes.backdrop} onClick={props.onConfirm} />;
};

const ModalOverlay = (props) => {
  return (
    <Card className={classes.modal}>
      <header className={classes.header}>
        <h2>{props.title}</h2>
      </header>
      <div className={classes.content}>
        <p>{props.message}</p>
      </div>
      <footer className={classes.actions}>
        <Button onClick={props.onConfirm}>Okay</Button>
      </footer>
    </Card>
  );
};

4) 동일한 src/ErrorModal.js 하단에 실제 다른 파일로 import 되어 사용하는 Component 인 ErrorModal 도 생성

import React from "react";
import ReactDOM from "react-dom";

***
// 3) 번 코드
***

const ErrorModal = (props) => {
  return (
    <React.Fragment>
      {ReactDOM.createPortal(
        <Backdrop onConfirm={props.onConfirm} />,
        document.getElementById("backdrop-root")
      )}
      {ReactDOM.createPortal(
        <ModalOverlay
          title={props.title}
          message={props.message}
          onConfirm={props.onConfirm}
        />,
        document.getElementById("overlay-root")
      )}
    </React.Fragment>
  );
};

export default ErrorModal;

🤔   react-dom 을 import 한 이유

  • react-dom 은 React 를 사용해 로직과 각종 기능들을 웹 브라우저로 가져오고, DOM 과 호환되도록 해주기 때문이다.

🤔   DOM API 구조

ReactDOM.createPortal(
  <Backdrop onConfirm={props.onConfirm} />,
  document.getElementById("backdrop-root")
)}
  • 첫 번째 인수(<Backdrop onConfirm={props.onConfirm} />) 는 렌더링 되어야 하는 리액트 노드
  • 두 번째 인수(document.getElementById("backdrop-root")) 는 요소들이 렌더링 되어야 하는 실제 DOM 안의 컨테이너를 가리키는 포인터

위의 과정을 거쳐서 Portals 설정을 완료해주면 아래 이미지와 같이 <div id="root> 요소 내에서 BackdropModalOverlay 가 실행 되는 것이 아닌 <body> 하위에서 <div id="root> 와 동일한 선상에서 실행이 되는 것을 확인할 수 있다.


Backdrop


ModalOverlay


3. 결론

렌더링 하고자 하는 컴포넌트를 다른 위치로 옮기는 것이 Portals 의 핵심이라고 할 수 있다. 이 때 createPortal 과 같은 DOM API 를 사용하여 이동시켜 렌더링 할 수 있다. 해당 앱 외부가 아니라 앱 내부에서도 원하는 곳에 렌더링 하고 싶을 때 사용할 수 있다.

DOM 트리 어느 곳에서도 위치할 수 있으나 마치 일반적으로 실행되는 자식 컴포넌트처럼 작용한다.


Udemy 강의 (React 완벽 가이드)
React 공식 문서
velog@velopert

post-custom-banner

0개의 댓글