모달(Modal)
모달은 기존에 이용하던 화면 위에 오버레이 되는 창을 뜻합니다.
닫기 버튼, 혹은 모달 범위 밖을 클릭하면 모달이 닫히는 것이 일반적이며, 모달을 닫기 전에는 기존 화면과 상호작용할 수 없습니다.또 다른 브라우저 페이지를 여는 팝업창과는 구분되는 개념입니다. 팝업은 브라우저에 의해 강제로 막힐 수 있지만, 모달은 브라우저 설정에 영향을 받지 않아, 꼭 보여주고 싶은 내용이 있다면 모달을 사용하는 것이 좋습니다.
- 출처: 코드스테이츠 유어클래스
리액트로 모달창을 구현하면서 CSS 관련된 기억해두면 좋을 팁들을 기록해두려고 한다.
Styled Components로 리액트 안에 CSS를 구현했다.
// 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>
</>
);
};