모달 창 구현 - modal chang guhyeon

# 리액트 모달창 구현 방법

- redux나 별도 라이브러리 없이 아주 쉽게 모달창을 구현하는 방법

1. state 로 Modal 노출 여부를 관리한다.

- useState 훅으로 모달창 노출 여부를 관리해준다.

2. Modal 창이 최상위에 노출되도록 CSS를 조정한다.

- position: absolute로 위치를 조정한다.

- z-index를 높게 줘서 최상위에 노출 시킨다.

3. (옵션) 모달창 외부 클릭시, 모달창 제거 처리해준다.

- 모달창 외부 document 클릭 이벤트를 달아준다.


1. state 로 Modal 노출 여부를 관리한다.

- button을 클릭하면, setModalOpen(true)를 호출한다.

- modalOpen이 true가 되면, <ModalBasic /> 컴포넌트가 노출된다.

- <ModalBasic /> 컴포넌트 내부에 X버튼 클릭시, 모달 제거를 위해 setModalOpen을 props 로 전달한다.

< 모달을 노출시키는 페이지 컴포넌트 >

import { useState } from 'react';
import ModalBasic from '../src/common/ModalBasic';

// 모달을 노출하는 페이지
function Modal() {
    // 모달창 노출 여부 state
    const [modalOpen, setModalOpen] = useState(false);

    // 모달창 노출
    const showModal = () => {
        setModalOpen(true);
    };

    return (
        <div>
            <button onClick={showModal}>모달 띄우기</button>
            {modalOpen && <ModalBasic setModalOpen={setModalOpen} />}
        </div>
    );
}

export default Modal;

< 모달창 컴포넌트 >

import styles from './ModalBasic.module.css';

function ModalBasic({ setModalOpen, id, title, content, writer }: PropsType) {
    // 모달 끄기 
    const closeModal = () => {
        setModalOpen(false);
    };

    return (
        <div className={styles.container}>
            <button className={styles.close} onClick={closeModal}>
                X
            </button>
            <p>모달창입니다.</p>
        </div>
    );
}
export default ModalBasic;

(참고) CSS Module 방식을 사용했다.

2. Modal 창이 최상위에 노출되도록 CSS를 조정한다.

- <ModalBasic> 컴포넌트의 CSS를 조정하여, 최상위에 노출되도록 한다.

/* 모달창을 화면 중앙. 최상단에 노출 */
.container {
  /* 모달창 크기 */
  width: 300px;
  height: 200px;

  /* 최상단 위치 */
  z-index: 999;
  
  /* 중앙 배치 */
  /* top, bottom, left, right 는 브라우저 기준으로 작동한다. */
  /* translate는 본인의 크기 기준으로 작동한다. */
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);

  /* 모달창 디자인 */
  background-color: gray;
  border: 1px solid black;
  border-radius: 8px;
}

/* 모달창 내부 X버튼 */
.close {
  position: absolute;
  right: 10px;
  top: 10px;
}

여기까지만 해도,

버튼을 누르면 모달창을 띄우고, X버튼을 누르면 제거하는 기본 모달 기능은 구현된다.

모달 창 구현 - modal chang guhyeon

3. 모달 외부 클릭시, 제거 처리

- document 에 mousedown 이벤트핸들러를 등록하고,

- 모달창 영역이 아닐 경우에만, modalOpen 상태를 false로 전환해준다.

import { useEffect, useRef } from 'react';
import styles from './ModalBasic.module.css';

function ModalBasic({ setModalOpen, id, title, content, writer }: PropsType) {

    // 모달 끄기 (X버튼 onClick 이벤트 핸들러)
    const closeModal = () => {
        setModalOpen(false);
    };

    // 모달 외부 클릭시 끄기 처리
    // Modal 창을 useRef로 취득
    const modalRef = useRef<HTMLDivElement>(null);
    
    useEffect(() => {
        // 이벤트 핸들러 함수
        const handler = () => {
            // mousedown 이벤트가 발생한 영역이 모달창이 아닐 때, 모달창 제거 처리
            if (modalRef.current && !modalRef.current.contains(event.target)) {
                setModalOpen(false);
            }
        };
        
        // 이벤트 핸들러 등록
        document.addEventListener('mousedown', handler);
        // document.addEventListener('touchstart', handler); // 모바일 대응
        
        return () => {
            // 이벤트 핸들러 해제
            document.removeEventListener('mousedown', handler);
            // document.removeEventListener('touchstart', handler); // 모바일 대응
        };
    });
    
    return (
        // 모달창을 useRef로 잡아준다.
        <div ref={modalRef} className={styles.container}>
            <button className={styles.close} onClick={closeModal}>
                X
            </button>
            <p>모달창입니다.</p>
        </div>
    );
}
export default ModalBasic;

위 3번 코드에서

useEffect 훅을 사용하는 부분은 커스텀훅으로 분리하면, 

코드가 더 간결해지고 가독성이 좋아질 것이다.

모달 창 구현 - modal chang guhyeon

자바스크립트를 이용하여
모달창(팝업창)을 만들겠습니다.

요즘에는 제이쿼리를 사용하지 않고,
순수한 자바스크립트, 바닐라 js를 이용한 기능 구현을 많이 합니다.

모달창 띄우기 구현내용

· 버튼 클릭시 모달창 open
· 모달창을 제외한 배경(background)는 어두운 검은 배경
· X 버튼 클릭시 모달창 close
· 검은 배경 클릭시 모달창 close

모달창(팝업창, 다이얼로그)창 띄우기 예제

html,  javascript

<button class="openBtn">모달창 open</button>
<div class="modal hidden">
  <div class="bg"></div>
  <div class="modalBox">
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Consectetur quam nobis quis corrupti amet maxime neque, optio, in illo, voluptatibus consequuntur! Rerum quo ea nulla qui, maxime consectetur magni soluta!</p>
    <button class="closeBtn">✖</button>
  </div>
</div>

<script>
  const open = () => {
    document.querySelector(".modal").classList.remove("hidden");
  }

  const close = () => {
    document.querySelector(".modal").classList.add("hidden");
  }

  document.querySelector(".openBtn").addEventListener("click", open);
  document.querySelector(".closeBtn").addEventListener("click", close);
  document.querySelector(".bg").addEventListener("click", close);

</script>

css

        button {
          background-color: #F9B514;
          padding: 5px 10px;
          border-radius: 4px;
          cursor: pointer;
        }

        .modal {
          position: fixed;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
          display: flex;
          justify-content: center;
          align-items: center;
        }

        .modal .bg {
          width: 100%;
          height: 100%;
          background-color: rgba(0, 0, 0, 0.6);
        }

        .modalBox {
          position: absolute;
          background-color: #fff;
          width: 400px;
          height: 200px;
          padding: 15px;
        }

        .modalBox button {
          display: block;
          width: 80px;
          margin: 0 auto;
        }

        .hidden {
          display: none;
        }

한 번에 보기(Result 클릭)