관리 메뉴

CASSIE'S BLOG

슈퍼코딩 47강 리액트 심화 Portal 본문

PROGRAMMING/슈퍼코딩 강의 정리

슈퍼코딩 47강 리액트 심화 Portal

ITSCASSIE1107 2023. 11. 11. 11:07

모달과 같은 Overlay 컴포넌트는 전체 HTML에서 동작하게 해야한다.
컴포넌트의 위치를 옮겨줄 때는 React Portal를 사용한다.

 

 

React Portal - 특정 컴포넌트를 루트 DOM 요소 외부에 렌더링가능하게한다, 

"Overlay 컴포넌트"는 일반적으로 모달 창 또는 다른 컴포넌트를 나타내는 데 사용되는 부모 컨테이너입니다. 이 컴포넌트는 전체 HTML 문서에 대해 위치하며, 모달 창이나 팝업과 같은 UI 요소를 나타내는 데 사용됩니다.

"React Portal"은 React 애플리케이션에서 컴포넌트를 렌더링하는 데 사용되는 기술 중 하나입니다. React Portal을 사용하면 특정 컴포넌트를 루트 DOM 요소 외부에 렌더링할 수 있습니다. 이는 React 트리에서 독립적으로 렌더링되어야 하는 경우에 유용합니다.

모달 창이나 팝업과 같은 Overlay 컴포넌트를 전체 HTML 문서에서 동작하게 만들 때, React Portal을 사용하여 이를 구현할 수 있습니다. 이렇게 하면 모달이나 팝업이 컴포넌트 트리의 어디에서든 렌더링되지 않고, 독립적으로 전체 HTML 문서에 나타날 수 있습니다.

간단한 예시로, 모달을 구현하는 React Portal의 사용법은 다음과 같을 수 있습니다:

 

사용예시:

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

const Modal = ({ children }) => {
  const portalRoot = document.getElementById('portal-root');
  const el = document.createElement('div');

  React.useEffect(() => {
    portalRoot.appendChild(el);
    return () => portalRoot.removeChild(el);
  }, [el, portalRoot]);

  return ReactDOM.createPortal(children, el);
};

// 사용 예시
const App = () => {
  return (
    <div>
      <h1>Main Content</h1>
      {/* ... */}
      <Modal>
        <div>
          <h2>Modal Content</h2>
          <p>This is a modal.</p>
        </div>
      </Modal>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

 

위 예시에서 Modal 컴포넌트는 React Portal을 사용하여 새로운 div 요소를 생성하고, 이를 portal-root라는 ID를 가진 DOM 요소에 추가합니다. 모달의 내용은 이 새로운 div에 렌더링되며, 이로써 모달이 컴포넌트 트리의 어디에서든 독립적으로 나타날 수 있게 됩니다.

 


Portal 

 

어떠한 지정한 위치에 만든 컴포넌트를 html의 다른 부분으로 전환을 하려고 할 때
사용되는 "방법"이다.

 

모달은 전체 화면을 감싸는 블럭이다. 

 

즉 모달은 전체 컴포넌트에서 뒤덮일 수 있는 코드다. 

 

왼쪽 코드처럼 Modal과 Card를 동일선상에 놓는 경우가 많다.

 

 

 

 

섹션이라고 하는 전체페이지에서 일부분이 form이고 일부분이 modal이라고 되어있는데
우리가 기대하는 것은 모달은 전체화면을 다 덮어야하는 코드이다. 그래야 표준에 맞는 코드다. 

즉 모달은 따로 있고 그리고 섹션안에 form이 있는 형태로 가야한다. 

 

practice/4-2로 갈 것 -> 없어서 보니까..practice/4-3으로 가야함..휴 

 

로컬에 그 저장소가 없어서..

 

fetch 꼭 써야한다. 그러면 git 저장소에 파일형태가아닌 객체 형태로 저장이 되서 불러올 수 있음. 

 

 

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

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>
  );
};

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;

 

 

지금 모달은 root directory 안에 있다.

 

 

모달을 root 바깥으로 즉 브라우저 바깥으로 뺼건데 이떄 portal를 쓸 예정이다.

 

일단 그러면 바깥으로 div를 만들어줘야한다. <noscript> 바로 아래에
div#backdrop-root 하면 단축키
div#overlay-root  둘다 만들고 modal을 둘에게 보낼 예정 index.html에서

 

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="backdrop-root"></div>
    <div id="overlay-root"></div>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

 

 

ErrorModal.js에 가면 backdrop과 카드는 별개라고함. 

 

card는 모달안에 컨텐츠 요소에 가깝고
backdrop은 뒷단에 검정배경을 의미함. 

 

backdrop과 modaloverlay를 역할에 맞게 구분을 해주자.
코드를 수정한다는개념보다는 위치를 바꿔준다는 개념으로 알고있기 

 

before

 

 

깃허브 코드

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

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>
  );
};

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;

 

강의중에 이렇게 

backdrop과 card를 그냥 바깥으로 빼줬데

 

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

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>
  );
};

const ErrorModal = (props) => {
  return (
    <div>
      <Backdrop/>
      <ModalOverlay/>
    </div>
  );
};

export default ErrorModal;

 

 

이러면 모달창에 아무것도 안 뜨게되는데 props를 안 넘겨줘서 그렇다. 놓친 부분

 

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

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>
  );
};

const ErrorModal = (props) => {
  return (
    <div>
      <Backdrop props={props}/>
      <ModalOverlay props={props}/>
    </div>
  );
};

export default ErrorModal;

 

이렇게 해도 그대로인데, 각각의 요소를 직접 해주면 될 것 같아요 (?) 

 

각각의 컴포넌트에서 필요한 props를 다 넣어주면 된다고함. 

 

혼자 실습함

 

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

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>
  );
};

const ErrorModal = (props) => {
  return (
    <div>
      <Backdrop onConfirm={props.onConfirm}/>
      <ModalOverlay title={props.title} message={props.message} onConfirm={props.onConfirm}/>
    </div>
  );
};

export default ErrorModal;

 

잘됨.

 

 

portal를 쓰려면 새로운 라이브러리를 알아야하는데 그게 react-dom

 

react-dom에서 portal를 관리함 

 

ReactDom의 내장메소드인데 createPortal()이라는 메소드는
우리는 Backdrop이라는 컴포넌트를 옮겨주고싶잖아요
어떤걸 옮겨줄지랑 어디로 옮겨줄지를 즉 두개의 인자를 createPortal()에 넣어줘야함 

 

설명란에 children은 그 안에 컴포넌트를 의미를 하고
container는 말그대로 dom에서 어디로 옮겨줄지 즉 dom node를 의미한다. 

 

 

index.html에 id=backdrop=root에 넣을예정임.
콤마 찍고 document.getElementId('backdrop-root')
원래 모든 컴포넌트는 root안에 있는데
이걸 해주면 backdrop-root로 들어가게됨. 
같은 방법으로 ModalOverlay도 해주려면 중괄호로 다시 열어서 해주면 된다. 

 

그러면 backdrop이 root 바깥으로 빠지네

 

React-dom은 기본적으로 설치되어있는 라이브러리라서 별도로 설치안해도된다고함.

 

 

some children = 우리들의 컴포넌트를 의미한다. 

반응형