초보 개발자의 성장 일기

Modal 렌더링은 어디에 해야할까? 본문

Development/React JS

Modal 렌더링은 어디에 해야할까?

YUNA 2023. 12. 8. 06:59

모달을 만들다보면, 폼의 옆에 생성이 되는데, 이 위치는 root의 안이다. 애플리케이션이 작다면 이렇게 해도 되지만, 현재 컴포넌트가 전체 애플리케이션의 위쪽에 위치해 있지 않고 다른 컴포넌트 안에 깊이 중첩되있기도 한다. 백드롭과 모달이 DOM의 다른 내용안에 깊이 중첩되버린다.

 

브라우저는 모달의 존재를 인식하지 못하고 구조적인 관점에서 모달이 맨 위에 있어야 하는 존재인지 인식하지 못한다.이때 렌더링 된 HTML을 Body 밑으로 모달을 옮기면 되는데, 이것은 Portal로 해결이 가능하다.

 

포털을 사용할 때 필요한 것

1. 컴포넌트를 이동시킬 장소
2. 컴포넌트에게 그 곳에 포털을 가져야한다고 알려주는 것

 

Potal

공식문서로 포탈을 살펴보면 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하는 최고의 방법을 제공한다.

 

index.html

<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>
</body>

포털을 사용할 div에 id를 추가해준다. 오버레이 외에도 모달, 사이드 드로어등을 가져올 수 있다. 장소를 만들었으니 이제 컴포넌트에게 알려주기만 하면 된다.

 

모달과 함께 사용해야하기 때문에 컴포넌트는 모달과 동일 파일 내에 작성해준다.

 

const ErrorModal = (props) => {
  return (
    <React.Fragment>
      <div className={classes.backdrop} onClick={props.onConfirm} />
      <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>
    </React.Fragment>
  );
};

이 에러 모달을 두개의 컴포넌트로 나눠준다.

 

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

 

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

 

이제 함수를 사용해서 실제 DOM과 다른 위치에 포털로 보내야 한다.

 

ReactDOM.createPortal(child, container)

인자를 두개를 받을 수 있다. 첫번째는 렌더링 되어야 하는 리액트 노드(자식)이고, 두번째는 포인터다. 렌더링 돼야 하는 실제 DOM의 컨테이너를 가리키는 포인터이다. index에서 만든 컴포넌트로 렌더링 해야한다. 이 때 DOM API를 사용해야한다. 이미 렌더링 된 기존 애플리케이션 내부로 포털시킨다. 렌더링 하려는 HTML 내용을 다른 장소로 이동 시키는 것이다.

 

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

 

 

backdrop-root 아래에 만들어졌다.

 

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

ModalOver에도 같은 방식으로 만들어줬다.

 

 

이렇게하면 버튼 클릭시에 모달이 활성화 되는 것을 볼 수 있다.