메시지

메시지 (Message)

성공·경고·오류 같은 피드백을 가볍게 알려 주는 컴포넌트예요. 화면 위쪽 중앙에 잠깐 표시됐다가 자동으로 사라지는, 흐름을 방해하지 않는 프롬프트예요.

출처: 문서

본문

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

  • 성공, 경고, 오류 등과 같은 피드백을 제공할 때 사용해요.
  • 메시지는 화면 위쪽 중앙에 표시됐다가 자동으로 사라져요. 사용자 흐름을 방해하지 않는 가벼운 프롬프트예요.

예시 (Examples)

Hooks 사용 (권장) (Hooks usage)

message.useMessage를 사용하면 컨텍스트에 접근할 수 있는 contextHolder를 얻을 수 있어요. 참고로 정적(static) 메서드 대신 최상위 등록 방식을 권장해요. 정적 메서드는 컨텍스트를 소비할 수 없어서 ConfigProvider 데이터가 동작하지 않기 때문이에요.

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

const App: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();

  const info = () => {
    messageApi.info('Hello, Ant Design!');
  };

  return (
    <>
      {contextHolder}
      <Button type="primary" onClick={info}>
        Display normal message
      </Button>
    </>
  );
};

export default App;

다른 타입의 메시지 (Other types of message)

success, error, warning 타입의 메시지예요.

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

const App: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();

  const success = () => {
    messageApi.open({
      type: 'success',
      content: 'This is a success message',
    });
  };

  const error = () => {
    messageApi.open({
      type: 'error',
      content: 'This is an error message',
    });
  };

  const warning = () => {
    messageApi.open({
      type: 'warning',
      content: 'This is a warning message',
    });
  };

  return (
    <>
      {contextHolder}
      <Space>
        <Button onClick={success}>Success</Button>
        <Button onClick={error}>Error</Button>
        <Button onClick={warning}>Warning</Button>
      </Space>
    </>
  );
};

export default App;

표시 시간 커스터마이즈 (Customize duration)

메시지 표시 시간을 기본 3s에서 10s로 바꿀 수 있어요.

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

const App: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();

  const success = () => {
    messageApi.open({
      type: 'success',
      content: 'This is a prompt message for success, and it will disappear in 10 seconds',
      duration: 10,
    });
  };

  return (
    <>
      {contextHolder}
      <Button onClick={success}>Customized display duration</Button>
    </>
  );
};

export default App;

스택 (Stack)

스택 설정은 기본적으로 비활성화돼 있어요. 메시지 수가 threshold를 넘으면 스택으로 쌓여요. 접힌 스택에는 가장 최근 메시지만 표시돼요.

import React from 'react';
import { Button, Divider, InputNumber, message, Space, Switch } from 'antd';

const App: React.FC = () => {
  const [enabled, setEnabled] = React.useState(true);
  const [threshold, setThreshold] = React.useState(3);
  const indexRef = React.useRef(0);
  const [messageApi, contextHolder] = message.useMessage({
    stack: enabled
      ? {
          threshold,
        }
      : false,
  });

  const openMessage = () => {
    indexRef.current += 1;
    const isOdd = indexRef.current % 2 === 1;

    messageApi.open({
      type: 'info',
      content: isOdd
        ? `Message ${indexRef.current}: This is a stacked message.`
        : `Message ${indexRef.current}: This is a slightly longer stacked message.`,
      duration: 0,
    });
  };

  return (
    <>
      {contextHolder}
      <Space size="large">
        <Space style={{ width: '100%' }}>
          <span>Enabled: </span>
          <Switch
            aria-label="Enable message stack"
            checked={enabled}
            onChange={(v) => setEnabled(v)}
          />
        </Space>
        <Space style={{ width: '100%' }}>
          <span>Threshold: </span>
          <InputNumber
            aria-label="Stack threshold"
            disabled={!enabled}
            value={threshold}
            step={1}
            min={1}
            max={10}
            onChange={(v) => setThreshold(v ?? 1)}
          />
        </Space>
      </Space>
      <Divider />
      <Space>
        <Button type="primary" onClick={openMessage}>
          Open the message box
        </Button>
        <Button onClick={() => messageApi.destroy()}>Destroy all</Button>
      </Space>
    </>
  );
};

export default App;

로딩 표시기가 있는 메시지 (Message with loading indicator)

