모달

모달 (Modal)

새 페이지로 이동하지 않고 현재 페이지 위에 떠 있는 레이어를 만들어 사용자 피드백을 받거나 정보를 보여 주는 컴포넌트예요. 사용자와 상호작용이 필요하지만 작업 흐름을 끊고 싶지 않을 때 써요.

출처: 문서

본문

언제 사용하나요 (When To Use)

사용자와의 상호작용이 필요하지만, 새 페이지로 이동해 사용자의 작업 흐름을 끊고 싶지 않을 때 Modal로 현재 페이지 위에 떠 있는 새 레이어를 만들어 피드백을 받거나 정보를 표시할 수 있어요.

또한, 간단한 확인 대화상자를 보여 주려면 App.useApp hooks를 사용할 수 있어요.

예시 (Examples)

기본 (Basic)

기본 모달이에요.

import React, { useState } from 'react';
import { Button, Modal } from 'antd';

const App: React.FC = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);

  const showModal = () => {
    setIsModalOpen(true);
  };

  const handleOk = () => {
    setIsModalOpen(false);
  };

  const handleCancel = () => {
    setIsModalOpen(false);
  };

  return (
    <>
      <Button type="primary" onClick={showModal}>
        Open Modal
      </Button>
      <Modal
        title="Basic Modal"
        closable={{ 'aria-label': 'Custom Close Button' }}
        open={isModalOpen}
        onOk={handleOk}
        onCancel={handleCancel}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Modal>
    </>
  );
};

export default App;

비동기 닫기 (Asynchronously close)

OK 버튼을 눌렀을 때 모달 대화상자를 비동기로 닫아요. 예를 들어 폼을 제출할 때 이 패턴을 사용할 수 있어요.

import React, { useState } from 'react';
import { Button, Modal } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);
  const [confirmLoading, setConfirmLoading] = useState(false);
  const [modalText, setModalText] = useState('Content of the modal');

  const showModal = () => {
    setOpen(true);
  };

  const handleOk = () => {
    setModalText('The modal will be closed after two seconds');
    setConfirmLoading(true);
    setTimeout(() => {
      setOpen(false);
      setConfirmLoading(false);
    }, 2000);
  };

  const handleCancel = () => {
    console.log('Clicked cancel button');
    setOpen(false);
  };

  return (
    <>
      <Button type="primary" onClick={showModal}>
        Open Modal with async logic
      </Button>
      <Modal
        title="Title"
        open={open}
        onOk={handleOk}
        confirmLoading={confirmLoading}
        onCancel={handleCancel}
      >
        <p>{modalText}</p>
      </Modal>
    </>
  );
};

export default App;

커스터마이즈된 푸터 버튼 바를 정의하는 더 복잡한 예시예요. 제출 버튼을 클릭한 뒤 대화상자가 로딩 상태로 바뀌고, 로딩이 끝나면 모달이 닫혀요.

기본 푸터 버튼이 필요 없다면 footer를 null로 설정할 수 있어요.

import React, { useState } from 'react';
import { Button, Modal } from 'antd';

const App: React.FC = () => {
  const [loading, setLoading] = useState(false);
  const [open, setOpen] = useState(false);

  const showModal = () => {
    setOpen(true);
  };

  const handleOk = () => {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
      setOpen(false);
    }, 3000);
  };

  const handleCancel = () => {
    setOpen(false);
  };

  return (
    <>
      <Button type="primary" onClick={showModal}>
        Open Modal with customized footer
      </Button>
      <Modal
        open={open}
        title="Title"
        onOk={handleOk}
        onCancel={handleCancel}
        footer={[
          <Button key="back" onClick={handleCancel}>
            Return
          </Button>,
          <Button key="submit" type="primary" loading={loading} onClick={handleOk}>
            Submit
          </Button>,
          <Button
            key="link"
            href="https://google.com"
            target="_blank"
            type="primary"
            loading={loading}
            onClick={handleOk}
          >
            Search on Google
          </Button>,
        ]}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Modal>
    </>
  );
};

export default App;

마스크 (mask)

마스크 효과예요.

import React from 'react';
import { Button, Modal, Space } from 'antd';

const modalConfig = {
  title: 'Title',
  content: 'Some contents...',
};

const App: React.FC = () => {
  const [modal, contextHolder] = Modal.useModal();

  return (
    <>
      <Space>
        <Button
          onClick={() => {
            modal.confirm({ ...modalConfig, mask: { blur: true } });
          }}
        >
          blur
        </Button>
        <Button
          onClick={() => {
            modal.confirm(modalConfig);
          }}
        >
          Dimmed mask
        </Button>
        <Button
          onClick={() => {
            modal.confirm({ ...modalConfig, mask: false });
          }}
        >
          No mask
        </Button>
      </Space>

      {contextHolder}
    </>
  );
};

export default App;

로딩 (Loading)

Modal의 로딩 상태를 설정해요.

