메시지
메시지 (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로 설정할 수 있어요.