모달(Modal)
✔️ 모달이란?
모달 : 기존 화면 위에 겹쳐서 나타나는 대화 상자나 팝업 창을 의미
✔️ 모달 vs 팝업
- 모달
- 모달 창을 닫거나, 내부에서 확인 또는 취소 액션을 하기 전까지는 원래 보던 화면(부모)을 건드릴 수 X
- 팝업
- 팝업 : 원래 보던 화면과 완전히 독립되어 띄워지는 새로운 창
- 팝업 창이 떠있어도 사용자는 뒤에 있는 화면을 클릭하거나 스크롤할 수 O
✔️ 모달 구현하기


// src/App.tsx
import { useState } from "react";
import Modal from "./components/Modal";
import ModalContent from "./components/ModalContent";
import ModalContent2 from "./components/ModalContent2";
export default function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
const toggleModal = () => setIsModalOpen((prev) => !prev);
return (
<div className="flex items-center justify-center min-h-screen bg-yellow-200">
<button
className="border-3 border-blue-500 bg-white px-2 py-1 rounded-lg cursor-pointer"
onClick={toggleModal}
>
모달 열기
</button>
{isModalOpen && (
<Modal onClose={toggleModal}>
<ModalContent />
<ModalContent2 />
</Modal>
)}
</div>
);
}
- const [isModalOpen, setIsModalOpen] = useState(false)
- isModalOpen : 현재 모달의 상태를 담는 변수
- setIsModalOpen : 모달을 켰다 껐다할 수 있는 변경 함수
- const toggleModal = () => setIsModalOpen((prev) => !prev)
- 이전 상태가 false --> !false --> true
- 이전 상태가 true --> !true --> false
// src/components/Modal.tsx
import type { ComponentPropsWithoutRef } from "react";
import cancelIcon from "../assets/closeIcon.png";
interface ModalProps extends ComponentPropsWithoutRef<"div"> {
onClose: () => void;
}
export default function Modal({ children, onClose, ...props }: ModalProps) {
return (
// 모달 창 바깥 영역
<div
role="presentation"
className="fixed inset-0 z-99 flex w-full h-full items-center justify-center bg-gray-900/50 backdrop-blur-sm"
onClick={onClose}
{...props}
>
{/* 모달 영역 */}
<section
role="dialog"
className="relative rounded-xl bg-[#1b1b22] px-5 py-10"
onClick={(e) => e.stopPropagation()}
>
{/* close 버튼 */}
<button
className="absolute right-4 top-4 w-3 h-3 cursor-pointer"
onClick={onClose}
>
<img
src={cancleIcon}
className="w-full h-full object-contain invert brightness-200"
/>
</button>
{children}
</section>
</div>
);
}
- extends ComponentPropsWithoutRef<"div">
- ModalProps에는 onClose, children만 존재 --> 모달 컴포넌트 자체에 id, className 등의 속성 사용 불가능
- <div> 태그가 가질 수 있는 모든 속성들을 가져와서 사용하겠다는 의미
- ㄴ ref(리액트에서 DOM을 직접 가리키는 역할)만 빼고
- if 모달이 아니라 커스텀 버튼을 만드는 중이었다면?) <"button">
- ({ children, onClos, ...props }: ModalProps)
- 구조 분해
- children, onClose만 골라 쓰고 나머지는 props로 받겠다는 의미
- <div role="presentation" className="fixed inset-0 z-99" onClick={onClose}>{...props}</div>
- role="presentation" : 웹 접근성 표준 속성 --> 시각용 레이어임을 선언
- fixed inset-0 z-99 : 뒤의 일반 본문 글자나 이미지들이 모달 배경 위로 오지 못하도록 설정
- onClick={onClose} : 모달 바깥을 클릭해도 모달 창이 닫힐 수 있도록 설정
- {...props} : 부모 컴포넌트가 모달을 부를 때 넘겨준 속성 중에서, children과 onClose를 제외한 나머지 속성(id, 추가 className 등)을 spread하여 주입하겠다는 의미
- <section role="dialog" onClick={(e) => e.stopPropagation()}>
- role="dialog" : 웹 접근성 표준 속성 --> 여기서부터는 사용자가 상호작용해야 하는 영역임을 선언
- onClick={(e) => e.stopPropagation() : 버블링 방지
- <img className="object-contain invert brightness-200" alt="close" />
- object-contain : 이미지의 가로세로 원본 비율 유지
- invert : 일반 텍스트가 아닌 PNG 이미지 파일이기 때문에 색상을 정반대로 반전
- brightness-200 : 밝기를 200% 증가
- alt="close" : 웹 접근성 표준 속성 --> 이미지의 정체를 알려주는 대체 텍스트
- {children}
- App 컴포넌트에서 <Modal></Modal> 태그 사이에 다른 컴포넌트를 집어넣음
- ㄴ 해당 컴포넌트들을 children으로 묶어 Modal 컴포넌트에게 전달
'React' 카테고리의 다른 글
| 리액트 애니메이션 - Framer Motion (0) | 2026.06.13 |
|---|---|
| 리액트 - Toggle Button 구현 (0) | 2026.05.28 |