import React from 'react';
import { Button, Modal } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = React.useState<boolean>(false);
  const [loading, setLoading] = React.useState<boolean>(true);

  const showLoading = () => {
    setOpen(true);
    setLoading(true);

    // Simple loading mock. You should add cleanup logic in real world.
    setTimeout(() => {
      setLoading(false);
    }, 2000);
  };

  return (
    <>
      <Button type="primary" onClick={showLoading}>
        Open Modal
      </Button>
      <Modal
        title={<p>Loading Modal</p>}
        footer={
          <Button type="primary" onClick={showLoading}>
            Reload
          </Button>
        }
        loading={loading}
        open={open}
        onCancel={() => setOpen(false)}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Modal>
    </>
  );
};

export default App;

원본 위에 확장할 수 있도록 푸터 렌더링 함수를 커스터마이즈해요.

import React, { useState } from 'react';
import { Button, Modal, Space } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);

  const showModal = () => {
    setOpen(true);
  };
  const handleOk = () => {
    setOpen(false);
  };

  const handleCancel = () => {
    setOpen(false);
  };
  return (
    <>
      <Space>
        <Button type="primary" onClick={showModal}>
          Open Modal
        </Button>
        <Button
          type="primary"
          onClick={() => {
            Modal.confirm({
              title: 'Confirm',
              content: 'Bla bla ...',
              footer: (_, { OkBtn, CancelBtn }) => (
                <>
                  <Button>Custom Button</Button>
                  <CancelBtn />
                  <OkBtn />
                </>
              ),
            });
          }}
        >
          Open Modal Confirm
        </Button>
      </Space>
      <Modal
        open={open}
        title="Title"
        onOk={handleOk}
        onCancel={handleCancel}
        footer={(_, { OkBtn, CancelBtn }) => (
          <>
            <Button>Custom Button</Button>
            <CancelBtn />
            <OkBtn />
          </>
        )}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Modal>
    </>
  );
};

export default App;

컨텍스트를 얻기 위한 hooks 사용 (Use hooks to get context)

Modal.useModal을 사용하면 컨텍스트에 접근할 수 있는 contextHolder를 얻을 수 있어요. Promise await 연산은 hooks 방식만 지원해요.

import React, { createContext } from 'react';
import { Button, Modal, Space } from 'antd';

const ReachableContext = createContext<string | null>(null);
const UnreachableContext = createContext<string | null>(null);

const config = {
  title: 'Use Hook!',
  content: (
    <>
      <ReachableContext.Consumer>{(name) => `Reachable: ${name}!`}</ReachableContext.Consumer>
      <br />
      <UnreachableContext.Consumer>{(name) => `Unreachable: ${name}!`}</UnreachableContext.Consumer>
    </>
  ),
};

const App: React.FC = () => {
  const [modal, contextHolder] = Modal.useModal();

  return (
    <ReachableContext.Provider value="Light">
      <Space>
        <Button
          onClick={async () => {
            const confirmed = await modal.confirm(config);
            console.log('Confirmed: ', confirmed);
          }}
        >
          Confirm
        </Button>
        <Button
          onClick={() => {
            modal.warning(config);
          }}
        >
          Warning
        </Button>
        <Button
          onClick={async () => {
            modal.info(config);
          }}
        >
          Info
        </Button>
        <Button
          onClick={async () => {
            modal.error(config);
          }}
        >
          Error
        </Button>
      </Space>
      {/* `contextHolder` should always be placed under the context you want to access */}
      {contextHolder}

      {/* Can not access this context since `contextHolder` is not in it */}
      <UnreachableContext.Provider value="Bamboo" />
    </ReachableContext.Provider>
  );
};

export default App;

국제화 (Internationalization)

버튼의 텍스트를 커스터마이즈하려면 okText와 cancelText props를 설정해야 해요.

import React, { useState } from 'react';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Button, Modal, Space } from 'antd';

const LocalizedModal = () => {
  const [open, setOpen] = useState(false);

  const showModal = () => {
    setOpen(true);
  };

  const hideModal = () => {
    setOpen(false);
  };

  return (
    <>
      <Button type="primary" onClick={showModal}>
        Modal
      </Button>
      <Modal
        title="Modal"
        open={open}
        onOk={hideModal}
        onCancel={hideModal}
        okText="确认"
        cancelText="取消"
      >
        <p>Bla bla ...</p>
        <p>Bla bla ...</p>
        <p>Bla bla ...</p>
      </Modal>
    </>
  );
};

const App: React.FC = () => {
  const [modal, contextHolder] = Modal.useModal();

  const confirm = () => {
    modal.confirm({
      title: 'Confirm',
      icon: <ExclamationCircleOutlined />,
      content: 'Bla bla ...',
      okText: '确认',
      cancelText: '取消',
    });
  };

  return (
    <>
      <Space>
        <LocalizedModal />
        <Button onClick={confirm}>Confirm</Button>
      </Space>
      {contextHolder}
    </>
  );
};

export default App;

수동 업데이트 및 파괴 (Manual to update destroy)

인스턴스를 통해 모달을 수동으로 업데이트하고 파괴해요.

import React from 'react';
import { Button, Modal } from 'antd';

