-
Notifications
You must be signed in to change notification settings - Fork 3
useModal
Choi Jeongmin edited this page Dec 1, 2024
·
1 revision
개발 중 모달을 보다 쉽게 제어하고 활용할 수 있도록 만든 과정을 기록합니다.
커스텀 훅을 통해 모달을 제어하여, 모달의 생성부터 제거까지 간단하게 사용할 수 있도록 한 내용을 포함하고 있습니다.
‘Ask-It' 에서는 UI에서 모달을 통해 다양한 상호작용을 하고 있습니다. 사용자들에게 적절한 피드백을 제공하거나 추가적인 정보를 요구하는 등 다양한 상황에서 모달이 활용됩니다.
모달 사용이 빈번할 것으로 예상되는 상황에서, 효율적으로 모달을 활용하기 위한 고민이 필요했습니다. 복잡한 모달 로직을 매번 개별 컴포넌트에 구현하는 대신, 모달을 손쉽게 관리하고 재사용할 수 있는 접근 방식이 필요했습니다.
요구 사항은 아래와 같습니다.
- 다양한 UI를 가진 컴포넌트들이 모달로 쉽게 사용될 수 있어야 한다.
- 모달이 표시될 때, 추가적인 요소들이 별도의 설정 없이 자동으로 적용되어야 한다 (예: 오버레이).
위 요구 사항을 충족하면서 모달의 사용을 간편하게 할 수 있는 무언가가 필요했습니다.
모달의 복잡한 로직을 간소화하고, 다른 컴포넌트들이 쉽게 사용할 수 있도록 커스텀 훅을 만들었습니다.
- 모달의 상태를 관리하기 위해 내부적으로
useState
훅을 사용하여 모달의 열림/닫힘 상태를 제어합니다. -
openModal
,closeModal
함수를 통해 모달을 여닫는 기능을 제공하고, 모달을 열 때마다 별도의ModalContext
를 통해 모달 내부에서 상태에 쉽게 접근하고 조작할 수 있도록 했습니다.
graph TD;
A[Component] -->|useModal Hook| B[Modal State];
B --> C{isOpen};
C -->|True| D[Render Modal];
C -->|False| E[Do Nothing];
D --> F[ModalContext];
F --> G[Background & Content];
아래는 핵심 부분의 코드입니다.
// modal.context.ts
import { createContext } from 'react';
export interface ModalContextProps {
openModal: () => void;
closeModal: () => void;
}
export const ModalContext = createContext<ModalContextProps | undefined>(
undefined,
);
// modal.hook.tsx
import { ReactNode, useContext, useMemo, useState } from 'react';
import { createPortal } from 'react-dom';
import Background from '@/features/modal/Background';
import { ModalContext } from '@/features/modal/modal.context';
export const useModal = (children: ReactNode) => {
const [isOpen, setIsOpen] = useState(false);
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);
const contextValue = useMemo(() => ({ openModal, closeModal }), []);
const Modal = useMemo(() => {
if (!isOpen) return null;
return createPortal(
<ModalContext.Provider value={contextValue}>
<Background>{children}</Background>
</ModalContext.Provider>,
document.body,
);
}, [isOpen, children, contextValue]);
return {
Modal,
openModal,
closeModal,
};
};
export const useModalContext = () => {
const context = useContext(ModalContext);
if (!context) {
throw new Error('useModalContext must be used within a ModalProvider');
}
return context;
};
-
createPortal
을 사용하여 모달 컴포넌트를document.body
에 렌더링함으로써, 모달의 DOM 계층 구조와 상관없이 최상위에 표시합니다. -
ModalContext.Provider
를 통해 모달 내부의 컴포넌트들이 모달을 쉽게 닫을 수 있도록 컨텍스트를 활용했습니다.
- 모달 로직을 여러 컴포넌트에서 반복적으로 구현하는 대신, 커스텀 훅을 사용함으로써 재사용 가능한 코드로 만들 수 있었습니다. 이를 통해 코드의 중복을 줄일 수 있었습니다.
- 커스텀 훅을 통해 모달의 상태 관리를 모달을 사용하는 각 UI 컴포넌트 외부로 분리함으로써, 해당 컴포넌트들의 복잡성을 줄이고 상태 관리의 책임을 명확하게 분리할 수 있었습니다.
-
useModal
훅을 통해 모달을 쉽게 열고 닫을 수 있는 함수를 제공함으로써, 모달을 사용하는 컴포넌트에서의 코드가 간결해졌습니다. 복잡한 상태 관리 로직 없이 간단하게 훅 하나로 해결할 수 있어 개발 시간을 단축할 수 있었습니다. - 모달이 열릴 때 오버레이를 자동으로 추가하고, 모달이 닫히는 액션이나 상태를 일관되게 관리함으로써 예측 가능한 상태를 유지할 수 있었습니다.