[React] 모달창 구현하기

프린이·2024년 11월 12일
post-thumbnail

🐱‍💻
여러 곳에서 재사용할 수 있는 간단한 모달창 만들기 (라이브러리❌)

🔥구현해보자❗

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

App.js

// FaRegTrashAlt: react-icons 라이브러리에서 가져온 휴지통 아이콘 컴포넌트
import { FaRegTrashAlt } from "react-icons/fa"; 
// Modal: 현재 디렉토리의 components 폴더에서 가져온 모달 컴포넌트
import Modal from "./components/Modal";

function App() {
  	return (
      <>
        {/* 버튼 생성하고 휴지통 아이콘과 함께 '삭제하기' 텍스트를 표시 */}
        <button>
          <FaRegTrashAlt /> {/* 휴지통 아이콘 */}
          삭제하기 {/* 버튼의 텍스트 */}
        </button>
        <Modal /> {/* Modal 컴포넌트를 추가 */}
      </>
    );
}
// App 컴포넌트를 기본 내보내기 하여 다른 파일에서 불러올 수 있게함
export default App;

⬇ Modal 컴포넌트에서 스타일링과 열고 닫는 기능 작성할 거

components>Modal.js

// IoClose: react-icons 라이브러리에서 가져온 'X' (닫기) 아이콘 컴포넌트
import { IoClose } from "react-icons/io5";

// 'Modal' 컴포넌트를 정의
// 이 컴포넌트는 모달 창을 나타내며, 다음의 props를 받음 :
// 'isOpen' - 모달 창이 열려 있는지 여부를 결정하는 값
// 'onClose' - 모달 창을 닫는 기능을 제공하는 함수
// 'children' - 모달 창 안에 표시될 내용
const Modal = ({ isOpen, onClose, children }) => {
  	return (
      // 모달 창 뜰 때 화면을 어둡게 하는 오버레이 역할의 div
      <div className="modal-overlay">
        {/* 모달 창 자체를 나타내는 div */}
        <div className="modal">
          {/* 모달 창 닫기 버튼, IoClose 아이콘이 버튼에 들어감 */}
          <button className="modal-close">
            <IoClose />  {/* X 아이콘 */}
          </button>
          {/* children은 모달 창 안에 삽입할 추가적인 내용을 렌더링 */}
          {children}
        </div>
      </div>
    );
};
// Modal 컴포넌트를 기본 내보내기 하여 다른 파일에서 불러올 수 있게함
export default Modal;

모달 열려있는지 여부 나타내는 'isOpen', 모달 닫는 'onClose',
모달 안에 들어갈 내용 작성할 'children'➡이 세가지 props를 받음

'children'➡컴포넌트 재사용하기 위해 내용을 prop으로 전달하는데
모달 컴포넌트 안에서 모든 내용 다 작성하면 위처럼
삭제하기 기능이 아닌 다른 곳에서 모달 컴포넌트 사용할 때
재사용 어렵기에 동적으로 컨텐츠 전달하는 역할을 함

🎨모달 스타일링 - modal.css

👉 보통 모달창 띄울 때 모달창 뒷 배경을
어둡게 또는 밝게 덮고 모달창을 가운데에 위치 시키기에
전체 화면을 덮는 배경색 지정하고 flex 사용해서 가운데 위치 시킴

/* .modal-overlay: 화면 전체를 덮는 반투명한 검은색 오버레이 */
.modal-overlay {
    position: fixed; /* 화면 스크롤과 상관없이 고정 위치에 표시 */
    top: 0; /* 화면의 맨 위에 맞춤 */
    left: 0; /* 화면의 왼쪽에 맞춤 */
    width: 100%; /* 화면의 너비 전체를 덮음 */
    height: 100%; /* 화면의 높이 전체를 덮음 */
    background: rgba(0, 0, 0, 0.5); /* 반투명한 검은색 배경 */
    display: flex; /* 모달을 가운데 정렬하기 위해 flexbox 사용 */
    justify-content: center; /* 수평 가운데 정렬 */
    align-items: center; /* 수직 가운데 정렬 */
}

/* .modal: 오버레이 위에 표시되는 흰색 모달 창 */
.modal {
    background: white; /* 모달 배경을 흰색으로 설정 */
    border-radius: 8px; /* 모달 모서리를 둥글게 만듦 */
    box-shadow: inset; /* 모달 안쪽에 그림자를 추가해 입체감 부여 */
    padding: 20px; /* 모달 내부 여백 추가 */
    position: relative; /* 닫기 버튼 위치를 잡기 위해 상대적 위치 설정 */
}

/* .modal-close: 모달 창의 닫기 버튼 스타일 */
.modal-close {
    position: absolute; /* 모달 내에서 절대적 위치 설정 */
    width: 20px; /* 닫기 버튼 너비 */
    height: 20px; /* 닫기 버튼 높이 */
    right: 5px; /* 모달 창의 오른쪽에서 5px 떨어진 위치에 배치 */
    top: 5px; /* 모달 창의 위쪽에서 5px 떨어진 위치에 배치 */
}

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

App.js

// App 컴포넌트 정의 파일
// 'FaRegTrashAlt'와 'Modal' 컴포넌트를 불러오고
// 상태 관리를 위해 useState 훅 사용
import { FaRegTrashAlt } from "react-icons/fa";
import Modal from "./components/Modal";
import { useState } from "react";

function App() {
    // 모달 창의 열림/닫힘 상태를 관리하는 상태 변수 'open'과
    // 이를 업데이트하는 함수 'setOpen'을 useState로 설정
    const [open, setOpen] = useState(false);
  
  	return (
        <>
          {/* '삭제하기' 버튼 클릭하면 'open' 상태를 true로 설정하여 모달 창 열음 */}
          <button onClick={() => setOpen(true)}>
            <FaRegTrashAlt /> {/* 휴지통 아이콘 */}
            삭제하기 {/* 버튼의 텍스트 */}
          </button>
        
          {/* Modal 컴포넌트를 표시, 'isOpen' prop에 'open' 상태 값을 전달하고,
          'onClose' prop에 모달을 닫는 함수를 전달 */}
          <Modal isOpen={open} onClose={() => setOpen(false)}>
            {/* children - Modal 컴포넌트에 표시될 내용 */}
                <div>
                    {/* 삭제 확인 메시지 */}
                    <p>이 항목을 삭제하시겠습니까?</p> 
                    <div>
                        {/* 삭제 버튼 - 여기에 실제 삭제 기능을 연결할 수 있음 */}
                        <button>삭제</button>
                        
                        {/* 취소 버튼 - 클릭 시 'open' 상태를 false로 설정하여 모달 창을 닫음 */}
                        <button onClick={() => setOpen(false)}>
                            취소
                        </button>
                    </div>
                </div>
            </Modal>
        </>
    );
}

// App 컴포넌트를 기본 내보내기 하여 다른 파일에서 불러올 수 있게함
export default App;

setOpen을 false➡true로 설정하면 모달 열 수 있기에
삭제하기 버튼 클릭하면 setOpen(true) 호출해서 상태를 업데이트 하도록 함
또한 Modal 컴포넌트에서도 사용할 수 있도록 상태와 이벤트 전달하기

Modal.js

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( ) 사용해서
모달 내부 클릭해도 모달 닫히지 않도록 이벤트 전파 막을 수 있음✋⭕

profile
안녕하세요! 퍼블리싱 & 프론트엔드 개발 공부 블로그 입니다!

0개의 댓글