const App: React.FC = () => {
  const [modal, contextHolder] = Modal.useModal();

  const countDown = () => {
    let secondsToGo = 5;

    const instance = modal.success({
      title: 'This is a notification message',
      content: `This modal will be destroyed after ${secondsToGo} second.`,
    });

    const timer = setInterval(() => {
      secondsToGo -= 1;
      instance.update({
        content: `This modal will be destroyed after ${secondsToGo} second.`,
      });
    }, 1000);

    setTimeout(() => {
      clearInterval(timer);
      instance.destroy();
    }, secondsToGo * 1000);
  };

  return (
    <>
      <Button onClick={countDown}>Open modal to close in 5s</Button>
      {contextHolder}
    </>
  );
};

export default App;

모달 위치 커스터마이즈 (To customize the position of modal)

centered, style.top 또는 다른 스타일로 모달 대화상자의 위치를 설정할 수 있어요.

import React, { useState } from 'react';
import { Button, Modal } from 'antd';

const App: React.FC = () => {
  const [modal1Open, setModal1Open] = useState(false);
  const [modal2Open, setModal2Open] = useState(false);

  return (
    <>
      <Button type="primary" onClick={() => setModal1Open(true)}>
        Display a modal dialog at 20px to Top
      </Button>
      <Modal
        title="20px to Top"
        style={{ top: 20 }}
        open={modal1Open}
        onOk={() => setModal1Open(false)}
        onCancel={() => setModal1Open(false)}
      >
        <p>some contents...</p>
        <p>some contents...</p>
        <p>some contents...</p>
      </Modal>
      <br />
      <br />
      <Button type="primary" onClick={() => setModal2Open(true)}>
        Vertically centered modal dialog
      </Button>
      <Modal
        title="Vertically centered modal dialog"
        centered
        open={modal2Open}
        onOk={() => setModal2Open(false)}
        onCancel={() => setModal2Open(false)}
      >
        <p>some contents...</p>
        <p>some contents...</p>
        <p>some contents...</p>
      </Modal>
    </>
  );
};

export default App;

okButtonProps와 cancelButtonProps를 넘기면 OK 버튼과 취소 버튼의 props를 커스터마이즈할 수 있어요.

import React, { useState } from 'react';
import { Button, Modal } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);

  const showModal = () => {
    setOpen(true);
  };

  const handleOk = () => {
    setOpen(false);
  };

  const handleCancel = () => {
    setOpen(false);
  };

  return (
    <>
      <Button type="primary" onClick={showModal}>
        Open Modal with customized button props
      </Button>
      <Modal
        title="Basic Modal"
        open={open}
        onOk={handleOk}
        onCancel={handleCancel}
        okButtonProps={{ disabled: true }}
        cancelButtonProps={{ disabled: true }}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Modal>
    </>
  );
};

export default App;

커스텀 모달 콘텐츠 렌더 (Custom modal content render)

모달 콘텐츠 렌더를 커스터마이즈해요. react-draggable을 사용해 드래그 가능하게 만듭니다.

import React, { useRef, useState } from 'react';
import { Button, ConfigProvider, Modal } from 'antd';
import type { DraggableData, DraggableEvent } from 'react-draggable';
import Draggable from 'react-draggable';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);
  const [bounds, setBounds] = useState({ left: 0, top: 0, bottom: 0, right: 0 });
  const draggleRef = useRef<HTMLDivElement>(null!);
  const { getPrefixCls } = React.useContext(ConfigProvider.ConfigContext);
  const prefixCls = getPrefixCls('modal');
  const dragHandle = `.${prefixCls}-draggable-title`;

  const showModal = () => {
    setOpen(true);
  };

  const handleOk = () => {
    setOpen(false);
  };

  const handleCancel = () => {
    setOpen(false);
  };

  const onStart = (_event: DraggableEvent, uiData: DraggableData) => {
    const { clientWidth, clientHeight } = window.document.documentElement;
    const targetRect = draggleRef.current?.getBoundingClientRect();
    if (!targetRect) {
      return;
    }
    setBounds({
      left: -targetRect.left + uiData.x,
      right: clientWidth - (targetRect.right - uiData.x),
      top: -targetRect.top + uiData.y,
      bottom: clientHeight - (targetRect.bottom - uiData.y),
    });
  };

  return (
    <>
      <Button onClick={showModal}>Open Draggable Modal</Button>
      <Modal
        title="Draggable Modal"
        open={open}
        onOk={handleOk}
        onCancel={handleCancel}
        classNames={{ title: dragHandle.slice(1) }}
        styles={{ title: { cursor: 'move' } }}
        modalRender={(modal) => (
          <Draggable
            bounds={bounds}
            nodeRef={draggleRef}
            handle={dragHandle}
            onStart={(event, uiData) => onStart(event, uiData)}
          >
            <div ref={draggleRef}>{modal}</div>
          </Draggable>
        )}
      >
        <p>
          Just don&apos;t learn physics at school and your life will be full of magic and miracles.
        </p>
        <br />
        <p>Day before yesterday I saw a rabbit, and yesterday a deer, and today, you.</p>
      </Modal>
    </>
  );
};

