Modal(이하 모달)은 사용자의 이목을 끌기 위해 사용하는 화면전환 기법 중에 하나다. 이번 프로젝트에서는 사용자가 어떤 행동을 하였을 때 이 행동을 정말로 진행할 것인지 확인 하는 용도로 사용을 하기도 하였고, 로그인이 필요한 기능을 수행하려 할 때 로그인 창을 모달로 띄워서 페이지 이동 없이 로그인을 진행하도록 수정하려고한다.
모달은 모달 컴포넌트를 만든 후 필요한 컴포넌트에서 불러오면 된다. 하지만 그냥 불러온다면 모달 컴포넌트가 보이기로는 맨앞에 보이지만 컴포넌트 트리 속 어딘가에 위치하게 되서 직관적이지 않게된다.
그래서 react-dom
의 createPortal
을 사용해서 기본의 컴포넌트 트리에서 벗어나게 직관적으로 모달을 구현해보려고 한다.
<!--public/index.html-->
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="modal"></div> // <- 코드 추가
</body>
우선 리액트 프로젝트 파일 내부의 public/index.html
파일에 root 밑에 <div id="modal"></div>
컨테이너를 추가한다.
//Modal.jsx
import React from 'react';
import { createPortal } from 'react-dom';
import styled from 'styled-components';
const ModalBg = styled.div`
display: flex;
position: fixed;
top: 0;
left: 0;
z-index: 9999;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
background-color: #ffffffe2;
`;
const ModalBox = styled.div`
width: 25rem;
background-color: white;
`;
const Modal = ({ setModalShow }) => {
const handleOk = () => {
console.log('댓글 삭제');
};
return createPortal(
<ModalBg>
<ModalBox>
<ModalTitle>댓글 삭제</ModalTitle>
<ModalContent>댓글을 정말로 삭제하시겠습니까?</ModalContent>
<ModalBtnBox>
<ModalBtn cancel onClick={() => setModalShow(false)}>
취소
</ModalBtn>
<ModalBtn onClick={() => handleOk()}>확인</ModalBtn>
</ModalBtnBox>
</ModalBox>
</ModalBg>,
document.getElementById('modal')
);
};
export default Modal;
그리고 Modal 컴포넌트의 반환 값을createPortal
로 감싸주고 원하는 element를 지정해주면 된다.
(위 코드에서 모달생성에 필수적이지 않다고 생각한 css 코드는 삭제했습니다.)
실행해보면 시각적으로는 똑같지만 html element를 보면 모달이 기존의 컴포넌트 트리에서 빠져나온 것을 확인할 수 있다.
모달도 잘 만들었는데 뒤에 배경이 스크롤이 된다면 완성도가 많이 떨어져 보일 것이다. 그래서 모달의 글의 마지막은 스크롤을 막는 방법이다.
body 태그의 css를 position
을 fixed로 변경하고, top
을 현재 스크롤 위치로 하고 overflow-y: scroll;
width: 100%;
을 추가하면 스크롤 방지를 할 수 있다.
해당 css를 변경할 때는 useEffect를 사용할 것이다. 모달이 사라질 때에는 useEffect의 return
을 사용해 body의 cssText를 리셋
시킨 다음 window.scrollTo
를 이용해 현재 스크롤 위치로 이동시키면 된다.
useEffect(() => {
document.body.style.cssText = `
position: fixed;
top: -${window.scrollY}px;
overflow-y: scroll;
width: 100%;`;
return () => {
const scrollY = document.body.style.top;
document.body.style.cssText = '';
window.scrollTo(0, parseInt(scrollY || '0', 10) * -1);
};
}, []);
프로그래밍을 잘 모르는 사람이 볼 때는 모달을 컴포넌트 트리에서 빼든 안 빼든 css적으로 동일하다면 상관없을 것이다. 하지만 이런 사소한 부분 하나 하나 모여 코드의 완성도와 깔끔함이 결정된다고 믿기에 이 방법이 좋은 프로그래밍이라고 생각한다.
💡직접 읽어보면 뼈가 되고 살이 되는 출처
👉https://codingbroker.tistory.com/126
👉http://yoonbumtae.com/?p=3776