[리액트] 모달 창 만들기

iberis2·2023년 2월 22일
0

React 리액트

목록 보기
2/20
post-thumbnail

모달(Modal)
모달은 기존에 이용하던 화면 위에 오버레이 되는 창을 뜻합니다.
닫기 버튼, 혹은 모달 범위 밖을 클릭하면 모달이 닫히는 것이 일반적이며, 모달을 닫기 전에는 기존 화면과 상호작용할 수 없습니다.

또 다른 브라우저 페이지를 여는 팝업창과는 구분되는 개념입니다. 팝업은 브라우저에 의해 강제로 막힐 수 있지만, 모달은 브라우저 설정에 영향을 받지 않아, 꼭 보여주고 싶은 내용이 있다면 모달을 사용하는 것이 좋습니다.
- 출처: 코드스테이츠 유어클래스

리액트로 모달창을 구현하면서 CSS 관련된 기억해두면 좋을 팁들을 기록해두려고 한다.

Styled Components로 리액트 안에 CSS를 구현했다.

가로 세로 가운데 정렬: flex

// Modal 구현 페이지 전체를 감싸고 있는 부모컴포넌츠인 모달컨테이너이다. 
// flex로 모달 실행 버튼을 가운데 정렬할 수 있다.
export const ModalContainer = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
`;

💡 flex로 가운데 정렬할 때, 세로 가운데 정렬이 안되면 부모 요소의 높이가 100%로 지정되어 있는지 확인하자

화면 전체에 배경 적용하는 법

(부모의 화면 크기를 넘어서)

// TODO : Modal이 떴을 때의 배경을 깔아주는 CSS
export const ModalBackdrop = styled.div`
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.4);
  display: flex;
  justify-content: center;
  align-items: center;
`;

position: fixed => 스크롤 이동해도 항상 적용
서로 반대되는 포지션에 0을 주면 양극단으로 끝에서 끝까지 차지하게 된다.
top: 0 배경이 위 쪽에 붙었다가 bottom: 0을 하면 위부터 아래 전체를 차지하게 된다.

이벤트 버블링 막기

먹색 배경을 클릭하거나 모달창 내부 X 버튼을 클릭하면 닫히지만,
모달 창 부분을 클릭하면 닫히지 않도록 하기

<ModalBackdrop onClick={openModalHandler}>
  <ModalView onClick={(e) => e.stopPropagation()}>
    <button onClick={openModalHandler}>X</button>
	<p>Hello Codestates!</p>
  </ModalView>
</ModalBackdrop>

이벤트 버블링(부모 요소의 이벤트가 자식 요소에서도 발생하는 것)을 막기 위해서는 event.stopPropagation() 메서드를 사용하면 된다.

모달 창 만들기 전체 코드

import { useState } from "react";
import styled from "styled-components";

export const ModalContainer = styled.div`
  // TODO : Modal을 구현하는데 전체적으로 필요한 CSS를 구현합니다.
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
`;

export const ModalBackdrop = styled.div`
  // TODO : Modal이 떴을 때의 배경을 깔아주는 CSS를 구현합니다.
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.4);
  display: flex;
  justify-content: center;
  align-items: center;
`;

export const ModalBtn = styled.button`
  background-color: #45579c;
  text-decoration: none;
  border: none;
  padding: 20px;
  color: white;
  border-radius: 30px;
  cursor: grab;
`;

export const ModalView = styled.div.attrs((props) => ({
  // attrs 메소드를 이용해서 아래와 같이 div 엘리먼트에 속성을 추가할 수 있습니다.
  role: "dialog",
}))`
  width: 300px;
  height: 150px;
  padding: 10px;

  position: absolute;

  background-color: #d2d7ed;
  border: none;
  border-radius: 30px;

  text-align: center;
  color: #121924;
  font-size: 30px;

  cursor: grab;
  > button {
    background: none;
    color: white;
    font-size: 1rem;
    border: none;
  }
  > p {
    color: white;
  }
`;

export const Modal = () => {
  const [isOpen, setIsOpen] = useState(false);

  const openModalHandler = () => {
    setIsOpen(!isOpen);
  };

  return (
    <>
      <ModalContainer>
        <ModalBtn onClick={openModalHandler}>
          {/* TODO : 조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때는 ModalBtn의 내부 텍스트가 'Opened!' 로 Modal이 닫힌 상태(isOpen이 false인 상태)일 때는 ModalBtn 의 내부 텍스트가 'Open Modal'이 되도록 구현해야 합니다. */}
          {isOpen ? "Opened!" : "Open Modal"}
        </ModalBtn>
        {/* TODO : 조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때만 모달창과 배경이 뜰 수 있게 구현해야 합니다. */}
        {isOpen && (
          <ModalBackdrop onClick={openModalHandler}>
            <ModalView onClick={(e) => e.stopPropagation()}>
              <button onClick={openModalHandler}>X</button>
              <p>Hello Codestates!</p>
            </ModalView>
          </ModalBackdrop>
        )}
      </ModalContainer>
    </>
  );
};
profile
React, Next.js, TypeScript 로 개발 중인 프론트엔드 개발자

0개의 댓글