Next.js에서 ReactPortal을 사용하기 위해서는 _app 이후에 동작하는 _document을 작성해야 합니다.
_document는 공통적으로 활용할 <head>
(Ex. 메타 태그)나 <body>
태그 안에 들어갈 내용들을 커스텀할때 활용합니다.
import Document, { Html, Head, Main, NextScript } from "next/document";
class MyDocument extends Document {
render() {
return (
<Html>
<Head />
<body>
<Main />
//document에 _modal이란 div가 생성된다.
<div id="_modal"></div>
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
import { Fragment, PropsWithChildren } from "react";
import ReactDOM from "react-dom";
import styles from './Modal.module.css'
interface ModalProps{
onClose : () => void
}
const BackDrop = (props : ModalProps) =>{
return(
<div className={styles.backdrop} onClick={props.onClose}></div>
)
}
const ModalOverlay = ({children}: PropsWithChildren) =>{
return (
<div className={styles.modal}>
<div className={styles.content}>
{children}
</div>
</div>
)
}
const Modal = ( props : PropsWithChildren<ModalProps>) =>{
const selectedElement = document.getElementById('_modal');
if(selectedElement === null){
//null에 대한 에러 처리를 할 수 있다.
return <div></div>
}
return <Fragment>
{ReactDOM.createPortal(<BackDrop onClose={props.onClose}/>, selectedElement)}
{ReactDOM.createPortal(<ModalOverlay>{props.children}</ModalOverlay>, selectedElement)}
</Fragment>
}
export default Modal;
import Link from "next/link";
import Modal from "../UI/Modal";
interface MenuItems {
url: string;
menu: string;
}
const menus: MenuItems[] = [
{ url: "/", menu: "Home" },
{ url: "/skills", menu: "Skills" },
{ url: "/projects", menu: "Project" },
];
interface Iprops{
onClose: () => void
}
const Menu = (props :Iprops) => {
return (
<Modal onClose={props.onClose}>
<ul onClick={props.onClose}>
{menus.map((menuObj) => (
<li key={menuObj.menu}>
<Link href={menuObj.url}>{menuObj.menu}</Link>
</li>
))}
</ul>
</Modal>
);
};
export default Menu;
const [menuSelected, setMenuSelected] = useState(false);
const menuHandler = () =>{
setMenuSelected(true);
}
const closeMenuHanlder = () =>{
setMenuSelected(false);
}
return (
<div>
{ menuSelected && <Menu onClose={closeMenuHanlder}/>}
<div className={styles.menuIcon}>
<Image onClick={menuHandler} width={"35px"} height={"35px"} src={menuIcon} alt={"메뉴 네비게이션입니다."}/>
</div>
</div>
)