역할
`Modal`은 화면 위에 임시 작업 흐름을 띄울 때 사용하는 공통 컴포넌트입니다. 삭제 확인, 그룹 추가, 이미지 선택, 설정 저장 확인처럼 현재 페이지의 맥락은 유지하면서 사용자의 결정을 받아야 할 때 사용합니다.
페이지마다 독자적인 fixed layer를 만들면 overlay 색상, 닫기 방식, z-index, 스크롤 잠금이 서로 달라집니다. 그래서 코어 모달을 기준으로 맞추고, 내부 내용만 각 기능에서 구성하는 것이 좋습니다.
src/core/components/modal/Modal.tsxsrc/core/components/modal/ModalPortal.tsx기본 사용
모달은 부모 컴포넌트에서 열린 상태를 갖고, `close` 함수로 닫힌 상태를 다시 받습니다. 내부 버튼에서 닫아야 할 때는 `useModal()`을 사용하면 props를 여러 단계로 넘기지 않아도 됩니다.
"use client";
import { useState } from "react";
import Button from "@components/button/Button";
import Modal, { useModal } from "@components/modal/Modal";
const ModalContent = () => {
const modal = useModal();
return (
<div className="p-6">
<button type="button" onClick={modal.close}>닫기</button>
</div>
);
};
const Example = () => {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>모달 열기</Button>
<Modal state={open} close={setOpen} size="md" position="center">
<ModalContent />
</Modal>
</>
);
};위치와 크기
`position`은 사용자의 시선과 작업 성격에 맞춰 선택합니다. 단순 확인은 `center`, 상단 알림성 작업은 `top`, 모바일에서 아래에서 올라오는 선택 UI는 `bottom`이 어울립니다. 크기는 내부 콘텐츠 양에 맞춰 최소한으로 잡는 편이 좋습니다.
position="center"position="top"position="bottom"size="sm"size="md"size="lg" 이상닫기 정책
기본적으로 ESC와 overlay 클릭으로 닫을 수 있습니다. 하지만 저장 중인 form, 필수 입력이 있는 관리자 작업, 실수로 닫히면 작성 내용이 사라지는 화면에서는 `overlayClose=` 또는 `escClose=`를 고려해야 합니다.
<Modal
state={open}
close={setOpen}
size="md"
position="center"
escClose={false}
overlayClose={false}
>
<RequiredForm />
</Modal>Props
stateclosesizepositionescCloseoverlayoverlayClose적용 기준
모달 안에서도 버튼, 입력 필드, fieldErrors 표시 방식은 기존 공통 컴포넌트를 사용합니다. 특히 관리자 그룹 추가처럼 validation 실패가 발생하는 화면은 목록에 Alert를 띄우기보다 모달 안의 해당 input 바로 아래에 오류를 보여주는 편이 자연스럽습니다.
내용이 길어지는 모달은 내부에 스크롤 가능한 영역과 하단 고정 버튼 영역을 분리해야 합니다. 저장 버튼이 화면 밖으로 밀려나면 사용자가 작업을 끝낼 수 없기 때문입니다.