개발 이야기/React & Next

[React/Typescript] Modal/Dialog, 모달 창 만들기

Heman 2021. 11. 28. 01:04

[React/Typescript/Styled-components] 모달 창 만들기

이번 포스팅에서는 React 라이브러리와 Typescript, 그리고 Styled-components로 우리가 흔히 모달이라 부르는 Dialog window를 만들어보겠습니다.

 

뭔가 만들기 어려울 것 같지만, 생각보다 너무 간단해서 놀라 자빠지실지도 모릅니다!

 

Overview

우선 최종 결과가 어떤 식으로 나오는지 보시면 좋을 것 같습니다!

 

버튼 클릭 전

 

버튼 클릭 후

 

최종적으로 코드를 적용하고 실행한 웹 페이지의 모습입니다.

"Open Modal" 버튼을 클릭하면 모달 창이 열리고, 반투명한 회색 배경(Backdrop)을 클릭하면 모달 창이 닫힙니다.

 

그럼 이제 구현 코드들을 살펴보겠습니다~

폴더구조

src 폴더의 구조는 아래와 같습니다.

 

엥 이게 다야? 네, 이게 끝입니다..ㅋ...ㅎㅎ

여기에 추가적으로 모달에 들어갈 내용들을 따로 관리하여 사용하실 수 있습니다.

 

App.tsx

이번 프로젝트의 상위 파일인 App.tsx에 작성한 코드입니다. 실제 프로젝트에 적용하실 때에는 모달 창을 여는 버튼이 속한 컴포넌트에 작성 하시면 됩니다.

 

import { useState, useCallback } from "react";
import styled from "styled-components";
import Modal from "./components/Modal";

function App() {
  const [isOpenModal, setOpenModal] = useState<boolean>(false);

  const onClickToggleModal = useCallback(() => {
    setOpenModal(!isOpenModal);
  }, [isOpenModal]);

  return (
    <Main>
      <Title>여긴 배경화면 입니다</Title>
      {isOpenModal && (
        <Modal onClickToggleModal={onClickToggleModal}>
          이곳에 children이 들어갑니다.
        </Modal>
      )}
      <DialogButton onClick={onClickToggleModal}>Open Modal</DialogButton>
    </Main>
  );
}

const Main = styled.main`
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
`;

const Title = styled.h3`
  text-align: center;
`;

const DialogButton = styled.button`
  width: 160px;
  height: 48px;
  background-color: blueviolet;
  color: white;
  font-size: 1.2rem;
  font-weight: 400;
  border-radius: 4px;
  border: none;
  cursor: pointer;

  &:hover {
    transform: translateY(-1px);
  }
`;

export default App;

 

 

useState 훅을 이용하여 간단하게 boolean 상태 값을 컨트롤하는 "onClickToggleModal" 함수를 만들었습니다.

isOpenModal 상태값에 따라 Modal 컴포넌트의 조건부 랜더링이 적용됩니다.

 

Modal 컴포넌트의 배경을 클릭했을 때, 모달 창을 닫아주기 위해 "onClickToggleModal" 함수를 props로 전달합니다.

useCallback 훅은 사용하지 않아도 기능 동작에 문제는 없습니다ㅎㅎ

 

children을 props로 전달 받을 수 있도록 작성할 것이기 때문에, "이곳에 children이 들어갑니다." 문구 대신 다른 태그나 컴포넌트를 삽입해도 무방합니다. 

 

components/Modal.tsx

Modal.tsx의 전체코드입니다. 

 

import React, { PropsWithChildren } from "react";
import styled from "styled-components";

interface ModalDefaultType {
  onClickToggleModal: () => void;
}

function Modal({
  onClickToggleModal,
  children,
}: PropsWithChildren<ModalDefaultType>) {
  return (
    <ModalContainer>
      <DialogBox>{children}</DialogBox>
      <Backdrop
        onClick={(e: React.MouseEvent) => {
          e.preventDefault();

          if (onClickToggleModal) {
            onClickToggleModal();
          }
        }}
      />
    </ModalContainer>
  );
}

const ModalContainer = styled.div`
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  position: fixed;
`;

const DialogBox = styled.dialog`
  width: 800px;
  height: 400px;
  display: flex;
  flex-direction: column;
  align-items: center;
  border: none;
  border-radius: 3px;
  box-shadow: 0 0 30px rgba(30, 30, 30, 0.185);
  box-sizing: border-box;
  background-color: white;
  z-index: 10000;
`;

const Backdrop = styled.div`
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  z-index: 9999;
  background-color: rgba(0, 0, 0, 0.2);
`;

export default Modal;

 

 

위(상위 컴포넌트)에서 props로 받아온 "onClickToggleModal" 함수를 "PropsWithChildren"과 함께 정의하여 사용합니다.

 

배경(Backdrop)과 ModalContainer에 position: fixed를 적용하고 다른 스타일들은 프로젝트에 맞게 작성하시면 됩니다.

진짜 별거 없습니다ㅋ  

 

배경에 onClick을 적용하여 상위 컴포넌트에 존재하는 isOpenModal 값을 컨트롤 할 수 있도록 작성해줍니다. 배경에만 onClick 이벤트가 국한 될 수 있도록, 이벤트 전이를 막는 e.preventDefault() 함수도 적용해 주면 간단한 모달 창 만들기 끝!