diff --git a/src/components/common/Modal.tsx b/src/components/common/Modal.tsx index 84a9b749..3dcd489a 100644 --- a/src/components/common/Modal.tsx +++ b/src/components/common/Modal.tsx @@ -1,4 +1,9 @@ -import { type MouseEventHandler, type ReactNode } from 'react'; +import { + type MouseEventHandler, + type ReactNode, + useEffect, + useRef, +} from 'react'; import Button from './Button'; import ic_check from '@/assets/icons/modal_check.svg'; import ic_exclamation from '@/assets/icons/modal_exclamation.svg'; @@ -14,6 +19,8 @@ interface Props { yesButtonContent?: string; /** modal 문구 */ children?: ReactNode; + /** 모달이 닫힐 때 (버튼 클릭 제외) 실행할 함수 */ + onClose: () => void; } export default function Modal({ @@ -22,11 +29,42 @@ export default function Modal({ onYesButtonClick, yesButtonContent, children, + onClose, }: Props) { + const modalRef = useRef(null); + + useEffect(() => { + // 1. 바깥 클릭 시 닫기 + function handleClickOutside(event: MouseEvent): void { + const target = event.target as Node; + if (modalRef.current && !modalRef.current.contains(target)) { + onClose(); + } + } + + // 2. ESC 키로 닫기 + function handleKeyDown(event: KeyboardEvent): void { + if (event.key === 'Escape') { + onClose(); + } + } + + document.addEventListener('mousedown', handleClickOutside); + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('keydown', handleKeyDown); + }; + }, [onClose]); + return (
{option === 'alert' ? ( -
+
{children}
@@ -38,7 +76,10 @@ export default function Modal({
) : ( -
+
{option === 'action' ? ( diff --git a/src/pages/account/Login.tsx b/src/pages/account/Login.tsx index a71fff31..83bc93ca 100644 --- a/src/pages/account/Login.tsx +++ b/src/pages/account/Login.tsx @@ -81,9 +81,9 @@ export default function Login() { } } - const handleModalConfirm = () => { + function handleModalConfirm() { setModal({ isOpen: false, message: '' }); - }; + } return ( <> @@ -128,7 +128,9 @@ export default function Login() { {modal.isOpen && ( - {modal.message} + + {modal.message} + )} ); diff --git a/src/pages/account/Signup.tsx b/src/pages/account/Signup.tsx index d32ef5de..04d61817 100644 --- a/src/pages/account/Signup.tsx +++ b/src/pages/account/Signup.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useCallback } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { postUser, type UserType } from '@/api/userApi'; import Input from '@/components/common/Input'; @@ -107,14 +107,14 @@ export default function Signup() { } } - const handleModalConfirm = () => { + const handleModalConfirm = useCallback(() => { if (modal.message === '가입이 완료되었습니다!') { setModal({ isOpen: false, message: '' }); navigate('/login'); } else { setModal({ isOpen: false, message: '' }); } - }; + }, [modal.message, navigate]); return ( <> @@ -223,7 +223,9 @@ export default function Signup() { {modal.isOpen && ( - {modal.message} + + {modal.message} + )} );