본문 바로가기
개발(Development)/React(리액트)

[React] 모달창(Modal) 초간단 구현 방법(리덕스, 라이브러리 X)

by 카레유 2022. 8. 16.

이 글은 useState, useRef 등만을 활용하여 수동으로 모달창을 구현하는 방법입니다.

 

이 것만으로도 이미 충분히 간단하지만,

만약 HTML의 dialog 엘리먼트로 훨씬 더 쉽게 구현하는 방법이 필요하시다면, 아래 글을 참고해주세요.

 

[React] 모달창(Modal) 쉽고 간단한 구현 방법 (HTML dialog 엘리먼트 사용)


# 리액트 모달창 구현 방법

- 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버튼을 누르면 제거하는 기본 모달 기능은 구현된다.

 

 

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 훅을 사용하는 부분은 커스텀훅으로 분리하면, 

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


참고) HTML dailog 엘리먼트를 이용해, 훨씬 쉽게 React에서 모달창을 구현하는 방법은 아래 글을 참고하시기 바랍니다.

[React] 모달창(Modal) 쉽고 간단한 구현 방법 (HTML dialog 엘리먼트 사용)

 

 

댓글