export default App;

모달 너비 커스터마이즈 (To customize the width of modal)

width로 모달 대화상자의 너비를 설정해요.

import React, { useState } from 'react';
import { Button, Flex, Modal } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);
  const [openResponsive, setOpenResponsive] = useState(false);

  return (
    <Flex vertical gap="medium" align="flex-start">
      {/* Basic */}
      <Button type="primary" onClick={() => setOpen(true)}>
        Open Modal of 1000px width
      </Button>
      <Modal
        title="Modal 1000px width"
        centered
        open={open}
        onOk={() => setOpen(false)}
        onCancel={() => setOpen(false)}
        width={1000}
      >
        <p>some contents...</p>
        <p>some contents...</p>
        <p>some contents...</p>
      </Modal>

      {/* Responsive */}
      <Button type="primary" onClick={() => setOpenResponsive(true)}>
        Open Modal of responsive width
      </Button>
      <Modal
        title="Modal responsive width"
        centered
        open={openResponsive}
        onOk={() => setOpenResponsive(false)}
        onCancel={() => setOpenResponsive(false)}
        width={{
          xs: '90%',
          sm: '80%',
          md: '70%',
          lg: '60%',
          xl: '50%',
          xxl: '40%',
        }}
      >
        <p>some contents...</p>
        <p>some contents...</p>
        <p>some contents...</p>
      </Modal>
    </Flex>
  );
};

export default App;

정적 메서드 (Static Method)

정적 메서드는 ConfigProvider가 제공하는 Context를 사용할 수 없어요. layer를 활성화하면 스타일 오류가 발생할 수도 있어요. hooks 버전이나 App에서 제공하는 인스턴스를 우선 사용해 주세요.

import React from 'react';
import { Button, Modal, Space } from 'antd';

const info = () => {
  Modal.info({
    title: 'This is a notification message',
    content: (
      <div>
        <p>some messages...some messages...</p>
        <p>some messages...some messages...</p>
      </div>
    ),
    onOk() {},
  });
};

const success = () => {
  Modal.success({
    content: 'some messages...some messages...',
  });
};

const error = () => {
  Modal.error({
    title: 'This is an error message',
    content: 'some messages...some messages...',
  });
};

const warning = () => {
  Modal.warning({
    title: 'This is a warning message',
    content: 'some messages...some messages...',
  });
};

const App: React.FC = () => (
  <Space wrap>
    <Button onClick={info}>Info</Button>
    <Button onClick={success}>Success</Button>
    <Button onClick={error}>Error</Button>
    <Button onClick={warning}>Warning</Button>
  </Space>
);

export default App;

정적 확인 (Static confirmation)

confirm()으로 확인 모달 대화상자를 표시해요. onCancel/onOk 함수가 promise 객체를 반환하도록 하면 대화상자 닫힘을 지연시킬 수 있어요.

import React from 'react';
import { ExclamationCircleFilled } from '@ant-design/icons';
import { Button, Modal, Space } from 'antd';

const { confirm } = Modal;

const showConfirm = () => {
  confirm({
    title: 'Do you want to delete these items?',
    icon: <ExclamationCircleFilled />,
    content: 'Some descriptions',
    onOk() {
      console.log('OK');
    },
    onCancel() {
      console.log('Cancel');
    },
  });
};

const showPromiseConfirm = () => {
  confirm({
    title: 'Do you want to delete these items?',
    icon: <ExclamationCircleFilled />,
    content: 'When clicked the OK button, this dialog will be closed after 1 second',
    onOk() {
      return new Promise((resolve, reject) => {
        setTimeout(Math.random() > 0.5 ? resolve : reject, 1000);
      }).catch(() => console.log('Oops errors!'));
    },
    onCancel() {},
  });
};

const showDeleteConfirm = () => {
  confirm({
    title: 'Are you sure delete this task?',
    icon: <ExclamationCircleFilled />,
    content: 'Some descriptions',
    okText: 'Yes',
    okType: 'danger',
    cancelText: 'No',
    onOk() {
      console.log('OK');
    },
    onCancel() {
      console.log('Cancel');
    },
  });
};

const showPropsConfirm = () => {
  confirm({
    title: 'Are you sure delete this task?',
    icon: <ExclamationCircleFilled />,
    content: 'Some descriptions',
    okText: 'Yes',
    okType: 'danger',
    okButtonProps: {
      disabled: true,
    },
    cancelText: 'No',
    onOk() {
      console.log('OK');
    },
    onCancel() {
      console.log('Cancel');
    },
  });
};

const App: React.FC = () => (
  <Space wrap>
    <Button onClick={showConfirm}>Confirm</Button>
    <Button onClick={showPromiseConfirm}>With promise</Button>
    <Button onClick={showDeleteConfirm} type="dashed">
      Delete
    </Button>
    <Button onClick={showPropsConfirm} type="dashed">
      With extra props
    </Button>
  </Space>
);

export default App;

확인 모달 대화상자 파괴 (destroy confirmation modal dialog)

