[๐Ÿ•น๏ธ ํ† ์ด ํ”„๋กœ์ ํŠธ] create portal๋ฅผ ํ†ตํ•œ modal ์ฝ”๋“œ ๊ฐœ์„ ํ•ด ๋ณด๊ธฐ

JEยท2025๋…„ 1์›” 12์ผ
1

์‹œ์ž‘

๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋กœ ์ง„ํ–‰ํ•œ ํฌ์ผ“๋ชฌ ๋„๊ฐ ์ค‘ modal ์š”์†Œ์— ๋ฌธ์ œ์ ์„ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

๋ฌธ์ œ์ 

ํ•ด๋‹น ์Šคํฌ๋ฆฐ์ƒท ์ฒ˜๋Ÿผ modal์ด ํ•ด๋‹น ์š”์†Œ ๋‚ด๋ถ€์— ์กด์žฌํ•˜๊ณ  ์žˆ๋‹ค.
๋ฌธ์ œ๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์ด๋Ÿด ๊ฒฝ์šฐ ๋ถ€๋ชจ ์š”์†Œ์— ๋”ฐ๋ผ ์˜ํ–ฅ์„ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๋ฐฉํ–ฅ์€ modal ๋“ฑ์˜ ์š”์†Œ๋Š” ์ตœ์ƒ์œ„์— ์œ„์น˜ํ•˜๋Š”๊ฒŒ ๋งž๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.
body์š”์†Œ ์ตœ์ƒ๋‹จ ๋˜๋Š” ์ตœํ•˜๋‹จ์— ๋ฐฐ์น˜ํ•˜๊ฑฐ๋‚˜ root์— ๋ฐฐ์น˜ํ•˜๊ณ  ์‹ถ๋‹ค.

๊ทธ๋Ÿฌ๋‹ค ์•Œ๊ฒŒ ๋œ๊ฒŒ create portal์ด๋‹ค.

ํ•ด๋‹น ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด modal์„ ๋ฆฌํŒฉํ† ๋งํ•  ์˜ˆ์ •์ด๋‹ค.
react create portal sample

createPortal?

์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•  ์œ„์น˜๋ฅผ DOM ํŠธ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.
์ฃผ๋กœ Modal, Tooltip, BottomSheet ๋“ฑ ํŠน์ˆ˜ํ•œ ์š”์†Œ์— ์‚ฌ์šฉํ•œ๋‹ค.

createPortal(children, domNode, key?)
  • children
    : React๋กœ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ฒƒ, JSX, Fragment, ๋ฌธ์ž์—ด์ด๋‚˜ ์ˆซ์ž ๋˜๋Š” ์ด๋Ÿฌํ•œ ์š”์†Œ๋“ค์˜ ๋ฐฐ์—ด (ex. <div />, <Component /> ๋“ฑ)

  • domNode
    : DOM ๋…ธ๋“œ๊ฐ€ ์žˆ์–ด์•ผ ํ•  ์œ„์น˜ (ex. document.body ๋“ฑ)

  • key(์„ ํƒ)
    : portal ํ‚ค๋กœ ์‚ฌ์šฉ๋  ๊ณ ์œ ํ•œ ๋ฌธ์ž์—ด์ด๋‚˜ ์ˆซ์ž

์šฉ๋ฒ•

  • DOM์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์— ๋ Œ๋”๋ง
  • Portal์„ ์‚ฌ์šฉํ•ด ๋ชจ๋‹ฌ ๋Œ€ํ™” ์ƒ์ž ๋ Œ๋”๋ง
  • React ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ React๊ฐ€ ์•„๋‹Œ DOM ๋…ธ๋“œ๋กœ ๋ Œ๋”๋ง
    createPortal - react docs

๋ฆฌํŒฉํ† ๋งํ•˜๊ธฐ

1. rootPortal ์ถ”๊ฐ€ํ•˜๊ธฐ

root ์ƒ๋‹จ์— rootPortal์„ ์ถ”๊ฐ€ํ•ด ์คฌ๋‹ค.

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/nav_logo.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>PokeDex</title>
  </head>
  <body>
    <div id="rootPortal"></div>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

2. Portal ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ

portal์„ modal ๋ง๊ณ ๋„ ๋‹ค๋ฅธ ์š”์†Œ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก
๋…๋ฆฝ์ ์ธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ƒ์„ฑํ•ด ์คฌ๋‹ค.

import { ReactNode } from 'react';
import { createPortal } from 'react-dom';

const Portal = ({ children }: { children: ReactNode }) => {
  const rootPortal = document.getElementById('rootPortal') as HTMLElement;

  return createPortal(children, rootPortal);
};

export default Portal;

3. Modal ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ

์ด์ „ ์ฝ”๋“œ์—์„  Modal ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์ง€ ์•Š์•˜์ง€๋งŒ
์ด๋ฒˆ์—” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถ„๋ฆฌํ•ด ์ž‘์—…ํ•ด ์คฌ๋‹ค.

