• [React] React로 모달창 구현하기

    2024. 1. 2.

    by. 지은이: 김지은

    728x90

    React로 라이브러리 없이 여러곳에서 재사용할 수 있는 간단한 모달창 만들기!

     

     

    // App.jsx
    
    import { FaRegTrashAlt } from "react-icons/fa";
    import Modal from "./components/Modal";
    
    function App() {
      return (
        <>
          <button>
            <FaRegTrashAlt />
            삭제하기
          </button>
          <Modal />
        </>
      );
    }
    
    export default App;

    먼저 상위 컴포넌트에 위처럼 버튼을 만들어주고, components 폴더를 생성해서 버튼을 눌렀을 때 나오는 Modal 컴포넌트를 불러오기

     

    다음 Modal 컴포넌트에서 스타일링과 열고 닫는 기능을 작성할건데

    // Modal.jsx
    
    import { IoClose } from "react-icons/io5";
    
    const Modal = ({ isOpen, onClose, children }) => {
      return (
        <div className="modal-overlay">
          <div className="modal">
            <button className="modal-close">
              <IoClose />
            </button>
            {children}
          </div>
        </div>
      );
    };
    
    export default Modal;

    모달이 열려있는지 여부를 나타내는 isOpen, 모달을 닫는 onClose, 그리고 모달 안에 들어갈 내용을 작성할 children 이 세가지 props를 받는다.

    여기서 'children'은 컴포넌트를 재사용하기 위해서 내용을 prop으로 전달하는데 모달 컴포넌트 안에서 모든 내용을 다 작성하면

    위 처럼 삭제하기 기능이 아닌 다른 곳에서 모달 컴포넌트를 사용할 때 재사용 하기 어렵기때문에 동적으로 컨텐츠를 전달하는 역할을 한다.

     

    모달 스타일링은 보통 모달창을 띄울 때 모달창 뒷 배경을 어둡게 또는 밝게 덮고 모달창을 가운데에 위치시키기 때문에

    전체 화면을 덮는 배경색을 지정하고 flex를 사용해서 가운데 위치시켰다.

    // modal.css
    
    .modal-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.5);
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .modal {
      background: white;
      border-radius: 8px;
      box-shadow: inset;
      padding: 20px;
      position: relative;
    }
    
    .modal-close {
      position: absolute;
      width: 20px;
      height: 20px;
      right: 5px;
      top: 5px;
    }

     

     

    모달 스타일링은 끝났고 다시 상위 컴포넌트로 돌아가서 모달 상태관리를 위해 useState훅 사용하기

    // App.jsx
    
    import { FaRegTrashAlt } from "react-icons/fa";
    import Modal from "./components/Modal";
    import { useState } from "react";
    
    function App() {
      // 상태 관리를 위한 useState 훅 
      const [open, setOpen] = useState(false);
      
      return (
        <>
          <button onClick={() => setOpen(true)}>
            <FaRegTrashAlt />
            삭제하기
          </button>
          
          <Modal isOpen={open} onClose={() => setOpen(false)}>
            {/* children */}
            <div>
              <p>이 항목을 삭제하시겠습니까?</p>
              <div>
                <button>삭제</button>
                <button onClick={() => setOpen(false)}>
                  취소
                </button>
              </div>
            </div>
          </Modal>
        </>
      );
    }
    
    export default App;

    setOpen을 false에서 true로 설정하면 모달을 열 수 있기 때문에

    삭제하기 버튼을 클릭하면 setOpen(true)를 호출해서 상태를 업데이트 하도록 한다,

    또한, Modal 컴포넌트에서도 사용할 수 있도록 상태와 이벤트를 전달하기

     

    // Modal.jsx
    
    const Modal = ({ isOpen, onClose, children }) => {
      // 만약 isOpen이 false이면 null을 반환하여 모달을 렌더링하지 않음
      if (!isOpen) return null;
      
      return (
        <div onClick={onClose} className="modal-overlay">
          <div onClick={(e) => e.stopPropagation()} className="modal">
            <button onClick={onClose} className="modal-close">
              <IoClose />
            </button>
            {children}
          </div>
        </div>
      );
    };

    Modal 컴포넌트에선 isOpen이 false, 닫힌 상태면 null을 반환해서 모달이 화면에 나타나지 않도록 한다.

    열려있는 모달창을 닫기 위해 닫기 버튼을 클릭하면 onClose가 호출되어 모달을 닫을 수 있다.

     

    근데 닫기 버튼 말고도 모달창 바깥을 클릭해도 모달창을 닫고 싶다면? modal-overlay 부분에도 onClose를 적용하면 된다.

    하지만! 부모요소에서 자식요소로 이벤트가 전파되는 이벤트 버블링 현상이 일어나기 때문에

    모달 안에 아무 영역을 클릭해도 모달창이 닫혀버린다.

     

    그래서 내부에 e.stopPropagation()을 사용해서 모달 내부를 클릭해도 모달이 닫히지 않도록 이벤트 전파를 막을 수 있다.

     

     

    댓글