Modal.destroyAll()은 모든 확인 모달 대화상자를 파괴해요. 보통 라우터 변경 이벤트에서 사용해 확인 모달을 자동으로 파괴할 수 있어요.

import React from 'react';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Button, Modal } from 'antd';

const { confirm } = Modal;

const destroyAll = () => {
  Modal.destroyAll();
};

const showConfirm = () => {
  for (let i = 0; i < 3; i += 1) {
    setTimeout(() => {
      confirm({
        icon: <ExclamationCircleOutlined />,
        content: <Button onClick={destroyAll}>Click to destroy all</Button>,
        onOk() {
          console.log('OK');
        },
        onCancel() {
          console.log('Cancel');
        },
      });
    }, i * 500);
  }
};

const App: React.FC = () => <Button onClick={showConfirm}>Confirm</Button>;

export default App;

커스텀 시맨틱 DOM 스타일 (Custom semantic dom styling)

classNames와 styles로 객체나 함수를 넘겨 Modal의 시맨틱 DOM 스타일을 커스터마이즈할 수 있어요.

import React, { useState } from 'react';
import { Button, Flex, Modal } from 'antd';
import type { GetProp, ModalProps } from 'antd';
import { createStaticStyles } from 'antd-style';

const lineStyle: React.CSSProperties = {
  lineHeight: '28px',
};

const sharedContent = (
  <>
    <div style={lineStyle}>
      Following the Ant Design specification, we developed a React UI library antd that contains a
      set of high quality components and demos for building rich, interactive user interfaces.
    </div>
    <div style={lineStyle}>🌈 Enterprise-class UI designed for web applications.</div>
    <div style={lineStyle}>📦 A set of high-quality React components out of the box.</div>
    <div style={lineStyle}>🛡 Written in TypeScript with predictable static types.</div>
    <div style={lineStyle}>⚙️ Whole package of design resources and development tools.</div>
    <div style={lineStyle}>🌍 Internationalization support for dozens of languages.</div>
    <div style={lineStyle}>🎨 Powerful theme customization in every detail.</div>
  </>
);

const classNames = createStaticStyles(({ css }) => ({
  container: css`
    border-radius: 10px;
    padding: 10px;
  `,
}));

const styles: ModalProps['styles'] = {
  mask: {
    backgroundImage: `linear-gradient(to top, #18181b 0, rgba(21, 21, 22, 0.2) 100%)`,
  },
};

const stylesFn: ModalProps['styles'] = (info): GetProp<ModalProps, 'styles', 'Return'> => {
  if (info.props.footer) {
    return {
      container: {
        borderRadius: 14,
        border: '1px solid #ccc',
        padding: 0,
        overflow: 'hidden',
      },
      header: {
        padding: 16,
      },
      body: {
        padding: 16,
      },
      footer: {
        padding: '16px 10px',
        backgroundColor: '#fafafa',
      },
    };
  }
  return {};
};

const App: React.FC = () => {
  const [modalOpen, setModalOpen] = useState(false);
  const [modalFnOpen, setModalFnOpen] = useState(false);

  const sharedProps: ModalProps = {
    centered: true,
    classNames,
  };

  const footer: React.ReactNode = (
    <>
      <Button
        onClick={() => setModalFnOpen(false)}
        styles={{ root: { borderColor: '#ccc', color: '#171717', backgroundColor: '#fff' } }}
      >
        Cancel
      </Button>
      <Button
        type="primary"
        styles={{ root: { backgroundColor: '#171717' } }}
        onClick={() => setModalOpen(true)}
      >
        Submit
      </Button>
    </>
  );

  return (
    <Flex gap="medium">
      <Button onClick={() => setModalOpen(true)}>Open Style Modal</Button>
      <Button type="primary" onClick={() => setModalFnOpen(true)}>
        Open Function Modal
      </Button>
      <Modal
        {...sharedProps}
        footer={null}
        title="Custom Style Modal"
        styles={styles}
        open={modalOpen}
        onOk={() => setModalOpen(false)}
        onCancel={() => setModalOpen(false)}
      >
        {sharedContent}
      </Modal>
      <Modal
        {...sharedProps}
        footer={footer}
        title="Custom Function Modal"
        styles={stylesFn}
        mask={{ enabled: true, blur: true }}
        open={modalFnOpen}
        onOk={() => setModalFnOpen(false)}
        onCancel={() => setModalFnOpen(false)}
      >
        {sharedContent}
      </Modal>
    </Flex>
  );
};

export default App;

API

공통 props 참조: Common props

