티스토리 뷰

TMI. 자꾸 날리는 바람에 이 주제 관련 포스팅만 몇 번째 쓰는지 모르겠다 ㅠㅠ


리액트 공식 홈페이지에서는 create Potals를 다음과 같이 설명하고 있다.

Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.

Potals는 부모 컴포넌트의 DOM 계층 구조의 밖에 존재하는 DOM 노드로 자식을 렌더링하는 최고의 방법을 제공한다는 의미이다.

이게 무슨 의미일까? 내 식대로 해석해보자면 다음과 같다. 게임을 많이 하는 편은 아니지만 RPG게임에서 미션을 받으면 [ㅇㅇ으로 이동하세요. 포털을 이용하면 더욱 빠릅니다]와 같은 식의 미션을 받는다. 빠르게 순간이동을 하여 미션을 수행할 수 있는 기능이 포탈인데, 리액트 포탈도 크게 다르지 않다.

보통 자식 컴포넌트는 부모 컴포넌트 하위에 속하여 컨트롤 당하는데, 포털 기능을 사용하면 꼭 부모-자식의 종속 관계가 아니더라도 쉽게 컨트롤 할 수 있다는 의미이다.

 

개인적으로 상단 설명 다 필요없다. 직접 코드로 구현하면서 이해하는게 최고다.

 

1. 먼저 root 마크업과 같은 레벨로 modal 마크업의 위치를 잡아준다. root가 위치한 마크업과 같은 레벨로 modal을 만다는 것은 부모-자식의 종속관계가 아님을 나타내기 위함이며, 보통 potals의 예시로 modal을 많이 사용해서 모달을 통한 예시를 진행했다.

앞으로 나오는 코드는 CodeSandBox의 틀을 기준으로 진행된다!
(참고 : 리액트 온라인 코드 편집기 모음)
// index.html

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

 

2. modalPotals 컴포넌트를 만들어준다. Potals 기본 사용 방법은 다음과 같다.

ReactDOM.createPortal(child, container)
// modalPotal.jsx

import ReactDom from "react-dom";

const ModalPotal = ({ children }) => {
  const el = document.getElementById("modal");
  return ReactDom.createPortal(children, el);
};

export default ModalPotal;

el을 찾는 방법은 getElementById도 있을 것이고, querySelector도 있을것이고, 그 외 방법들이 있을텐데 개인적으로는 getElementById를 추천한다. 여러 노드들을 찾아보는 것 보다 id가 지정된 노드들 중에서 id만 확인하기 때문에 시간을 많이 잡아먹지 않기 때문이다.

3. 상단까지는 움직일 수 있는 문, 포탈을 만들었으니 이제 그 사이를 왔다갔다 할 modal을 만들 차례이다. (사실 만드는 순서는 상관없다. 더불어 나는 포스팅의 예시를 위해 간단하게 스타일도 함께 적용하였다.)

// modal.jsx

import React from "react";
import "./styles.css";

const Modal = () => {
  return (
    <>
      <div className="dim"></div>
      <div className="modal">
        <div className="modal-content">
          모달 관련 내용이 들어가는 곳입니다.<br />예를들어 확인 버튼을 눌러주세요.
        </div>
        <button className="modal-confirm">확인</button>
      </div>
    </>
  );
};

export default Modal;

modal 컴포넌트를 app에서 import하여 확인하여준다. 어설프지만 제법 모달과 같이 보이는 창을 띄웠다.

// App.js

import "./styles.css";
import Modal from "./modal";

export default function App() {
  return (
    <div className="App">
      <Modal />
    </div>
  );
}

여기서 잠시 확인해야할 점은 element의 위치인데, 나는 클래스 명이 app인 div의 하위에서 modal을 불러왔기 때문에 다음과 같은 마크업이 구성되어진다. id가 modal인 div하위는 비어있음을 확인할 수 있다.

 

4. App.js에서 불러왔던 modal 컴포넌트를 modalPotal 컴포넌트로 한 번 감싸주자-

// App.js

import "./styles.css";
import Modal from "./modal";
import ModalPotal from './modalPotal'

export default function App() {
  return (
    <div className="App">
      <ModalPotal>
        <Modal />
      </ModalPotal>
    </div>
  );
}

ModalPotal로 감싸주었을 뿐인데, 신기한 일이 발생한다. modal 마크업이 App 하위가 아닌 modal 하위로 움직인 것이다-! 이게 바로 potal을 통한 이동이다.

굳이 포탈을 통하여 이동하는 이유는

스타일에서의 자유-

이다. modal 혹은 toast message 등과 같은 컴포넌트들은 부모 컴포넌트 스타일의 영향을 많이 받는데, 이때 가장 골치아픈 부분이 바로  레이어의 위치를 결정하는 z-index 이다. 포털을 이용하게 되면 부모-자식의 관계에 얽매이지 않고, 자유롭게 스타일을 조절할 수 있다.

5. 이제 모달에 기능을 달아볼 차례이다. 모달의 열기/닫기를 구혀하여 버튼에 달아준다. 이는 보통 구현하는 모달과 비슷하게 진행하면 된다.

나는 useState를 통하여 showModal을 컨트롤 하였고, showModal이 true일 때 모달 오픈을 showModal이 false일 때 모달 클로즈를 하였다.

// App.js

import "./styles.css";
import Modal from "./modal";
import ModalPotal from './modalPotal'
import { useState } from "react";

export default function App() {
  const [showModal, setShowModal] = useState(false);
  return (
    <div className="App">
      <button onClick={() => setShowModal(true)}>모달열기</button>
      {
        showModal && 
          <ModalPotal>
            <Modal closeModal={() => setShowModal(false)}/>
          </ModalPotal>
      }
    </div>
  );
}

모달 닫기는 모달 컴포넌트 내에 있는 버튼을 클릭 시, 가능한 기능으로 props로 해당 함수를 내려주었다.

// modal.jsx

import React from "react";
import "./styles.css";

const Modal = (props) => {
  return (
    <>
      <div className="dim"></div>
      <div className="modal">
        <div className="modal-content">
          모달 관련 내용이 들어가는 곳입니다.<br />예를들어 확인 버튼을 눌러주세요.
        </div>
        <button className="modal-confirm" onClick={props.closeModal}>확인</button>
      </div>
    </>
  );
};

export default Modal;

 


create.Potal 기능을 사용하게 되면 원하는 DOM에 특정 컴포넌트를 랜더링 시킬 수 있다. 나는 기본적인 동작을 보기 위하여 root와 동일한 레벨에 마크업을 넣어두고, App에서 potal을 사용하였는지만 필요한 부분에 마크업을 적어두고 아무 컴포넌트에서나 potal을 불러오면 해당 위치에 랜더링 되게 된다.

현재까지 이해하는 가장 이점은 상단에서 언급한 것 처럼 스타일에서의 자유이다. 이 외에 장점을 더 발견한다면 포스팅에 추가해야지 룰루

더불어 나는 예시로 모달을 보여줬지만 실제로는 모달 뿐 만 아니라 토스트 메세지나 기타 등등 최상단에 떠야할 어떤것이라면 (혹은 최상단이 아니더라도) 자유롭게 활용하면 될 것 같다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
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
글 보관함