전역 로딩 표시기를 보여 주고, 비동기적으로 스스로 사라지게 해요.

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

const App: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();

  const success = () => {
    messageApi.open({
      type: 'loading',
      content: 'Action in progress..',
      duration: 0,
    });
    // Dismiss manually and asynchronously
    setTimeout(messageApi.destroy, 2500);
  };
  return (
    <>
      {contextHolder}
      <Button onClick={success}>Display a loading indicator</Button>
    </>
  );
};

export default App;

Promise 인터페이스 (Promise interface)

message는 onClose에 대한 promise 인터페이스를 제공해요. 위 예시는 이전 메시지가 닫히려 할 때 새 메시지를 표시해요.

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

const App: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();

  const success = () => {
    messageApi
      .open({
        type: 'loading',
        content: 'Action in progress..',
        duration: 2.5,
      })
      .then(() => message.success('Loading finished', 2.5))
      .then(() => message.info('Loading finished', 2.5));
  };

  return (
    <>
      {contextHolder}
      <Button onClick={success}>Display sequential messages</Button>
    </>
  );
};

export default App;

커스텀 시맨틱 스타일 (Custom semantic styles)

classNames와 styles로 메시지의 시맨틱 DOM 스타일을 커스터마이즈할 수 있어요.

import React from 'react';
import { Button, message, Space } from 'antd';
import type { GetProp, MessageArgsProps } from 'antd';

const defaultStyles: GetProp<MessageArgsProps, 'styles', 'Return'> = {
  root: {
    backgroundColor: '#f6ffed',
    border: '2px solid #95de64',
    borderRadius: 16,
    boxShadow: '4px 4px 0 #d9f7be',
  },
  icon: {
    color: '#237804',
  },
  title: {
    color: '#237804',
    fontWeight: 600,
  },
};

const stylesFn: MessageArgsProps['styles'] = ({
  props,
}): GetProp<MessageArgsProps, 'styles', 'Return'> => {
  if (props.type === 'error') {
    return {
      root: {
        ...defaultStyles.root,
        backgroundColor: '#fff2f0',
        borderColor: '#ffccc7',
        boxShadow: '4px 4px 0 #ffccc7',
      },
      icon: {
        color: '#cf1322',
      },
      title: {
        color: '#cf1322',
        fontWeight: 600,
      },
    };
  }
  return defaultStyles;
};

const App: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();

  const showObjectStyle = () => {
    messageApi.open({
      type: 'success',
      content: 'This is a message with object styles',
      styles: defaultStyles,
    });
  };

  const showFunctionStyle = () => {
    messageApi.open({
      type: 'error',
      content: 'This is a message with function styles',
      styles: stylesFn,
    });
  };

  return (
    <>
      {contextHolder}
      <Space>
        <Button onClick={showObjectStyle}>Object style</Button>
        <Button onClick={showFunctionStyle} type="primary">
          Function style
        </Button>
      </Space>
    </>
  );
};

export default App;

메시지 콘텐츠 업데이트 (Update Message Content)

고유한 key로 메시지 콘텐츠를 업데이트할 수 있어요.

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

const App: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();
  const key = 'updatable';

  const openMessage = () => {
    messageApi.open({
      key,
      type: 'loading',
      content: 'Loading...',
    });
    setTimeout(() => {
      messageApi.open({
        key,
        type: 'success',
        content: 'Loaded!',
        duration: 2,
      });
    }, 1000);
  };

  return (
    <>
      {contextHolder}
      <Button type="primary" onClick={openMessage}>
        Open the message box
      </Button>
    </>
  );
};

export default App;

정적 메서드 (권장되지 않음) (Static method)

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

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

const info = () => {
  message.info('This is a normal message');
};

const App: React.FC = () => (
  <Button type="primary" onClick={info}>
    Static Method
  </Button>
);

export default App;

API

공통 props는 Common props를 참고해요.

이 컴포넌트는 몇 가지 정적 메서드를 제공하며, 사용법과 인자는 다음과 같아요.

  • message.success(content, [duration], onClose)
  • message.error(content, [duration], onClose)
  • message.info(content, [duration], onClose)
  • message.warning(content, [duration], onClose)
  • message.loading(content, [duration], onClose)
