2020.02.12
이후에 프로젝트를 하면서 모달창을 다시 만들 기회가 생겼다.
이 포스팅에서 작성한 코드에서 수정해 조금 다르게 만들었다.
👉포스팅 보러가기
nav컴포넌트의 버튼을 클릭하면 모달창이 떠야했다.
그런데 모달창이 뜨는 위치에 대한 고민이 생겼다.
별도로 모달창 컴포넌트를 만들었더라도 렌더링하는 위치를 지정해주어야 하는데,
실제로 모달창은 전역 위치에서 보여주는 것인데... 특정 컴포넌트의 하위에서 호출해 렌더링하는 것이 맞는 건지, 더 좋은 방법이 있을지 궁금했다.
그리고 찾아보다가 ReactDom에서 제공하는 Portal이라는 기능을 발견했고 한번 사용해보기로 결정했다.
부모 컴포넌트의 돔 계층구조 바깥에 있는 돔 노드로 자식을 렌더링하는 기능
즉, 외부에 존재하는 돔 노드가 안에 존재하는 것처럼 연결해주는 포탈 기능 제공
컴포넌트를 부모 컴포넌트의 계층 구조 바깥에 있는 돔노드로 자식을 렌더링해준다.
<body>
<div id="root"></div>
<div id="modal"></div>
</body>
이 둘은 형제 관계처럼 보이지만 modal은 root내부에 있는 자식 컴포넌트이고, 렌더링만 portal을 통해 바깥에서 이루어지는 것이다.
컴포넌트, 요소: 렌더링 가능한 리액트 자식요소
컴포넌트를 넣어줄 DOM 요소
일반적으로 컴포넌트의 render 메서드에서 요소를 반환하면 가장 가까운 상위 노드의 자식으로 DOM에 마운트 된다.
import ReactDOM from 'react-dom';
function ModalPortal({ children }) {
const el = document.getElementById('modal');
return ReactDOM.createPortal(children, el);
}
export default ModalPortal;
ModalPortal이라는 이름으로 React.Portal을 생성하는 함수형 컴포넌트를 만들었다.
const modalTitle = {
location: '어디로 떠날까요?',
date: '언제 떠날까요?',
date_detail: '예약 날짜를 선택해주세요.',
};
const modalButton = {
location: 'SEARCH',
date: 'SEARCH',
date_detail: 'DONE',
};
const modalContent = {
location: <Location />,
date: <SelectDate />,
date_detail: <SelectDate detail={true} />,
};
console.log(modalContent[showModal]);
return (
<Wrapper>
<PopUp>
<PopUpTitle>
<h2>{modalTitle[showModal]}</h2>
<GrClose onClick={onClose} />
</PopUpTitle>
<CalendarWrapper>{modalContent[showModal]}</CalendarWrapper>
<SearchButton
diabled={!buttonIsValid}
validDates={validDates}
onClick={onClickSearch}
>
{modalButton[showModal]}
<BsArrowRight />
</SearchButton>
</PopUp>
</Wrapper>
);
}
<ModalPortal>
<Modal />
</ModalPortal>
실제로는 nav 컴포넌트에서 위 코드를 작성했는데, ModalPortal이라는 포털로 인해 하위에 위치한 Modal 컴포넌트는 Nav컴포넌트 하위가 아닌, 모든 리액트 컴포넌트들이 계층구조를 갖고 렌더링되는 root 요소도 아닌,그 옆에 위치한 modal이라는 클래스명을 가진 요소에 렌더링된다.
실제로 개발자도구에서 확인해보자
이렇게 root가 아닌 modal 요소에서 독립적으로 렌더링되는 것을 볼 수 있다.
물론, 실제로는 Nav컴포넌트 하위에 위치해 있으므로 기존과 동일하게 props를 내려받는 등의 동작을 할 수 있다.
렌더링을 전역적으로 하도록 만들었더라도, Nav컴포넌트에서 이 ModalPortal과 Modal 컴포넌트를 호출하는 것이 적절할까? 라는 생각이 들었다.
리팩토링 하면서 이 부분에 대해서는 다시 고민해봐야겠다.