[NEXT.js] 모달 띄우는 법

현용찬·2024년 10월 6일
0

최상위 컴포넌트 (LayOut.tsx와 같은)에 modal을 props로 넘겨주고 모달을 띄우고 싶은 컴포넌트에 모달 상태 관리(useState)를 사용해서 모달 띄우기

예제 코드

파일 구조

MainLaout.tsx

import React, { ReactNode } from "react";
import styled from "styled-components";
import { useRouter } from "next/router";
import Footer from "./Footer";
import Header from "./Header";
import SearchHeader from "../components/SearchHeader";
// import WriteHeader from "../components/WriteHeader";
import PageTransition from "../components/PageTransition/PageTransition";
import CommentFooter from "../components/CommentFooter";

interface MainLayoutProps {
  children: ReactNode;
  modal?: ReactNode; // modal 속성 추가
}

const MainLayout: React.FC<MainLayoutProps> = ({ children, modal }) => {
  const router = useRouter();

  // 특정 경로에서는 Header와 Footer 렌더링 안 함
  const hideHeader =
    router.pathname === "/search" ||
    router.pathname === "/write" ||
    router.pathname === "/" ||
    router.pathname === "/signup";
  const showSearchHeader = router.pathname === "/search";
  const showWriteHeader = router.pathname === "/write";
  const hideFooter = router.pathname === "/";
  const commentFooter =
    router.pathname === "/" ||
    router.pathname === "/write" ||
    router.pathname === "/search";

  return (
    <>
      <PageTransition>
        <Main>
          {children}
          {modal}
        </Main>
      
      </PageTransition>
    </>
  );
};

export default MainLayout;

const Main = styled.main`
  display: flex;
  flex-direction: column;
  max-width: 430px;
  margin: 0 auto;
  min-height: calc(100vh - 50px);

  height: 100%;
  @media (min-width: 375px) {
    width: 430px;
  }
  @media (max-width: 500px) {
    width: 100vw;
  }
`;

Header.tsx

import React, { useState } from "react";
import styled from "styled-components";
import { useRouter } from "next/router";

import ReadingGlasses from "../assets/header/ReadingGlasses.svg";
import VericalDot from "../assets/header/VerticalDots.svg";
import Back from "../assets/header/Back.svg";
import GoBack from "../assets/header/GoBack.svg";
import Link from "next/link";
import EditModal from "../pages/modal/edit/EditModal"; // 모달 컴포넌트 가져오기

interface FooterProps {
  token: string | undefined;
}

const Header: React.FC<FooterProps> = ({ token }) => {
  const router = useRouter();
  const ShowBack = router.pathname === "/content";
  const Home = router.pathname === "/";

  // '/main'에 정확히 일치하는지 여부와 '/main/contents/*' 경로 구분

  const isContentsPage = router.pathname.startsWith("/main/contents");

  // 모달 상태 관리
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <HeaderContainer>
      <HeaderBack onClick={() => router.push(`/main/${token}`)}>
        {ShowBack ? <Back /> : !Home && <GoBack />}
      </HeaderBack>

      <HeaderItem>
        자유게시판
        <HeadersubItem>상명대 천안캠</HeadersubItem>
      </HeaderItem>
      <HeaderBt>
        <Link href={`/search?token=${token}`}>
          <HeaderRG>
            <ReadingGlasses />
          </HeaderRG>
        </Link>
        {/* '/main'에서는 HeaderVD만 렌더링, '/main/contents/*'에서는 클릭 이벤트 포함 */}

        {isContentsPage ? (
          <HeaderVD onClick={() => setIsModalOpen(true)}>
            <VericalDot />
          </HeaderVD>
        ) : (
          <HeaderVD></HeaderVD>
        )}
      </HeaderBt>

      {/* 모달이 열려 있을 때만 렌더링 */}
      {isModalOpen && <EditModal token={token} />}
    </HeaderContainer>
  );
};

export default Header;

const HeaderContainer = styled.div`
  display: flex;
  position: sticky;
  text-align: center;
  width: 100%;
  height: 50px;
  top: 0;
  background-color: white;
`;
const HeaderBack = styled.div`
  margin-top: 18px;
  margin-left: 3%;
  width: 20px;
`;

const HeaderItem = styled.div`
  flex-direction: column;
  font-size: 13px;
  width: 100%;
  background-color: white;
  display: flex;
  padding-top: 12px;
  padding-left: 70px;
`;

const HeadersubItem = styled.div`
  font-size: 13px;
  color: gray;
`;

const HeaderBt = styled.div`
  margin-right: 5%;
  margin-top: 20px;
  display: flex;
  gap: 30px;
`;

const HeaderRG = styled.div``;

const HeaderVD = styled.div``;

EditModal.tsx(모달 컴포넌트)

import { useRouter } from "next/router";
import React from "react";
import styled from "styled-components";

interface EditModalProps {
  token: string | undefined;
}
const EditModal: React.FC<EditModalProps> = ({ token }) => {
  const router = useRouter();
  return (
    <ModalOverlay>
      <ModalContent>
        <h2>Edit Modal</h2>
        <p>내용을 수정할 수 있습니다.</p>
        <button onClick={() => router.back()}>닫기</button>
      </ModalContent>
    </ModalOverlay>
  );
};

export default EditModal;

const ModalOverlay = styled.div`
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0);
  display: flex;
  justify-content: center;
  align-items: center;
`;

const ModalContent = styled.div`
  background-color: white;
  padding: 20px;
  border-radius: 10px;
  width: 400px;
  text-align: center;
`;

정리

MainLayout에서 modal을 props로 넘겨주고 Header.tsx에서 모달 상태관리를 통해 해당 버튼을 누르면 초깃값이 false였던게 true로 바뀌면서 모달 컴포넌트(EditModal.tsx)를 띄움

profile
항상 웃어 봅시다

0개의 댓글