스프린트 중에 모달 이라는 게 나왔는데, 이 이상한 어감의 단어는 무엇인지 궁금해졌습니다.
영어 사전에 치면 1.양식의 2.형태상의 3. 법의 ..이런 식으로 쓰이는데, 아무래도 실제 용도와 전혀 상관 없는 듯 합니다.
Modal에 대해서 위키페디아에서는 "사용자 인터페이스 디자인 개념에서 자식 윈도에서 부모 윈도로 돌아가기 전에 사용자의 상호동작을 요구하는 창을 말한다. 그래서 응용 프로그램의 주 창의 작업 흐름을 방해한다. 일반적으로 모달 대화상자로 불리는데, 그 이유는 대화상자를 부를 때 흔히 사용되기 때문이다. 우리가 자주 사용하는 파일 열기/저장 대화상자를 생각하면 이해하기 쉽다." 이렇게 묘사합니다.
주로 팝업창과 모달창을 비교할 때, 자식 윈도에서 부모 윈도로 상속에 개념에 집중하여서 비교해서 설명합니다. modal의 의미에 좀 더 가까운 것은 응용 프로그램의 주 창의 작업흐름을 방해한다에 있습니다.
한번 모달창이 열리면 (자식 페이지), 모달창이 끝날 때까지 부모페이지의 작업을 하지 못하게됩니다. 과정이 추가가 되는 것입니다.
모달 창이라고 이름이 붙은 것은 이 모달 창의 작업이 종료되기전까지 다른 작업을 하지 못하게 됩니다. 모달 창의 의미와 중요성은 여기에 있습니다. 다른 작업으로 넘어가기전에 꼭 필요한 '정보 전달'을 할 수도 있고 과정을 추가하여 필요한 데이터를 받아올 수도 있습니다.
//ModalContainer.jsx
import React, { useState } from 'react'
import Modal from './Modal'
const ModalContainer = () => {
const [modalOpen, setModalOpen] = useState(false)
const modalClose = () => {
setModalOpen(!modalOpen)
}
return (
<>
<button onClick={modalClose}>Click</button>
{ modalOpen && <Modal modalClose={modalClose}></Modal>}
</> // 조건부 사용
)
}
export default ModalContainer
button을 누르면 modalClose 함수가 발동!, modalOpen이 true값으로 반환되어진다.
그리고 Modal 컴포넌트가 실행되어진다
// Modal.jsx
import React from 'react'
import './Modal.scss'
const Modal = ({modalClose}) => {
const onCloseModal = (e) => {
console.log('e.target: ', e.target)
console.log('e.tarcurrentTargetget: ', e.currentTarget)
if(e.target === e.currentTarget){
modalClose()
}
}
return (
<div className="modal__container" onClick = {onCloseMedal}>
<div className="modal">
<button className="modal__button" onClick={modalClose}> Modal Close</button>
</div>
</div>
)
}
export default Modal
이 부분에서 유념해서 봐야할 곳은 State 끌어올리기이다, 부모 컴포넌트에서 자식창을 여는 것까지는 됬지만 이제는 반대로 자식창에서 부모창으로 데이터를 전달해서 다시 false값으로 바꿔야합니다.
그래서 자식 컴포넌트로 {modalClose}를 전달하고 그 값을 다시 받아와야합니다.
// Modal.scss
.modal__container{
width: 100%;
height: 100vh;
background-color: rgba(0,0,0,0.4);
z-index: 10;
position: fixed;
top: 0;
left: 0;
.modal{
width: 300px;
height: 150px;
background-color: #fff;
// Modal 창 브라우저 가운데로 조정
position: absolute;
left: 50%;
top:50%;
transform: translate(-50%, -50%);
z-index:100;
.modal__button{
position: relative;
left: 50%;
top:50%;
transform: translate(-50%, -50%);
}
}
}
버튼을 눌렀을 때 모달창을 켜고, 끄는 것 까지는 아무래도 무사히 되었습니다. 하지만 그냥 만족할만큼 되면 개발이 아니지요. Modal 컴포넌트 창안에 모달창을 여는 핸들러를 선언해서 버튼을 누를때마다 true에서 false로 false에서 true로 바뀌게는 했지만
<ModalContainer >
<ModalBtn onClick = {openModalHandler} >
{isOpen ? 'Opened!' : 'Open Modal'}
{isOpen && <ModalBackdrop>
<ModalView>
<ModalViewButton onClick = {openModalHandler}>
X
</ModalViewButton>
"Hello states"
</ModalView>
</ModalBackdrop>}
</ModalBtn>
</ModalContainer>
기존에 밑으로 줄줄이 달아놓으니, 문제가 생겼던 거 같습니다.
그래서 버튼을 끝내고 별개의 것으로 취급하니 해결되었습니다.
찾아보니 '이벤트 버블링'으로 인한 문제 였습니다. 한 요소에 이벤트가 발생하면 그 부모의 이벤트도 발생한 다는 것입니다.
이럴경우 저처럼 분리시키던지 이벤트가 발생한 부분에 {e => e.openModalHandler}로 바꾸면 이러한 문제를 막을 수 있습니다.
<ModalContainer >
<ModalBtn onClick = {openModalHandler} >
{isOpen ? 'Opened!' : 'Open Modal'}
</ModalBtn>
{isOpen && <ModalBackdrop>
<ModalView>
<ModalViewButton onClick = {openModalHandler}>
X
</ModalViewButton>
"Hello states"
</ModalView>
</ModalBackdrop>}
</ModalContainer>
padding과 border의 중요성을 세삼 알게되었습니다.
const ModalContainer = styled.div`
text-align: center; //가운데 정렬
margin: 125px
// 125px 만큼 여분
// auto;auto : 가로 중앙에 배치한다는 뜻, 그리고 자연스럽게 좌우 여백은 균등하게 배분
width: 100%; //영역을 다루는게 아직 미숙하다...
height: 5rem;
`;
const ModalBackdrop = styled.div`
// TODO : Modal이 떴을 때의 배경을 깔아주는 CSS를 구현합니다.
top: 0;
right: 0;
bottom: 0;
left: 0;
position: fixed;
//position : absolute 원래 위치와 상관없이 위치를 지정, 가장 가까운 상위 요소를 기준으로 위치가 결정
//position : fixed 원래 위치와 상관없이 위치를 지정, 상위요소에 영향받지않기 떄문에 화면이 바뀌더라도 고정된 위치를 설정할 수 있다
background-color: rgba(0, 0, 0, 0.6);
`;
const ModalView = styled.div.attrs(props => ({
role: 'dialog'
}))`
width: 300px;
height: 300px;
border-radius: 10px; // 모서리 깍기
background-color: white;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%); // 가운데 정렬하는 또 다른 방법
font-size: 2em;
color: black
`;
https://dkmqflx.github.io/frontend/2021/04/26/react-modal-close/