속성 설명 타입 기본값 버전 전역 설정
afterClose Modal이 완전히 닫혔을 때 호출되는 함수를 지정 function - ×
cancelButtonProps 취소 버튼의 props ButtonProps - 6.0.0
cancelText 취소 버튼의 텍스트 ReactNode Cancel ×
centered Modal을 세로 중앙에 배치 boolean false 5.24.0
classNames Modal 컴포넌트 내 각 시맨틱 구조에 대한 클래스를 커스터마이즈. 객체 또는 함수를 지원 Record<SemanticDOM, string> | (info: { props }) => Record<SemanticDOM, string> - 5.10.0
closable 우측 상단에 닫기(x) 버튼을 표시할지 여부 boolean | ClosableType true - 5.16.0
closeIcon 커스텀 닫기 아이콘. 5.7.0: null 또는 false로 설정하면 닫기 버튼이 숨겨짐 ReactNode <CloseOutlined /> 5.14.0
confirmLoading OK 버튼에 로딩 시각 효과를 적용할지 여부 boolean false ×
destroyOnClose 닫을 때 하위 컴포넌트를 마운트 해제할지 여부 boolean false ×
destroyOnHidden 닫을 때 하위 컴포넌트를 마운트 해제할지 여부 boolean false 5.25.0 ×
focusTriggerAfterClose 대화상자가 닫힌 후 트리거 요소에 포커스를 줄지 여부. focusable.focusTriggerAfterClose를 대신 사용 boolean true 4.9.0 ×
footer 푸터 콘텐츠. 기본 버튼이 필요 없으면 footer={null}로 설정 ReactNode | (originNode: ReactNode, extra: { OkBtn: React.FC, CancelBtn: React.FC }) => ReactNode (OK 및 Cancel 버튼) renderFunction: 5.9.0 ×
forceRender Modal 강제 렌더링 boolean false ×
focusable Modal에서 포커스 관리를 위한 설정 { trap?: boolean, focusTriggerAfterClose?: boolean } - 6.2.0 6.4.0
getContainer Modal의 마운트 노드. 여전히 전체 화면에 표시됨 HTMLElement | () => HTMLElement | Selectors | false document.body ×
keyboard esc 키로 닫기 지원 여부 boolean true ×
mask 마스크 효과 boolean | {enabled?: boolean, blur?: boolean, closable?: boolean} true mask.closable: 6.3.0 6.0.0, mask.closable: 6.3.0
maskClosable 마스크(모달 바깥 영역)를 클릭해 모달을 닫을지 여부. mask.closable을 대신 사용 boolean true - ×
modalRender 커스텀 모달 콘텐츠 렌더 (node: ReactNode) => ReactNode - 4.7.0 ×
okButtonProps OK 버튼의 props ButtonProps - 6.0.0
okText OK 버튼의 텍스트 ReactNode OK ×
okType OK 버튼의 type string primary ×
style 떠 있는 레이어의 스타일. 주로 위치 조정에 사용 CSSProperties - 5.7.0
styles Modal 컴포넌트 내 각 시맨틱 구조에 대한 인라인 스타일을 커스터마이즈. 객체 또는 함수를 지원 Record<SemanticDOM, CSSProperties> | (info: { props })=> Record<SemanticDOM, CSSProperties> - 5.10.0
loading Skeleton 표시 boolean 5.18.0 ×
scrollLock Modal이 열릴 때 body 스크롤을 잠글지 여부 boolean true 6.5.0 ×
title 모달 대화상자의 제목 ReactNode - ×
open 모달 대화상자의 표시 여부 boolean false ×
width 모달 대화상자의 너비 string | number | Breakpoint 520 Breakpoint: 5.23.0 ×
wrapClassName 모달 대화상자 컨테이너의 클래스 이름 string - ×
zIndex Modal의 z-index number 1000 ×
onCancel 사용자가 마스크, 우측 상단 닫기 버튼 또는 취소 버튼을 클릭했을 때 호출되는 함수를 지정 function(e) - ×
onOk 사용자가 OK 버튼을 클릭했을 때 호출되는 함수를 지정 function(e) - ×
afterOpenChange Modal이 켜지고 꺼질 때 애니메이션이 끝나는 시점의 콜백 (open: boolean) => void - 5.4.0 ×

참고 (Note)

  • Modal의 상태는 기본적으로 컴포넌트 생명주기 동안 유지돼요. 매번 완전히 새로운 상태로 열고 싶다면 destroyOnHidden을 설정하세요.
  • <Modal />을 Form과 함께 사용하면 destroyOnHidden을 설정해도 Modal을 닫을 때 필드 값이 지워지지 않는 상황이 있어요. 이 경우 <Form preserve={false} />가 필요해요.
  • Modal.method()의 RTL 모드는 hooks만 지원해요.

Modal.method()

콘텐츠의 성격에 따라 정보를 표시하는 다섯 가지 방법이 있어요.

  • Modal.info
  • Modal.success
  • Modal.error
  • Modal.warning
  • Modal.confirm

위 항목은 모두 함수이며, 설정 객체를 파라미터로 받아요. 객체의 속성은 다음과 같아요.