๋’ค์— ๋ฐฐ๊ฒฝ์ด ํ•„์ˆ˜์ ์œผ๋กœ ๋“ค์–ด๊ฐ€๊ณ 
ํด๋ฆญ ์‹œ ๋‹ซํ˜€์•ผํ•˜๊ธฐ์— DropBox์— actionFn์ด๋ผ๋Š” prop์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

import styled from 'styled-components';
import DropBox from '../box/DropBox';
import Portal from '../portal';
import { ReactNode } from 'react';

const Modal = ({ children, actionFn }: { children: ReactNode; actionFn: () => void }) => {
  return (
    <Portal>
      <ModalContainer>
        <ModalWrapper>{children}</ModalWrapper>
        <DropBox actionFn={actionFn} />
      </ModalContainer>
    </Portal>
  );
};

export default Modal;

4. Modal ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€ํ•˜๊ธฐ

๋ชจ๋‹ฌ๋กœ ํ‘œ์‹œ๋˜๊ธธ ์›ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ์ฃผ๋ฉด ๋

 {modal && nameId === name && (
   <Modal actionFn={() => dispatch(overlayMadalActions.toggleModal())}>
     <PokemonDetail name={name} />
   </Modal>
 )}

์นด๋“œ ์ปดํฌ๋„ŒํŠธ ํด๋ฆญ ์‹œ rootPortal๋กœ ์š”์†Œ๊ฐ€ ์ƒ์„ฑ๋˜๋Š”๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


๋งˆ์น˜๋ฉฐ

createPortal์— ๋Œ€ํ•ด ๊ธ€๋กœ๋งŒ ๋Œ€๋žต ์•Œ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ ์ด๋ ‡๊ฒŒ ์ ์šฉ ํ•  ์ˆ˜ ์žˆ๋˜ ๊ธฐํšŒ์˜€๋‹ค.

์ด์ „์—” ํ•ญ์ƒ ํ•ด๋‹น ์š”์†Œ ๊ทผ์ฒ˜์— ํ‘œ์‹œ ๋˜๋„๋ก ํ–ˆ๋Š”๋ฐ
ํ•™์Šตํ–ˆ๋˜ ๊ธฐ์–ต๊ณผ ์ง€๊ธˆ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋Š” ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด
createPortal๋กœ modal, tooltip ๋“ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฑธ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.
(๋„ˆ๋ฌด ๋Šฆ๊ฒŒ ์•Œ์•„ ๋ฌธ์ œ...)

์ง€๊ธˆ์€ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๋ฐฉ์‹๋งŒ ์ตํžˆ๋Š” ์ •๋„๋กœ
ํ† ์ด ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋ง ํ–ˆ๋‹ค.

modal ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด์„œ ์˜ˆ์ „์˜ ๋‚ด๊ฐ€ ์‹ค์ˆ˜ํ•œ ์ฝ”๋“œ๋„ ๊ฐ™์ด ์ˆ˜์ • ํ•  ์ˆ˜ ์žˆ๋˜ ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ๋‹ค.

๋‚˜์ค‘์—” index.html์— div์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์ด ์•„๋‹Œ
๋™์ ์œผ๋กœ ์ƒ์„ฑ๋˜๊ณ  ์ œ๊ฑฐ ๋˜๋„๋ก ๋ฆฌํŒฉํ† ๋ง ํ•  ์˜ˆ์ •์ด๋‹ค.

๋˜ํ•œ id ๊ฐ’๋„ ์—ญํ• ์— ๋งž๊ฒŒ ๋ณ€๊ฒฝ๋˜๋„๋ก ํ•  ์˜ˆ์ •์ด๋‹ค. (์ง€๊ธˆ์€.. ์ง‘์ค‘๋ ฅ ํ•˜๋ฝ..)

๋ฆฌํŒฉํ† ๋งํ•  ๊ฒฝ์šฐ ํ•ด๋‹น ๋‚ด์šฉ๋„ ์ˆ˜์ • ์˜ˆ์ •

+) ๊ฐ„๋งŒ์— ํ† ์ด ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ๋ฅผ ๋ณด๋‹ˆ ๋ฆฌํŒฉํ† ๋งํ•  ๋ถ€๋ถ„์ด ๋งŽ์•˜๋‹ค ๋‹ค์‹œ ์‹œ๊ฐ„ ๋  ๋•Œ ํ‹ˆํ‹ˆ์ด ์ˆ˜์ •ํ•ด ๋ณด์ž.

profile
[ํ”„๋ก ํŠธ ์• ์†ก์ด] ์ž‘์€ ๊นจ๋‹ฌ์Œ๋„ ๊ธฐ๋กํ•˜๊ธฐ

0๊ฐœ์˜ ๋Œ“๊ธ€