[React] React에서 모달창 구현하기

Bam·2023년 5월 18일
4

React

목록 보기
37/40
post-thumbnail
post-custom-banner

예전에 CSS와 자바스크립트를 이용해서 모달창을 구현했습니다. 마찬가지로 리액트로도 모달창을 구현할 수 있는데요. 바닐라 자바스크립트와는 조금 다르기에 방법을 소개해보려고 합니다.


기존 방식에서의 문제점

제가 소개드린 방식으로도 리액트 환경에서 모달을 구현할 수 있습니다.

classList 속성을 통해서 DOM요소에 직접 접근하여 class를 수정함으로써 모달창을 구현했었는데요. 리액트에서는 직접적으로 DOM요소를 조작하는 것을 지양하고 있습니다.

리액트는 Virtual DOM을 통해, 미리 렌더링을 하게 됩니다. 이 과정에서 필요한 부분만 리렌더링해서 효율성을 챙겼는데요. 우리가 리액트에서 DOM 요소를 직접 조작한다는 것은 리액트의 효율성을 해치는 일을 한다는 것 입니다.

따라서 우리는 리액트에서 직접 DOM 조작을 피하기 위해 모달창을 조금 다른 방식으로 구현해보려고 합니다.


리액트에서 모달창 구현하기

이번 모달은 버튼을 누르면 닫히는 기능과 모달 영역 외부를 클릭하면 닫히는 기능 두 가지를 모두 구현했습니다.

전체 코드

const App = () => {
  const [modalOpen, setModalOpen] = useState(false);
  const modalBackground = useRef();

  return (
    <>
      <div className={'btn-wrapper'}>
        <button className={'modal-open-btn'} onClick={() => setModalOpen(true)}>
          모달 열기
        </button>
      </div>
      {
        modalOpen &&
        <div className={'modal-container'} ref={modalBackground} onClick={e => {
          if (e.target === modalBackground.current) {
            setModalOpen(false);
          }
        }}>
          <div className={'modal-content'}>
            <p>리액트로 모달 구현하기</p>
            <button className={'modal-close-btn'} onClick={() => setModalOpen(false)}>
              모달 닫기
            </button>
          </div>
        </div>
      }
    </>
  );
};

export default App;

코드 살펴보기

state와 ref 선언

const [modalOpen, setModalOpen] = useState(false);
const modalBackground = useRef();
  • modalOpen은 모달창을 표지할지에 대한 true/false값을 담은 state입니다.
  • modalBackground는 모달의 바깥(여기서는 콘텐츠 바깥의 <div>)을 클릭했을 때 닫힐 수 있도록 하기 위해 선언한 ref입니다. 이 ref를 바깥이 되는 영역에 전달하면 DOM을 조작할 수 있게 됩니다.

return 구문

  return (
    <>
      <div className={'btn-wrapper'}>
        <button className={'modal-open-btn'} onClick={() => setModalOpen(true)}>
          모달 열기
        </button>
      </div>
      {
        modalOpen &&
        <div className={'modal-container'} ref={modalBackground} onClick={e => {
          if (e.target === modalBackground.current) {
            setModalOpen(false);
          }
        }}>
          <div className={'modal-content'}>
            <p>리액트로 모달 구현하기</p>
            <button className={'modal-close-btn'} onClick={() => setModalOpen(false)}>
              모달 닫기
            </button>
          </div>
        </div>
      }
    </>
  );
        <button className={'modal-open-btn'} onClick={() => setModalOpen(true)}>
          모달 열기
        </button>
  • 버튼을 누르면 모달창이 열립니다. 이때 onClick이벤트로 state를 조작해 모달이 열리도록 합니다.
      {
        modalOpen &&
        <div className={'modal-container'} ref={modalBackground} onClick={e => {
          if (e.target === modalBackground![](https://velog.velcdn.com/images/bami/post/c49c2a78-1889-4079-b005-b8246129294e/image.gif)
.current) {
            setModalOpen(false);
          }
        }}>
          <div className={'modal-content'}>
            <p>리액트로 모달 구현하기</p>
            <button className={'modal-close-btn'} onClick={() => setModalOpen(false)}>
              모달 닫기
            </button>
          </div>
        </div>
      }
  • 모달 구현에는 조건부 렌더링을 이용합니다. modalOpen state가 true이면 모달창이 보여지게 됩니다.
  • 콘텐츠 외부를 클릭하면 닫히도록 <div className={'modal-container}>에 ref를 전달했습니다. 이때 onClick이벤트를 통해 이벤트가 ref로 지정된 영역에서 발생하면 modalOpen state를 false로 만들어서 모달이 안보이도록 만들어줍니다.
  • 콘텐츠에도 버튼을 삽입해서 onClick이벤트를 통해 버튼을 누르면 modalOpen을 false로 만들어 모달이 안보이도록 만들었습니다.

CSS

이번에는 리액트로 구현하는 게 목적이므로, css에 대한 설명은 이전 포스트를 참조해주시기 바랍니다.

.btn-wrapper {
  display: flex;
  justify-content: center;
  margin-top: 5rem;
}

.modal-open-button, .modal-close-btn {
  cursor: pointer;
  margin-left: auto;
}

.modal-container {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, 0.5);
}

.modal-content {
  background-color: #ffffff;
  width: 250px;
  height: 150px;
  padding: 15px;
}

post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 11월 15일

도움받았습니다! 감사합니다.

답글 달기