속성 설명 타입 기본값 버전
afterClose Modal이 완전히 닫혔을 때 호출되는 함수를 지정 function - 4.9.0
autoFocusButton 자동 포커스할 버튼 지정. focusable.autoFocusButton을 대신 사용 null | ok | cancel ok
cancelButtonProps 취소 버튼의 props ButtonProps -
cancelText Modal.confirm의 취소 버튼 텍스트 string Cancel
centered Modal을 세로 중앙에 배치 boolean false
className 컨테이너의 className string -
closable 확인 대화상자 우측 상단에 닫기(x) 버튼을 표시할지 여부 boolean | ClosableType false -
closeIcon 커스텀 닫기 아이콘 ReactNode undefined 4.9.0
content 콘텐츠 ReactNode -
focusable.autoFocusButton 자동 포커스할 버튼 지정 null | ok | cancel ok 6.2.0
footer 푸터 콘텐츠. 기본 버튼이 필요 없으면 footer: null로 설정 ReactNode | (originNode: ReactNode, extra: { OkBtn: React.FC, CancelBtn: React.FC }) => ReactNode - renderFunction: 5.9.0
getContainer Modal의 마운트 노드 반환 HTMLElement | () => HTMLElement | Selectors | false document.body
icon 커스텀 아이콘 ReactNode <ExclamationCircleFilled />
keyboard esc 키로 닫기 지원 여부 boolean true
mask 마스크 효과 boolean | {enabled?: boolean, blur?: boolean, closable?: boolean} true
maskClosable 마스크(모달 바깥 영역)를 클릭해 모달을 닫을지 여부. mask.closable을 대신 사용 boolean false -
scrollLock Modal이 열릴 때 body 스크롤을 잠글지 여부 boolean true 6.5.0
okButtonProps OK 버튼의 props ButtonProps -
okText OK 버튼의 텍스트 string OK
okType OK 버튼의 type string primary
style 떠 있는 레이어의 스타일. 주로 위치 조정에 사용 CSSProperties -
title 제목 ReactNode -
width 모달 대화상자의 너비 string | number 416
wrapClassName 모달 대화상자 컨테이너의 클래스 이름 string - 4.18.0
zIndex Modal의 z-index number 1000
onCancel onCancel 콜백 클릭. 파라미터는 닫는 함수이고, promise를 반환하면 resolve는 정상 닫기, reject는 닫지 않음을 의미 function(close) -
onOk onOk 콜백 클릭. 파라미터는 닫는 함수이고, promise를 반환하면 resolve는 정상 닫기, reject는 닫지 않음을 의미 function(close) -

모든 Modal.method는 참조를 반환하며, 이를 통해 모달 대화상자를 업데이트하고 닫을 수 있어요.

ClosableType

속성 설명 타입 기본값 버전
afterClose Modal이 완전히 닫혔을 때 호출되는 함수를 지정 function - -
closeIcon 커스텀 닫기 아이콘 ReactNode undefined -
disabled 닫기 아이콘 비활성화 여부 boolean false -
onClose Modal이 닫힐 때 트리거 Function undefined -
const modal = Modal.info();

modal.update({
  title: 'Updated title',
  content: 'Updated content',
});

// on 4.8.0 or above, you can pass a function to update modal
modal.update((prevConfig) => ({
  ...prevConfig,
  title: `${prevConfig.title} (New)`,
}));

modal.destroy();
  • Modal.destroyAll

Modal.destroyAll()은 모든 확인 모달 대화상자(Modal.confirm|success|info|error|warning)를 파괴할 수 있어요. 보통 라우터 변경 이벤트에서 모달 참조를 사용하지 않고 확인 모달을 자동으로 파괴할 때 쓰면 편해요.

import { browserHistory } from 'react-router';

// router change
browserHistory.listen(() => {
  Modal.destroyAll();
});

Modal.useModal()

Context를 사용해야 할 때는 Modal.useModal이 만든 contextHolder를 children에 삽입할 수 있어요. hooks로 만든 Modal은 contextHolder가 있는 위치의 모든 컨텍스트를 얻어요. 만들어진 modal은 Modal.method와 같은 생성 함수를 가져요.

const [modal, contextHolder] = Modal.useModal();

React.useEffect(() => {
  modal.confirm({
    // ...
  });
}, []);

return <div>{contextHolder}</div>;

modal.confirm은 다음 메서드를 반환해요.

  • destroy: 현재 모달 파괴
  • update: 현재 모달 업데이트
  • then: (hooks 전용) Promise 체인 호출, await 연산 지원
// `onOk`를 클릭하면 `true`를, `onCancel`을 클릭하면 `false`를 반환합니다
const confirmed = await modal.confirm({ ... });

Semantic DOM

https://ant.design/components/modal/semantic.md

Design Token

Component Token (Modal)

토큰 이름 설명 타입 기본값
contentBg 콘텐츠의 배경색 string #ffffff
footerBg 푸터의 배경색 string transparent
headerBg 헤더의 배경색 string transparent
titleColor 제목의 글꼴 색상 string rgba(0,0,0,0.88)
titleFontSize 제목의 글꼴 크기 string | number 16
titleLineHeight 제목의 줄 높이 string | number 1.5

Global Token