속성 (Property) 설명 (Description) 타입 (Type) 기본값 (Default)
content 메시지의 콘텐츠예요. ReactNode | config -
duration 자동으로 닫히기 전까지의 시간(초)이에요. 0으로 설정하면 닫히지 않아요. number 3
onClose 메시지가 닫힐 때 호출할 함수를 지정해요. function -

afterClose는 thenable 인터페이스에서 호출할 수 있어요.

  • message[level](content, [duration]).then(afterClose)
  • message[level](content, [duration], onClose).then(afterClose)

여기서 level은 message의 정적 메서드 중 하나를 의미해요. then 메서드의 결과는 Promise예요.

객체에 감싼 인자를 전달하는 것도 지원해요.

  • message.open(config)
  • message.success(config)
  • message.error(config)
  • message.info(config)
  • message.warning(config)
  • message.loading(config)

config의 속성은 다음과 같아요.

속성 (Property) 설명 (Description) 타입 (Type) 기본값 (Default) 버전 (Version) 글로벌 설정
className 커스터마이즈된 CSS class string - - 5.7.0
classNames 컴포넌트 내부의 각 시맨틱 구조에 대한 class를 지정해요. 객체 또는 함수를 지원해요. Record<SemanticDOM, string> | (info: { props })=> Record<SemanticDOM, string> - 6.0.0 6.0.0
content 메시지의 콘텐츠예요. ReactNode - - ×
duration 자동으로 닫히기 전까지의 시간(초)이에요. 0으로 설정하면 닫히지 않아요. number 3 - ×
icon 커스터마이즈된 아이콘 ReactNode - - ×
pauseOnHover hover 시 타이머를 계속 돌릴지 여부예요. boolean true - ×
key Message의 고유 식별자예요. string | number - - ×
style 커스터마이즈된 인라인 스타일 CSSProperties - - 5.7.0
styles 컴포넌트 내부의 각 시맨틱 구조에 대한 인라인 스타일을 지정해요. 객체 또는 함수를 지원해요. Record<SemanticDOM, CSSProperties> | (info: { props })=> Record<SemanticDOM, CSSProperties> - 6.0.0 6.0.0
onClick 메시지가 클릭될 때 호출할 함수를 지정해요. function - - ×
onClose 메시지가 닫힐 때 호출할 함수를 지정해요. function - - ×

전역 정적 메서드 (Global static methods)

전역 설정과 파괴(destroy)를 위한 메서드도 제공돼요.

  • message.config(options)
  • message.destroy()

특정 메시지를 제거하려면 message.destroy(key)를 사용해요.

message.config

전역 설정에 ConfigProvider를 사용하면 시스템이 기본적으로 RTL 모드를 자동으로 시작해요.(4.3.0+)

단독으로 사용하고 싶다면 다음 설정으로 RTL 모드를 시작할 수 있어요.

message.config({
  top: 100,
  duration: 2,
  maxCount: 3,
  rtl: true,
  prefixCls: 'my-message',
});
속성 (Property) 설명 (Description) 타입 (Type) 기본값 (Default) 버전 (Version) 글로벌 설정
duration 자동으로 닫히기 전까지의 시간(초) number 3 ×
getContainer Message가 마운트될 노드를 반환해요. 여전히 전체 화면에 표시돼요. () => HTMLElement () => document.body ×
maxCount 표시할 최대 메시지 수예요. 한도를 초과하면 가장 오래된 것을 버려요. number - ×
prefixCls 메시지 노드의 prefix className이에요. string ant-message 4.5.0 ×
rtl RTL 모드를 활성화할지 여부예요. boolean false ×
stack 메시지 수가 threshold를 넘으면 스택으로 쌓여요. 접힌 스택에는 가장 최근 메시지만 표시돼요. boolean | { threshold: number } false 6.4.0 ×
top 위에서부터의 거리예요. string | number 8 ×

시맨틱 DOM (Semantic DOM)

시맨틱 DOM 구조는 https://ant.design/components/message/semantic.md 에서 확인할 수 있어요.

디자인 토큰 (Design Token)

컴포넌트 토큰 (Message) (Component Token)

토큰 이름 (Token Name) 설명 (Description) 타입 (Type) 기본값 (Default Value)
contentBg Message의 배경색 string #ffffff
contentPadding Message의 패딩 Padding<string | number> | undefined 9px 12px
zIndexPopup Message의 z-index number 2010

글로벌 토큰 (Global Token)

