시작하기

빈 React 앱에서 시작해, 사용자의 선택을 모달을 연 코드로 돌려주는 모달까지 만들어 봅니다. 5분 정도 걸립니다.

요구 사항

React Snappy Modal은 React 외에 런타임 의존성이 없습니다. 패키지는 React 16.12, 17, 18을 peer dependency로 선언하며, 이 문서 사이트는 React 19에서 사용하고 있습니다.

모달은 SnappyModal.show()로 열고 Promise를 완료해 닫으므로 async와 await에 익숙하면 좋습니다. TypeScript 타입 선언이 패키지에 포함되어 있어 별도의 @types 패키지가 필요 없습니다.

1. 패키지 설치

사용하는 패키지 매니저로 설치합니다. 스타일시트가 패키지에 포함되어 있고 react-snappy-modal을 import하면 자동으로 적용되므로 따로 추가할 CSS 파일이 없습니다.

bash
1npm install react-snappy-modal
2# or
3yarn add react-snappy-modal
4# or
5pnpm add react-snappy-modal

2. SnappyModalProvider 추가

SnappyModalProvider는 열린 모달이 렌더링되는 위치입니다. 앱의 루트 근처에 한 번만 배치하세요. 모달은 Provider의 children 다음에 렌더링되므로 페이지 위에 표시됩니다.

그다음부터는 컴포넌트, 이벤트 핸들러, React 밖의 일반 유틸 함수 등 어디서든 SnappyModal.show()를 호출할 수 있습니다.

main.tsxtsx
1import { createRoot } from 'react-dom/client';
2import { SnappyModalProvider } from 'react-snappy-modal';
3import App from './App';
4
5createRoot(document.getElementById('root')!).render(
6  <SnappyModalProvider>
7    <App />
8  </SnappyModalProvider>,
9);

3. 모달 컴포넌트 작성

모달은 평범한 React 컴포넌트입니다. 컴포넌트 안에서 useCurrentModal()을 호출해 resolveModal을 얻습니다. resolveModal(value)를 호출하면 바로 이 모달이 닫히고 value가 모달을 연 코드로 전달됩니다.

라이브러리는 콘텐츠 위치와 배경 딤만 처리하고, 콘텐츠에 배경색·여백·테두리를 넣지 않습니다. 아래 예제처럼 카드 스타일은 직접 지정하세요.

ChooseColor.tsxtsx
1import { useCurrentModal } from 'react-snappy-modal';
2
3export function ChooseColor() {
4  const { resolveModal } = useCurrentModal();
5
6  return (
7    <div style={{ background: 'white', padding: 24, borderRadius: 12 }}>
8      <h2>Pick a color</h2>
9      <button onClick={() => resolveModal('red')}>Red</button>
10      <button onClick={() => resolveModal('blue')}>Blue</button>
11      <button onClick={() => resolveModal(null)}>Cancel</button>
12    </div>
13  );
14}

4. 모달을 열고 결과 기다리기

SnappyModal.show(element, options?)는 요소를 렌더링하고 Promise를 반환합니다. 함수는 모달이 닫힐 때까지 await에서 멈췄다가 결과를 받아 이어서 실행됩니다. isOpen 상태나 콜백 prop을 연결할 필요가 없습니다.

App.tsxtsx
1import { useState } from 'react';
2import SnappyModal from 'react-snappy-modal';
3import { ChooseColor } from './ChooseColor';
4
5export default function App() {
6  const [color, setColor] = useState('none');
7
8  const pickColor = async () => {
9    const result = await SnappyModal.show(<ChooseColor />);
10    // Runs only after the modal is closed
11    if (result) setColor(result);
12  };
13
14  return <button onClick={pickColor}>Selected: {color}</button>;
15}

Promise가 반환하는 값

결과는 모달이 닫힌 방식에 따라 달라집니다.

모달이 닫힌 방식결과
resolveModal(value) 또는 SnappyModal.close(value)value로 resolve
배경 클릭 (allowOutsideClick이 기본값 true일 때)undefined로 resolve
rejectModal(error) 또는 SnappyModal.throw(error)error로 reject되므로 try/catch로 처리

동작 원리

내부적으로 한 번의 호출은 네 단계를 거칩니다.

  • show()가 모달을 내부 스택에 추가하고 고유한 modalId를 부여한 뒤 대기 중인 Promise를 반환합니다.
  • SnappyModalProvider가 다시 렌더링되며 스택의 모든 모달을 layer 순서대로 그립니다.
  • 컴포넌트가 resolveModal()을 호출하면 모달이 스택에서 제거되고 Promise가 완료됩니다.
  • await 다음 코드가 결과를 받아 실행됩니다.

문제 해결

설정 문제는 대부분 아래 경우 중 하나입니다.

증상원인과 해결
아무것도 표시되지 않고 await 다음 코드가 실행되지 않음SnappyModalProvider가 마운트되지 않았습니다. 화면에 있는 트리를 Provider가 감싸고 있는지 확인하세요. 모달이 닫힐 때까지 Promise는 대기 상태로 남습니다.
Error: useCurrentModal must be used within CurrentModalProvideruseCurrentModal()은 SnappyModal.show()로 렌더링된 컴포넌트 안에서만 동작합니다. 훅을 모달 컴포넌트 안으로 옮기거나, 바깥에서는 SnappyModal.close(value)로 닫으세요.
모달 콘텐츠가 투명함콘텐츠 래퍼는 위치만 담당합니다. 루트 요소에 배경과 여백을 지정하거나 옵션으로 className 또는 style을 전달하세요.
모달이 sticky 헤더 아래에 표시됨모달은 기본적으로 z-index: 1000을 사용합니다. 더 큰 zIndex 옵션을 전달하거나 다른 요소의 z-index를 낮추세요.
모달 뒤 페이지가 스크롤되지 않음allowScroll은 기본값이 false라서 모달이 열려 있는 동안 페이지를 고정하고, 닫힐 때 스크롤 위치를 복원합니다. 페이지를 가리지 않는 패널이라면 allowScroll: true를 전달하세요.
다음API 레퍼런스 →