React로 Modal 컴포넌트 만들기 - 1편
Modal의 정의
- 웹이나 앱 UI에서 자주 쓰이는 "팝업 대화상자" 형태의 컴포넌트.
- 사용자가 현재 보고 있는 화면 위에 오버레이 형태로 나타나며, 사용자의 주의를 집중시키거나 추가 입력을 받는 용도로 사용된다.
Modal의 동작 특성
- 사용자가 현재 페이지를 떠나게 되면 Modal 인스턴스도 같이 제거된다.
- Modal을 열면 스크롤이 잠기고 닫으면 해제된다.
- 오버레이 배경 영역을 클릭하거나 ESC키를 입력하면 닫는다.
- 하나의 Modal을 표시하고 있는 상태에서 추가로 표시하는 경우 더 높은 레이어에서 표시된다.
Modal의 구조
Modal을 만들기 위해 필요한 두 하위 컴포넌트는 ModalLayer, ModalPanel이다.
각각의 컴포넌트에 역할을 부여하여 보자.
- ModalLayer
- 페이지 콘텐트가 표시되는 루트 레이어보다 높은 레이어에 Overlay를 표시하는 역할.
- ModalPanel의 위치가 항상 화면상의 중앙에 오도록 하는 역할.
- 이 영역을 클릭하면 모달을 닫는 역할.
- ESC키를 입력하면 모달을 닫는 역할.
- ModalPanel
- Modal에 표시하고 싶은 콘텐츠를 보여주는 역할.
Modal의 마크업과 스타일링
ModalLayer 컴포넌트를 작성해보자.
우선 사용자의 화면을 꽉 채우는 하나의 불투명한 레이어가 필요하다.
ModalLayer.tsx
1import { PropsWithChildren } from 'react';
2import { twMerge } from 'tailwind-merge';
3
4interface Props extends PropsWithChildren {
5 className?: string;
6}
7
8export default function ModalLayer({
9 className,
10 children,
11}: PropsWithChildren<Props>) {
12 return (
13 <div
14 className={twMerge(
15 'fixed inset-0 bg-black/50 flex items-center justify-center',
16 className,
17 )}
18 >
19 {children}
20 </div>
21 );
22}
ModalPanel 컴포넌트를 작성한다.
이 컴포넌트는 하얀 네모박스안에 children 으로 전달한 어떤 요소든 표시하기 시작한다.
ModalPanel.tsx
1import { PropsWithChildren } from 'react';
2
3export default function ModalPanel({ children }: PropsWithChildren) {
4 return <div className="bg-white rounded-[4px] shadow-md">{children}</div>;
5}
두 컴포넌트를 합성하여 Modal 컴포넌트로 추상화하자.
React.createPortal을 통해 document.body 하위에 Modal을 위치시키는 것이 중요하다.
왜 Portal 을 사용해야 할까? 그 이유는 다음편에서 다루도록 하자.
Modal.tsx
1import { PropsWithChildren } from 'react';
2import { createPortal } from 'react-dom';
3import ModalLayer from './ModalLayer.tsx';
4import ModalPanel from './ModalPanel.tsx';
5
6export default function Modal({ children }: PropsWithChildren) {
7 return createPortal(
8 <ModalLayer>
9 <ModalPanel>{children}</ModalPanel>
10 </ModalLayer>,
11 document.body,
12 );
13}
여기까지가 Modal 컴포넌트를 만들기위한 구조화, HTML 마크업, CSS 스타일링 과정이다.
현재까지의 구현은 렌더링만 가능하며 열고 닫거나 할 수 없는 상태이다.
다음편에서는 Modal의 동작특성을 만족하기 위해 기능을 추가하는 내용을 다룬다.