React로 모달을 구현할 때 사용되는 핵심 개념들에 대해 자세히 알아보겠습니다.
모달 컴포넌트를 만들 때 createContext, useContext, createPortal 같은 API들이 왜 필요하고 어떻게 작동하는지 알아보겠습니다.
createContext는 컴포넌트 트리 전체에서 데이터를 공유할 수 있게 해주는 React API입니다.
const ModalContext = createContext({
isOpen: false,
onClose: () => {},
});
이 코드는 모달의 상태(isOpen)와 닫는 함수(onClose)를 담는 컨텍스트를 생성합니다.
기본값은 모달이 닫혀있고, onClose 함수는 아무 동작도 하지 않는 빈 함수입니다.
Provider는 Context의 값을 자식 컴포넌트들에게 제공합니다.
<ModalContext.Provider value={{ isOpen, onClose }}>
{children}
</ModalContext.Provider>
이렇게 하면 Provider 내부의 모든 컴포넌트가 isOpen과 onClose 값에 접근할 수 있게 됩니다.
자식 컴포넌트에서 Context 값을 사용하려면 useContext 훅을 사용합니다.
const { onClose } = useContext(ModalContext);
이 코드는 ModalContext에서 onClose 함수를 가져옵니다. 이렇게 하면 props drilling(여러 계층을 통해 props를 전달하는 것) 없이도 필요한 데이터에 접근할 수 있습니다.
모달은 보통 페이지의 최상위에 렌더링되어야 합니다.
createPortal을 사용하면 컴포넌트를 DOM 트리의 다른 위치에 렌더링할 수 있습니다.
return createPortal(
<div className="modal-overlay">
<div className="modal-content">
{children}
</div>
</div>,
document.body
);
이 코드는 모달 내용을 현재 컴포넌트 위치가 아닌, document.body 바로 아래에 렌더링합니다. 이렇게 하면:
전체 흐름을 살펴보면:
isOpen과 onClose props를 받습니다.isOpen이 true면 Portal을 사용해 모달 내용을 body에 렌더링합니다.isOpen과 onClose 값을 자식 컴포넌트들에게 제공합니다.useContext로 이 값들에 접근합니다.useContext로 가져온 onClose 함수를 호출해 모달을 닫습니다.이 모달은 컴파운드 컴포넌트 패턴을 사용합니다.
여러 관련 컴포넌트가 하나의 그룹으로 작동하면서도 개별적으로 조작할 수 있게 해주는 패턴입니다.
<Modal isOpen={isOpen} onClose={onClose}>
<Modal.Header>제목</Modal.Header>
<Modal.Body>내용</Modal.Body>
<Modal.Footer>
<Modal.CancelButton onClick={handleCancel} />
<Modal.ConfirmButton onClick={handleConfirm}>확인</Modal.ConfirmButton>
</Modal.Footer>
</Modal>
이 패턴의 장점은:
모달이 필요한 여러 상황에서 이 컴포넌트를 활용할 수 있습니다:
// 확인 모달
<Modal isOpen={isConfirmOpen} onClose={closeConfirmModal}>
<Modal.Header>확인</Modal.Header>
<Modal.Body>변경사항을 저장하시겠습니까?</Modal.Body>
<Modal.Footer>
<Modal.CancelButton onClick={() => {}} />
<Modal.ConfirmButton onClick={handleSave}>저장</Modal.ConfirmButton>
</Modal.Footer>
</Modal>
// 삭제 모달
<Modal isOpen={isDeleteOpen} onClose={closeDeleteModal}>
<Modal.Header>삭제 확인</Modal.Header>
<Modal.Body>정말 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.</Modal.Body>
<Modal.Footer>
<Modal.CancelButton onClick={() => {}} />
<Modal.ConfirmButton onClick={handleDelete} variant="danger">
삭제
</Modal.ConfirmButton>
</Modal.Footer>
</Modal>
React의 Context API와 Portal을 사용하면 유연하고 재사용 가능한 모달 컴포넌트를 만들 수 있습니다. 컴파운드 컴포넌트 패턴을 통해 사용하기 쉬우면서도 높은 커스터마이징이 가능한 컴포넌트를 구현할 수 있습니다.
이 패턴을 활용하면 모달뿐만 아니라 드롭다운, 탭, 아코디언 등 다양한 복합 컴포넌트도 효과적으로 구현할 수 있습니다.