본문 바로가기
일상이 개발

React 모달 시스템 설계 가이드 – 전역 Context와 다이얼로그 관리 전략

by 디어노미 2025. 4. 13.
반응형

🪟 React에서 모달/다이얼로그 시스템 설계 및 Context 관리 전략

React 앱을 개발하다 보면 거의 반드시 등장하는 UI 요소가 있습니다. 바로 모달(Modal) 또는 다이얼로그(Dialog)입니다.
경고창, 설정창, 상세정보 보기 등 다양한 목적의 모달은 그 자체로도 중요하지만, 더 중요한 건 “어떻게 관리할 것인가”입니다.

이번 글에서는 다음과 같은 상황을 해결할 수 있는 전략을 중점적으로 다룹니다

  • 전역에서 모달을 띄우고 닫을 수 있는 구조
  • 여러 종류의 모달을 동적으로 다루는 방법
  • Context와 Portal을 활용한 확장 가능한 설계

🎯 1. 왜 전역 모달 관리가 필요한가?

기본적인 모달은 보통 아래와 같이 작성됩니다:

{`{isOpen &&  setOpen(false)} />} `}

하지만 다음과 같은 문제점이 발생합니다:

  • 모달을 띄우기 위한 isOpen 상태가 각 페이지마다 분산됨
  • 중첩된 모달이나 다이얼로그가 생기면 관리가 복잡해짐
  • 컴포넌트 간의 깊은 props 전달 필요 (props drilling)

이럴 때는 전역 모달 컨텍스트를 만들어

언제 어디서든 모달을 띄우고 닫을 수 있는

구조로 관리하는 것이 훨씬 유리합니다.


🧱 2. 전역 모달 시스템의 핵심 구조

전역 모달 시스템의 핵심은 다음 3가지입니다:

  1. ModalContext: 현재 열린 모달과 제어 함수들을 담은 전역 상태
  2. ModalProvider: Context를 감싸고 모달을 Portal로 렌더링
  3. useModal Hook: 컴포넌트 어디서든 모달을 띄울 수 있도록 제공

1) Context 정의

{`const ModalContext = React.createContext(null);`}

2) Provider 구현

{`function ModalProvider({ children }) {
  const [modal, setModal] = useState(null);

  const openModal = (Component, props) => {
    setModal({ Component, props });
  };

  const closeModal = () => {
    setModal(null);
  };

  return (
    
      {children}
      {modal && (
        
          
        
      )}
    
  );
}`}

3) useModal Hook

{`function useModal() {
  const context = useContext(ModalContext);
  if (!context) throw new Error('ModalProvider 내부에서 사용하세요');
  return context;
}`}

4) Portal 구현 (예: 모달 root에 랜더링)

{`const ModalPortal = ({ children }) => {
  const el = document.getElementById('modal-root');
  return createPortal(children, el);
};`}

이 구조를 기반으로, 어떤 컴포넌트에서도 다음처럼 사용할 수 있습니다:

{`const { openModal } = useModal();

openModal(MyDialog, { title: '알림', message: '정말 삭제하시겠습니까?' });`}

📦 3. 다이얼로그 종류가 많을 때는?

복잡한 앱에서는 Alert, Confirm, Custom 등 다양한 다이얼로그가 필요합니다.
이때는 모달 이름과 매핑된 컴포넌트를 관리하는 방식으로 확장할 수 있습니다.

모달 등록 방식 예시

{`const MODAL_COMPONENTS = {
  'ALERT_MODAL': AlertModal,
  'CONFIRM_MODAL': ConfirmModal,
  'FORM_MODAL': FormModal,
};`}
{`const openModal = (modalName, props) => {
  const Component = MODAL_COMPONENTS[modalName];
  if (!Component) return;
  setModal({ Component, props });
};`}

이렇게 하면 문자열 키만 넘겨도 원하는 모달을 띄울 수 있어 유지보수성과 가독성이 크게 향상됩니다.


🧠 4. 모달 속 상태/로직은 어디서 관리할까?

모달 안에서 폼을 작성하거나 API를 호출하는 경우도 많습니다.
이때 상태를 모달 외부에서 관리하면 너무 복잡해지고, 내부에서만 관리하면 재사용성이 떨어집니다.

따라서 다음 기준으로 판단하는 것이 좋습니다:

  • 다시 열 때 초기화되어야 하는 상태: 모달 내부 useState
  • 부모 컴포넌트와 동기화돼야 하는 상태: Props로 전달
  • 다수 모달 간 공유해야 하는 상태: 전역 상태관리 (예: Redux, Zustand)

예시

{`openModal(EditProfileModal, { userId: 123, onComplete: refetchUser });`}

→ 부모에서 필요한 액션은 prop으로 전달하고, 나머지는 내부에서 처리하는 방식이 적절합니다.


🧪 5. 모달 테스트와 UX 향상 팁

모달도 결국 하나의 UI 컴포넌트입니다. 따라서 다음과 같은 UX 요소를 고려하는 것이 좋습니다:

  • ESC 키로 닫기
  • 바깥 영역 클릭 시 닫기
  • 포커스 트랩: 모달 내 포커스만 이동하게
  • ARIA 속성: 접근성 향상

예를 들어 ESC 키 닫기 기능은 아래처럼 구현할 수 있습니다:

{`useEffect(() => {
  const onKeyDown = (e) => {
    if (e.key === 'Escape') onClose();
  };
  window.addEventListener('keydown', onKeyDown);
  return () => window.removeEventListener('keydown', onKeyDown);
}, []);`}

✅ 마무리 요약

React에서 모달 시스템을 제대로 설계하면 UI의 유연성과 유지보수성이 크게 향상됩니다.
특히 중대형 프로젝트일수록 전역 Context + Portal 기반 설계는 필수가 됩니다.

전략 핵심 요약
전역 관리 Context + Provider로 전역 모달 상태 관리
Portal 활용 DOM 최상단에 렌더링해 위치 제어
유연한 호출 openModal(Component, props) 또는 openModal(key, props)
상태 관리 전략 Props vs 내부 State vs 전역 상태 구분
UX 고려 ESC, 배경 클릭 닫기, 포커스트랩, 접근성

모달 시스템은 단순 UI가 아니라 앱의 중앙 제어 센터처럼 다뤄야 합니다.
이 글을 바탕으로 여러분만의 견고한 모달 시스템을 구축해보세요! 🚀

반응형