이전에 일할땐 레이어 팝업, 팝업, 모달창을 모조리 섞어 써서 머리가 박살날뻔햇는데 다시 또 모달을 만들 생각에 기분이 좋아지기 시작했다.
여기서 잠깐 정리를 하자면
1) 팝업창
팝업창이란 현재 열려있는 브라우저 페이지에 또다른 브라우저 페이지를 띄우는 것이다.
창 위에 또다른 창이 뿅하고 있는거지.
브라우저에서 이 창을 열고 닫을수있다.
2) 모달창
모달창은 기존의 브라우저 페이지 위에 새로운 윈도우 창이 아닌 레이어를 까는 것을 말한다.
모달창은 제거를 하지 않고도 페이지를 이동하면 같이 사라지고
기존의 페이지와 부모-자식 관계를 갖는다.
그래서 나는 새로 창을 하나 띄우는거보다 모달창을 하나 만들면 아주 손쉽게 끝내 버릴 수 있을거 같아 모달창을 구현하기로 했다.
나는 아주 즐거운 마음으로 Material-ui의 modal부분을 봤다
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Modal from '@material-ui/core/Modal';
function rand() {
return Math.round(Math.random() * 20) - 10;
}
function getModalStyle() {
const top = 50 + rand();
const left = 50 + rand();
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
};
}
const useStyles = makeStyles((theme) => ({
paper: {
position: 'absolute',
width: 400,
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
export default function SimpleModal() {
const classes = useStyles();
// getModalStyle is not a pure function, we roll the style only on the first render
const [modalStyle] = React.useState(getModalStyle);
const [open, setOpen] = React.useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const body = (
<div style={modalStyle} className={classes.paper}>
<h2 id="simple-modal-title">Text in a modal</h2>
<p id="simple-modal-description">
Duis mollis, est non commodo luctus, nisi erat porttitor ligula.
</p>
<SimpleModal />
</div>
);
return (
<div>
<button type="button" onClick={handleOpen}>
Open Modal
</button>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
>
{body}
</Modal>
</div>
);
}
이걸 어떻게 container presenter부분으로 나누지?
보자마자 숨쉬기가 힘들었다.
자 하지만 늘 내가 말하는것이 뭐다? 차근차근 천천히 하면 뭐든 할 수 있다.
일단 위에 부분을 보자면 함수 부분들과 스타일 부분으로 크게 나눌 수 있을 것 같다. 그리고 스타일은 나중에 styled.js로 따로 정의 할 예정이니 위에서 import한 스타일 관련 함수들은 없어도 무방할것이다.
그렇다면 container에 들어갈 녀석들과 presenter에 들어갈 녀석들을 쉽게 찢어 버릴 수 있다.
//container
export default function CommentPop() {
return (
<CommentPopUI
/>
);
}
.....
//presenter
const CommentPopUI = () => {
<Dialog style={{height:'600px'}}
//open={true}
// TransitionComponent={Transition}
// keepMounted
onClose={handleClose}
fullWidth
maxWidth="lg"
>
//내부 스타일
</Dialog>
}
이렇게 큰 틀로 나누어 보자.
부연 설명을 하자면
container 부분에선 댓글 수정에 필요한 state들과 쿼리문, 함수들이 존재 할것이고
presenter 부분은 dialog자체는 material-ui에서 정의 한 모달창의 기본 창 모습일 것이니 그 부분은 반드시 살리고
dialog 내부에 원하는 html의 모습을 style로 정의 하고 가져오면 될것이다.
자 지금 한 작업은 페이지의 동작과 페이지의 모습을 나눈것 뿐이다.
따라서 '모달 호출' '모달 닫기' 부분을 정의를 해줘야 한다.
위에서 가져온 modal부분을 잠시 유심히 보면
const [open, setOpen] = React.useState(false);
부분이 존재한다.
즉 setState를 통해 초기 상태를 false로 만들고 그것의 참, 거짓에 따라 Dialog를 보여줄지 안보여줄지를 정의 할 수 있다.
그렇다면 미리 해당 댓글 수정용 Modal을 켜두고, 그 댓글을 수정하는 요청을 누를 때만 모달창을 보여주고, 해당 댓글의 id를 가져오면 될것이다.
그렇다면 순서도가 다음과 같을것이다
댓글 창 - 댓글 모달창 컨테이너로 값을 넘김 - 모달창 컨테이너가 모달창 presenter로 값을 넘김
그렇다면 댓글창 container에선 모달창을 열고 닫는 state구문과 닫아주는 함수.
댓글창 presenter에선 정의 한 모달창을 불러오고 그것들을 연동 시켜주면 될것이다.
모달의 상태를 open ={open}
상태로 두지 말고 일단 항상 띄어져 있게 하기 위해 open={true}
상태로 둔다.
그렇다면 닫아주는 행위는 부모 컨테이너인 댓글창에서 해줘야 하기 때문에 handleClose
부분을 따로 부모 컨테이너 댓글창으로 넘겨주고 그것을 매개변수로 넘겨주면 될것이다.
//container
export default function CommentPop({handleClose}) {
return (
<CommentPopUI
/>
);
}
.....
//presenter
const CommentPopUI = ({handleClose}) => {
<Dialog style={{height:'600px'}}
open={true}
// TransitionComponent={Transition}
// keepMounted
onClose={handleClose}
fullWidth
maxWidth="lg"
>
//내부 스타일. 즉 모달창의 모양을 정의할 html요소들
</Dialog>
}
presenter
와 container
에 매개변수로 담아주자
왜 open이 항상 true상태인지는 아래에서 설명하겠다.
그리고 댓글창 부분에서 handleClose를 붙여 넣고, open의 state도 정의 하여 댓글창에서 handleClose를 통해 Open의 State를 조정할 수 있도록 정의 하고, 이를 props를 통해 넘겨주자
//BoardComment.contianer
const [open, setOpen] = useState(false);
....
const handleClose = () => {
setOpen(false);
}
...
return(
<BoardCommentUI
data = {data}
hadleClickPostComment = {hadleClickPostComment}
handleOnChange = {handleOnChange}
.....
modalComment = {modalComment}
open = {open}/>
)
}
그리고 마지막으로 presenter에서 해당 모달창을 위에서 open = {true}
상태로 둔 이유는 container부분에서 State상태에 따라 해당 창을 보여주거나 닫아주기 위해 두었다.
자 그렇다면 && 연산자로 현재 container에서 const [open, SetOpen] = useState(false)
인 상태가 true가 된다면, modal창이 뜰것이다.
//container에 정의된 handleClose
const handleClose = () => {
setOpen(false);
router.push(`/board/${routeChange}`);
}
위에서 봣던 handleClose에 open상태를 false로 바꾸고, router를 통해 해당 글을 완성한 부분으로 이동시켜준다면. 게임 오버다.
물론 현재는 테스트 코드라 글의 id를 통해 이동하지만, 나중에는 글의 번호로 이동하도록 리팩토링이 필요하다.
중요한점:
1. Dialog로 api를 가져왓다. 나중에는 직접 짜봐야할것같다.
2. 조금 매끄럽지 않은 부분들이 있다. 댓글을 마무리 한 후 끝내야할것같다.
3. 모달창을 Container, Presenter로 나누는 작업을 좀더 연습해야한다.