토큰 이름 설명 타입 기본값
borderRadiusLG LG 크기의 테두리 반경. Card, Modal 등 큰 테두리 반경 컴포넌트에서 사용 number
borderRadiusSM SM 크기의 테두리 반경. 작은 크기 입력 컴포넌트(Button, Input, Select 등)에서 사용 number
boxShadow 요소의 박스 섀도 스타일 제어 string
colorBgMask 마스크의 배경색. 마스크 아래 콘텐츠를 덮는 데 사용하며, Modal, Drawer, Image 등 컴포넌트가 이 토큰 사용 string
colorBgTextActive 활성 상태 텍스트의 배경색 제어 string
colorBgTextHover 호버 상태 텍스트의 배경색 제어 string
colorIcon 약한 동작. allowClear나 Alert 닫기 버튼 등 string
colorIconHover 약한 동작의 호버 색상. allowClear나 Alert 닫기 버튼 등 string
colorPrimaryBorder 메인 색 그라데이션의 획 색상. Slider 등 컴포넌트의 획에 사용 string
colorSplit 구분선의 색상으로 사용. colorBorderSecondary와 같지만 투명도를 가짐 string
colorText W3C 표준을 따르는 기본 텍스트 색상. 가장 어두운 중립 색 string
controlHeight Ant Design에서 버튼, 입력 상자 등 기본 컨트롤의 높이 number
fontFamily Ant Design의 글꼴 패밀리는 시스템 기본 인터페이스 글꼴을 우선하며, 화면 표시에 적합한 대체 글꼴 라이브러리를 제공 string
fontSize 디자인 시스템에서 가장 널리 쓰이는 글꼴 크기. 텍스트 그라데이션이 여기서 파생됨 number
fontSizeHeading5 h5 태그의 글꼴 크기 string | number
fontSizeLG 큰 글꼴 크기 number
fontWeightStrong 제목 컴포넌트(h1, h2, h3)나 선택된 항목의 글꼴 두께 제어 number
lineHeight 텍스트의 줄 높이 number
lineHeightHeading5 h5 태그의 줄 높이 number
lineType 기본 컴포넌트의 테두리 스타일 string
lineWidth 기본 컴포넌트의 테두리 너비 number
lineWidthFocus 컴포넌트가 포커스 상태일 때 라인의 너비 제어 number
margin 요소의 여백을 중간 크기로 제어 number
marginXS 요소의 여백을 작은 크기로 제어 number
motionDurationMid 중간 속도 모션. 중간 요소 애니메이션 상호작용에 사용 string
motionDurationSlow 느린 속도 모션. 큰 요소 애니메이션 상호작용에 사용 string
motionEaseInOutCirc 사전 설정된 모션 커브 string
motionEaseOutCirc 사전 설정된 모션 커브 string
padding 요소의 패딩 제어 number
screenLGMin 대형 화면의 최소 너비 제어 number
screenMDMin 중형 화면의 최소 너비 제어 number
screenSMMax 소형 화면의 최대 너비 제어 number
screenSMMin 소형 화면의 최소 너비 제어 number
screenXLMin 대형 화면 이상의 최소 너비 제어 number
screenXSMin 초소형 화면의 최소 너비 제어 number
screenXXLMin 초대형 화면의 최소 너비 제어 number
screenXXXLMin XXXL 화면의 최소 너비 제어 number
zIndexPopupBase FloatButton, Affix처럼 큰 팝업이 덮을 수 있는 컴포넌트의 기본 zIndex number

FAQ

Modal을 닫았을 때 콘텐츠가 업데이트되지 않는 이유는 뭔가요? {#faq-content-not-update}

Modal은 닫힐 때 콘텐츠 점프를 막기 위해 memo를 사용해요. 또한 Modal에서 Form을 사용한다면 effect에서 resetFields를 호출해 initialValues를 재설정할 수 있어요.

Modal.xxx에서 context, redux, ConfigProvider locale/prefixCls에 접근할 수 없는 이유는 뭔가요? {#faq-context-redux}

Modal 메서드를 호출하면 antd가 ReactDOM.render로 React 인스턴스를 동적으로 생성해요. 이 인스턴스의 컨텍스트는 원래 코드가 위치한 컨텍스트와 달라요.

(ConfigProvider context 같은) 컨텍스트 정보가 필요하다면 Modal.useModal을 사용해 modal 인스턴스와 contextHolder 노드를 얻은 뒤 children에 넣어 주세요.

const [modal, contextHolder] = Modal.useModal();

// then call modal.confirm instead of Modal.confirm

return (
  <Context1.Provider value="Ant">
    {/* contextHolder is in Context1, which means modal will get context of Context1 */}
    {contextHolder}
    <Context2.Provider value="Design">
      {/* contextHolder is out of Context2, which means modal will not get context of Context2 */}
    </Context2.Provider>
  </Context1.Provider>
);

참고: hooks를 사용한다면 contextHolder를 반드시 children에 넣어야 해요. 컨텍스트 연결이 필요 없다면 원래 메서드를 그대로 써도 돼요.

App Package 컴포넌트를 쓰면 useModal 등 contextHolder를 수동으로 심어야 하는 메서드의 문제를 간단히 해결할 수 있어요.

정적 메서드의 prefixCls를 어떻게 설정하나요? {#faq-set-prefix-cls}

ConfigProvider.config로 설정할 수 있어요.

더 알아보기 (Learn more)