Frontend/HTML
[HTML] <dialog> 태그 + createPortal로 모달(modal) 만들기?? [10/3 study]
동혁이
2024. 10. 3. 14:41
<dialog> 태그로 모달(modal) 만들기??
이 글을 작성하게 된 이유
원래는 실제 프로젝트에서 useModal 커스텀훅과 상위 파일인 layout.tsx에 div태그에 id값을 주어야 하는 방법까지 번거롭게 작업했었다.
하지만 강의를 들으면서 상위 layout 파일에 div 요소도 안넣고 더 간편하게 사용하는 방법을 알게 되어 글을 작성해본다.
import '../styles/globals.css';
import Providers from './providers';
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ko">
<body>
<Providers>{children}</Providers>
<div id="modal-root" />
</body>
</html>
);
}
'use client';
import { type ReactNode } from 'react';
import ReactDOM from 'react-dom';
import { useModal } from '@/_hooks/useModal';
type ModalProps = {
children: ReactNode;
isOpen: boolean;
onClose: () => void;
};
/**
* 사용법
* ex)
* const { isOpen, openModal, closeModal } = useModal();
*
* <button onClick={openModal}>Open Modal</button>
<Modal isOpen={isOpen} onClose={closeModal}>
<div className="m-auto px-[90px] pb-[28px] pt-[26px] text-right text-[18px] md:w-[540px] md:px-[33px]">
<p className="pb-[43px] pt-[53px] text-center">가입이 완료되었습니다!</p>
<span className="flex justify-center md:justify-end">
<button className="h-[42px] w-[138px] rounded-[8px] bg-black text-white">확인</button> // 버튼 컴포넌트 넣어주시면 될 것 같습니다!
<button onClick={closeModal}>취소</button> // 버튼 컴포넌트 넣어주시면 될 것 같습니다!
</span>
</div>
</Modal>
*/
/**
* Modal 컴포넌트의 props
*
* @property {boolean} isOpen - Modal Open 여부
* @property {() => void} onClose - Modal Close 함수
* @property {ReactNode} children - Modal 자식 요소들
*/
export default function Modal({ isOpen, onClose, children }: ModalProps) {
const { isMounted } = useModal();
const handleBackgroundClick = (event: React.MouseEvent<HTMLDivElement>) => {
// 배경 클릭 시 모달 닫기
if (event.target === event.currentTarget) {
onClose();
}
};
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'Escape') {
onClose();
}
};
if (!isOpen || !isMounted) return null;
return ReactDOM.createPortal(
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black bg-opacity-70" onClick={handleBackgroundClick} onKeyDown={handleKeyDown}>
<div className="relative rounded-[12px] bg-white shadow-lg">{children}</div>
</div>,
document.getElementById('modal-root') as HTMLElement,
);
}
바로바로 dialog 태그와 createPortal를 이용하면 된다!
딱봐도 코드가 더 간결해지고 children을 받으면서 쉽게 커스텀도 용이해졌다!
지금이라도 내 게시글을 보는 사람들은 이 방법으로 바꿨으면 좋겠다! (더 좋은 방법이 있으면 말좀요...! ㅎ)
import { createPortal } from "react-dom";
import React, { useRef, useState } from "react";
type ModalProps = {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
};
const ModalComp = (
<dialog
ref={ref}
open={isOpen}
id={"modal"}
className={`absolute left-0 top-0 w-screen h-screen bg-opacity-20 bg-gray-800 ${animation}`}
onClick={(e) => {
if (e.target === ref.current) {
handleClose();
}
}}
>
<article
id={"modal-content"}
className={"fixed left-1/4 top-1/4 w-1/2 h-1/2 bg-white rounded-lg p-5"}
>
{children}
</article>
</dialog>
);
return createPortal(ModalComp, document.body);
}