React에서 Modal 구현하기(feat. createPortal, 스크롤 막기)

JeongHoon Park·2021년 8월 29일
23

For React👍

목록 보기
6/6
post-thumbnail

1. Modal

Modal(이하 모달)은 사용자의 이목을 끌기 위해 사용하는 화면전환 기법 중에 하나다. 이번 프로젝트에서는 사용자가 어떤 행동을 하였을 때 이 행동을 정말로 진행할 것인지 확인 하는 용도로 사용을 하기도 하였고, 로그인이 필요한 기능을 수행하려 할 때 로그인 창을 모달로 띄워서 페이지 이동 없이 로그인을 진행하도록 수정하려고한다.
모달은 모달 컴포넌트를 만든 후 필요한 컴포넌트에서 불러오면 된다. 하지만 그냥 불러온다면 모달 컴포넌트가 보이기로는 맨앞에 보이지만 컴포넌트 트리 속 어딘가에 위치하게 되서 직관적이지 않게된다.

그래서 react-domcreatePortal을 사용해서 기본의 컴포넌트 트리에서 벗어나게 직관적으로 모달을 구현해보려고 한다.


2. 구현 코드

<!--public/index.html-->
<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <div id="modal"></div> // <- 코드 추가
  </body>

우선 리액트 프로젝트 파일 내부의 public/index.html파일에 root 밑에 <div id="modal"></div> 컨테이너를 추가한다.

//Modal.jsx
import React from 'react';
import { createPortal } from 'react-dom';
import styled from 'styled-components';

const ModalBg = styled.div`
  display: flex;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  align-items: center;
  justify-content: center;
  width: 100vw;
  height: 100vh;
  background-color: #ffffffe2;
`;

const ModalBox = styled.div`
  width: 25rem;
  background-color: white;
`;

const Modal = ({ setModalShow }) => {
  const handleOk = () => {
    console.log('댓글 삭제');
  };
  return createPortal(
    <ModalBg>
      <ModalBox>
        <ModalTitle>댓글 삭제</ModalTitle>
        <ModalContent>댓글을 정말로 삭제하시겠습니까?</ModalContent>
        <ModalBtnBox>
          <ModalBtn cancel onClick={() => setModalShow(false)}>
            취소
          </ModalBtn>
          <ModalBtn onClick={() => handleOk()}>확인</ModalBtn>
        </ModalBtnBox>
      </ModalBox>
    </ModalBg>,
    document.getElementById('modal')
  );
};

export default Modal;

그리고 Modal 컴포넌트의 반환 값을createPortal로 감싸주고 원하는 element를 지정해주면 된다.
(위 코드에서 모달생성에 필수적이지 않다고 생각한 css 코드는 삭제했습니다.)

실행해보면 시각적으로는 똑같지만 html element를 보면 모달이 기존의 컴포넌트 트리에서 빠져나온 것을 확인할 수 있다.


3. Modal 배경 스크롤 막기

모달도 잘 만들었는데 뒤에 배경이 스크롤이 된다면 완성도가 많이 떨어져 보일 것이다. 그래서 모달의 글의 마지막은 스크롤을 막는 방법이다.

3-1. 구현 방법

body 태그의 css를 position을 fixed로 변경하고, top을 현재 스크롤 위치로 하고 overflow-y: scroll; width: 100%;을 추가하면 스크롤 방지를 할 수 있다.

해당 css를 변경할 때는 useEffect를 사용할 것이다. 모달이 사라질 때에는 useEffect의 return을 사용해 body의 cssText를 리셋시킨 다음 window.scrollTo를 이용해 현재 스크롤 위치로 이동시키면 된다.

3-2. 구현 코드

useEffect(() => {
  document.body.style.cssText = `
    position: fixed; 
    top: -${window.scrollY}px;
    overflow-y: scroll;
    width: 100%;`;
  return () => {
    const scrollY = document.body.style.top;
    document.body.style.cssText = '';
    window.scrollTo(0, parseInt(scrollY || '0', 10) * -1);
  };
}, []);

4. 마무리

프로그래밍을 잘 모르는 사람이 볼 때는 모달을 컴포넌트 트리에서 빼든 안 빼든 css적으로 동일하다면 상관없을 것이다. 하지만 이런 사소한 부분 하나 하나 모여 코드의 완성도와 깔끔함이 결정된다고 믿기에 이 방법이 좋은 프로그래밍이라고 생각한다.

💡직접 읽어보면 뼈가 되고 살이 되는 출처
👉https://codingbroker.tistory.com/126
👉http://yoonbumtae.com/?p=3776

profile
Develop myself, FE developer!

0개의 댓글