토큰 이름 (Token Name) 설명 (Description) 타입 (Type) 기본값 (Default Value)
borderRadiusLG LG 크기 테두리 반경이에요. Card, Modal 등 큰 테두리 반경을 가진 컴포넌트에 사용돼요. number
boxShadow 요소의 박스 섀도우 스타일을 제어해요. string
boxShadowTertiary 요소의 3차 박스 섀도우 스타일을 제어해요. string
colorBgElevated 팝업 레이어의 컨테이너 배경색이에요. 다크 모드에서는 이 토큰의 색이 colorBgContainer보다 약간 밝아요. 예: modal, pop-up, menu 등. string
colorError 오류 Button, 오류 Result 컴포넌트 등 작업 실패의 시각적 요소를 나타내는 데 사용돼요. string
colorInfo Alert, Tag, Progress 등 작업 정보를 나타내는 토큰 시퀀스에 사용돼요. string
colorSuccess Result, Progress 등 작업 성공의 토큰 시퀀스를 나타내는 데 사용돼요. string
colorText W3C 표준을 따르는 기본 텍스트 색이에요. 가장 어두운 중성색이기도 해요. string
colorTextHeading 제목의 글자 색을 제어해요. string
colorWarning Notification, Alert 등 경고를 나타내는 map 토큰에 사용돼요. Alert나 Input 같은 컨트롤 컴포넌트도 이 map 토큰을 사용해요. string
controlHeightLG LG 컴포넌트 높이 number
fontFamily Ant Design의 글꼴은 시스템의 기본 인터페이스 글꼴을 우선시하고, 화면 표시에 적합한 대체 글꼴 라이브러리를 제공해 플랫폼과 브라우저에 따라 가독성을 유지하며 친근하고 안정적이며 전문적인 특성을 반영해요. string
fontSize 디자인 시스템에서 가장 널리 쓰이는 글자 크기로, 여기서 텍스트 그라데이션이 파생돼요. number
fontSizeLG 큰 글자 크기 number
lineHeight 텍스트의 줄 높이예요. number
lineHeightLG 큰 텍스트의 줄 높이예요. number
margin 중간 크기의 요소 여백을 제어해요. number
marginLG 큰 크기의 요소 여백을 제어해요. number
marginXS 작은 크기의 요소 여백을 제어해요. number
marginXXL 가장 큰 크기의 요소 여백을 제어해요. number
motionDurationFast 동작 속도, 빠른 속도예요. 작은 요소의 애니메이션 상호작용에 사용돼요. string
motionDurationMid 동작 속도, 중간 속도예요. 중간 요소의 애니메이션 상호작용에 사용돼요. string
motionDurationSlow 동작 속도, 느린 속도예요. 큰 요소의 애니메이션 상호작용에 사용돼요. string
motionEaseInOut 미리 정의된 모션 곡선이에요. string
paddingContentHorizontalLG 콘텐츠 요소의 가로 패딩을 제어해요. 큰 화면 기기에 적합해요. number
paddingLG 요소의 큰 패딩을 제어해요. number
paddingMD 요소의 중간 패딩을 제어해요. number
paddingSM 요소의 작은 패딩을 제어해요. number

FAQ

왜 message에서 context, redux, ConfigProvider locale/prefixCls/theme에 접근할 수 없나요? {#faq-context-redux}

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

ConfigProvider 컨텍스트 같은 컨텍스트 정보가 필요하다면 message.useMessage로 api 인스턴스와 contextHolder 노드를 얻고, 이를 children 안에 넣어 주세요.

const [api, contextHolder] = message.useMessage();

return (
  <Context1.Provider value="Ant">
    {/* contextHolder is inside Context1 which means api will get value of Context1 */}
    {contextHolder}
    <Context2.Provider value="Design">
      {/* contextHolder is outside Context2 which means api will **not** get value of Context2 */}
    </Context2.Provider>
  </Context1.Provider>
);

참고: hooks를 사용할 때는 반드시 contextHolder를 children 안에 삽입해야 해요. 컨텍스트 연결이 필요 없다면 원래 메서드를 사용해도 돼요.

App 컴포넌트를 사용하면 useMessage와 contextHolder를 수동으로 심어야 하는 다른 메서드들의 문제를 간단히 해결할 수 있어요.

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

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

더 알아보기 (Learn more)