숫자 입력
숫자 입력 (InputNumber)
숫자 값만 입력받는 입력 상자 컴포넌트예요. 최소·최대 범위, 증감 단계, 포맷터 등을 지원해요.
출처: 문서
본문
언제 사용하나요 (When To Use)
- 숫자 값을 입력받아야 할 때 사용해요.
예시 (Examples)
기본 (Basic)
숫자 전용 입력 상자예요.
import React from 'react';
import type { InputNumberProps } from 'antd';
import { InputNumber } from 'antd';
const onChange: InputNumberProps['onChange'] = (value) => {
console.log('changed', value);
};
const App: React.FC = () => <InputNumber min={1} max={10} defaultValue={3} onChange={onChange} />;
export default App;
크기 (Sizes)
숫자 입력 상자에는 세 가지 크기가 있어요. 기본 크기는 32px이며, 추가로 large(40px)와 small(24px)이 있어요.
import React from 'react';
import type { InputNumberProps } from 'antd';
import { InputNumber, Space } from 'antd';
const onChange: InputNumberProps['onChange'] = (value) => {
console.log('changed', value);
};
const App: React.FC = () => (
<Space wrap>
<InputNumber size="large" min={1} max={100000} defaultValue={3} onChange={onChange} />
<InputNumber min={1} max={100000} defaultValue={3} onChange={onChange} />
<InputNumber size="small" min={1} max={100000} defaultValue={3} onChange={onChange} />
</Space>
);
export default App;
비활성화 (Disabled)
버튼을 클릭해 사용 가능 상태와 비활성 상태를 전환해요.
import React, { useState } from 'react';
import { Button, InputNumber } from 'antd';
const App: React.FC = () => {
const [disabled, setDisabled] = useState(true);
const toggle = () => {
setDisabled(!disabled);
};
return (
<>
<InputNumber min={1} max={10} disabled={disabled} defaultValue={3} />
<div style={{ marginTop: 20 }}>
<Button onClick={toggle} type="primary">
Toggle disabled
</Button>
</div>
</>
);
};
export default App;
고정밀 소수 (High precision decimals)
stringMode를 사용하면 고정밀 소수를 지원해요. onChange가 대신 문자열 값을 반환해요. 브라우저가 BigInt를 지원하지 않으면 BigInt의 polyfill이 필요해요.
import React from 'react';
import type { InputNumberProps } from 'antd';
import { InputNumber } from 'antd';
const onChange: InputNumberProps['onChange'] = (value) => {
console.log('changed', value);
};
const App: React.FC = () => (
<InputNumber<string>
style={{ width: 200 }}
defaultValue="1"
min="0"
max="10"
step="0.00000000000001"
onChange={onChange}
stringMode
/>
);
export default App;
포맷터 (Formatter)
formatter로 값을 상황에 맞게 표시하고, 보통 parser도 함께 사용해요.
다음은 Intl.NumberFormat 기반의 InputNumber 구현이에요: https://codesandbox.io/s/currency-wrapper-antd-input-3ynzo
import React from 'react';
import type { InputNumberProps } from 'antd';
import { InputNumber, Space } from 'antd';
const onChange: InputNumberProps['onChange'] = (value) => {
console.log('changed', value);
};
const formatter: InputNumberProps<number>['formatter'] = (value) => {
const [start, end] = `${value}`.split('.') || [];
const v = `${start}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return `$ ${end ? `${v}.${end}` : `${v}`}`;
};
const App: React.FC = () => (
<Space>
<InputNumber<number>
defaultValue={1000}
formatter={formatter}
parser={(value) => value?.replace(/\$\s?|(,*)/g, '') as unknown as number}
onChange={onChange}
/>
<InputNumber<number>
defaultValue={100}
min={0}
max={100}
formatter={(value) => `${value}%`}
parser={(value) => value?.replace('%', '') as unknown as number}
onChange={onChange}
/>
</Space>
);
export default App;
키보드 (Keyboard)
keyboard로 키보드 동작을 제어해요.
import React, { useState } from 'react';
import { Checkbox, InputNumber, Space } from 'antd';
const App: React.FC = () => {
const [keyboard, setKeyboard] = useState(true);
return (
<Space>
<InputNumber min={1} max={10} keyboard={keyboard} defaultValue={3} />
<Checkbox
onChange={() => {
setKeyboard(!keyboard);
}}
checked={keyboard}
>
Toggle keyboard
</Checkbox>
</Space>
);
};
export default App;
휠 (Wheel)
마우스 휠로 제어해요.
import React from 'react';
import type { InputNumberProps } from 'antd';
import { InputNumber } from 'antd';
const onChange: InputNumberProps['onChange'] = (value) => {
console.log('changed', value);
};
const onStep: InputNumberProps['onStep'] = (value, info) => {
console.log('onStep', value, info);
};
const App: React.FC = () => (
<InputNumber
min={1}
max={10}
defaultValue={3}
onChange={onChange}
onStep={onStep}
changeOnWheel
/>
);
export default App;
변형 (Variants)
InputNumber의 변형에는 네 가지가 있어요. outlined, filled, borderless, underlined.
import React from 'react';
import { Flex, InputNumber } from 'antd';
const App: React.FC = () => (
<Flex vertical gap={12}>
<InputNumber placeholder="Outlined" style={{ width: 200 }} />
<InputNumber placeholder="Filled" variant="filled" style={{ width: 200 }} />
<InputNumber placeholder="Borderless" variant="borderless" style={{ width: 200 }} />
<InputNumber placeholder="Underlined" variant="underlined" style={{ width: 200 }} />
</Flex>
);
export default App;
스피너 (Spinner)
숫자 스피너 예시예요.
import React from 'react';
import type { InputNumberProps } from 'antd';
import { Flex, InputNumber } from 'antd';
const onChange: InputNumberProps['onChange'] = (value) => {
console.log('changed', value);
};
const sharedProps = {
mode: 'spinner' as const,
min: 1,
max: 10,
defaultValue: 3,
onChange,
style: { width: 150 },
};
const App: React.FC = () => (
<Flex vertical gap="medium">
<InputNumber {...sharedProps} placeholder="Outlined" />
<InputNumber {...sharedProps} variant="filled" placeholder="Filled" />
</Flex>
);
export default App;
범위 밖 (Out of range)
제어(control)로 value가 범위를 벗어나면 경고 스타일을 보여 줘요.
import React, { useState } from 'react';
import { Button, InputNumber, Space } from 'antd';
const App: React.FC = () => {
const [value, setValue] = useState<string | number | null>('99');
return (
<Space>
<InputNumber min={1} max={10} value={value} onChange={setValue} />
<Button
type="primary"
onClick={() => {
setValue(99);
}}
>
Reset
</Button>
</Space>
);
};
export default App;
접두사 / 접미사 (Prefix / Suffix)
입력 안쪽에 접두사나 접미사를 추가해요.
import React from 'react';
import { UserOutlined } from '@ant-design/icons';
import { Flex, InputNumber, Space } from 'antd';
const App: React.FC = () => (
<Flex vertical gap="medium">
<InputNumber prefix="¥" style={{ width: '100%' }} />
<Space.Compact block>
<Space.Addon>
<UserOutlined />
</Space.Addon>
<InputNumber prefix="¥" style={{ width: '100%' }} />
</Space.Compact>
<InputNumber prefix="¥" disabled style={{ width: '100%' }} />
<InputNumber suffix="RMB" style={{ width: '100%' }} />
</Flex>
);
export default App;
상태 (Status)
status로 InputNumber에 상태를 추가해요. error 또는 warning이 될 수 있어요.
import React from 'react';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
import { InputNumber, Space } from 'antd';
const App: React.FC = () => (
<Space vertical style={{ width: '100%' }}>
<InputNumber status="error" style={{ width: '100%' }} />
<InputNumber status="warning" style={{ width: '100%' }} />
<InputNumber status="error" style={{ width: '100%' }} prefix={<ClockCircleOutlined />} />
<InputNumber status="warning" style={{ width: '100%' }} prefix={<ClockCircleOutlined />} />
</Space>
);
export default App;
포커스 (Focus)
추가 옵션과 함께 포커스를 맞춰요.
import React, { useRef } from 'react';
import type { GetRef } from 'antd';
import { Button, InputNumber, Space } from 'antd';
type InputNumberRef = GetRef<typeof InputNumber>;
const App: React.FC = () => {
const inputRef = useRef<InputNumberRef>(null);
return (
<Space vertical style={{ width: '100%' }}>
<Space wrap>
<Button
onClick={() => {
inputRef.current?.focus({ cursor: 'start' });
}}
>
Focus at first
</Button>
<Button
onClick={() => {
inputRef.current?.focus({ cursor: 'end' });
}}
>
Focus at last
</Button>
<Button
onClick={() => {
inputRef.current?.focus({ cursor: 'all' });
}}
>
Focus to select all
</Button>
<Button
onClick={() => {
inputRef.current?.focus({ preventScroll: true });
}}
>
Focus prevent scroll
</Button>
</Space>
<InputNumber style={{ width: '100%' }} defaultValue={999} ref={inputRef} />
</Space>
);
};
export default App;
시맨틱 DOM 스타일링 (Custom semantic dom styling)
classNames와 styles로 객체나 함수를 전달해 InputNumber의 시맨틱 DOM 스타일을 커스터마이즈할 수 있어요.
import React from 'react';
import { Flex, InputNumber } from 'antd';
import type { GetProp, InputNumberProps } from 'antd';
import { createStyles } from 'antd-style';
const useStyle = createStyles(({ token }) => ({
root: {
border: `${token.lineWidth}px ${token.lineType} ${token.colorPrimary}`,
borderRadius: token.borderRadiusLG,
width: 200,
},
}));
const stylesObject: InputNumberProps['styles'] = {
input: {
fontSize: 14,
},
};
const stylesFn: InputNumberProps['styles'] = ({
props,
}): GetProp<InputNumberProps, 'styles', 'Return'> => {
if (props.size === 'large') {
return {
root: {
backgroundColor: 'rgba(250,250,250, 0.5)',
borderColor: '#722ed1',
},
};
}
return {};
};
const App: React.FC = () => {
const { styles: classNames } = useStyle();
const sharedProps: InputNumberProps = {
classNames,
};
return (
<Flex vertical gap="medium">
<InputNumber {...sharedProps} styles={stylesObject} placeholder="Object" />
<InputNumber {...sharedProps} styles={stylesFn} placeholder="Function" size="large" />
</Flex>
);
};
export default App;
API
공통 props는 Common props를 참고해요.
| 속성 (Property) | 설명 (Description) | 타입 (Type) | 기본값 (Default) | 버전 (Version) | 글로벌 설정 |
|---|---|---|---|---|---|
| 입력 필드 뒤(오른쪽)에 표시되는 라벨 텍스트예요. Space.Compact를 대신 사용해 주세요. | ReactNode | - | 4.17.0 | × | |
| 입력 필드 앞(왼쪽)에 표시되는 라벨 텍스트예요. Space.Compact를 대신 사용해 주세요. | ReactNode | - | 4.17.0 | × | |
| changeOnBlur | blur 시 onChange를 트리거해요. 예: blur로 값을 범위 안으로 리셋. |
boolean | true | 5.11.0 | × |
| changeOnWheel | 마우스 휠로 제어를 허용해요. | boolean | - | 5.14.0 | × |
| classNames | 컴포넌트 내부의 각 시맨틱 구조에 대한 class를 지정해요. 객체 또는 함수를 지원해요. | Record<SemanticDOM, string> | (info: { props })=> Record<SemanticDOM, string> | - | 6.0.0 | 6.0.0 |
| controls | +- 컨트롤을 표시할지, 또는 커스텀 화살표 아이콘을 설정할지 여부 |
boolean | { upIcon?: React.ReactNode; downIcon?: React.ReactNode; } | - | × | |
| decimalSeparator | 소수점 구분자 | string | - | - | × |
| placeholder | 플레이스홀더 | string | - | × | |
| defaultValue | 초기 값 | number | - | - | × |
| disabled | 입력 비활성화 여부 | boolean | false | - | × |
| formatter | 표시되는 값의 형식을 지정해요. | function(value: number | string, info: { userTyping: boolean, input: string }): string | - | × | |
| keyboard | 키보드 동작 활성화 여부 | boolean | true | × | |
| max | 최대 값 | number | Number.MAX_SAFE_INTEGER | - | × |
| min | 최소 값 | number | Number.MIN_SAFE_INTEGER | - | × |
| parser | formatter에서 추출할 값을 지정해요. | function(string): number | - | - | × |
| precision | 입력 값의 정밀도예요. formatter 설정 시 formatter를 사용해요. |
number | - | - | × |
| readOnly | 입력이 읽기 전용인지 여부 | boolean | false | - | × |
| status | 검증 상태 설정 | 'error' | 'warning' | - | × | |
| styles | 컴포넌트 내부의 각 시맨틱 구조에 대한 인라인 스타일을 지정해요. 객체 또는 함수를 지원해요. | Record<SemanticDOM, CSSProperties> | (info: { props })=> Record<SemanticDOM, CSSProperties> | - | 6.0.0 | 6.0.0 |
| prefix | Input의 접두사 아이콘 | ReactNode | - | × | |
| suffix | Input의 접미사 아이콘 | ReactNode | - | 5.20.0 | × |
| size | 입력 상자의 높이 | large | medium | small |
- | - | × |
| step | 현재 값이 증가하거나 감소하는 단위예요. 정수 또는 소수일 수 있어요. | number | string | 1 | - | × |
| stringMode | 값을 문자열로 설정해 고정밀 소수를 지원해요. onChange가 문자열 값을 반환해요. |
boolean | false | 4.13.0 | × |
| mode | 입력 또는 스피너 표시 | 'input' | 'spinner' |
'input' |
× | |
| value | 컴포넌트의 현재 값 | number | - | - | × |
| variant | Input의 변형 | outlined | borderless | filled | underlined |
outlined |
5.13.0 | underlined: 5.24.0 |
5.19.0 |
| onChange | 값이 변경될 때 트리거되는 콜백 | function(value: number | string | null) | - | - | × |
| onPressEnter | Enter 키를 누를 때 트리거되는 콜백 함수 | function(e) | - | - | × |
| onStep | 위·아래 버튼 / 키보드 / 휠을 클릭할 때 트리거되는 콜백 함수 | (value: number, info: { offset: number, type: 'up' | 'down', emitter: 'handler' | 'keydown' | 'wheel' }) => void | - | - | × |
테두리 스타일 여부예요. variant를 대신 사용해 주세요. |
boolean | true | - | × |
Ref
| 이름 (Name) | 설명 (Description) | 타입 (Type) | 버전 (Version) |
|---|---|---|---|
| blur() | 포커스 제거 | - | |
| focus() | 포커스 획득 | (option?: { preventScroll?: boolean, cursor?: 'start' | 'end' | 'all' }) | cursor - 5.22.0 |
| nativeElement | 네이티브 DOM 요소 | - | 5.17.3 |
시맨틱 DOM (Semantic DOM)
시맨틱 DOM 구조는 https://ant.design/components/input-number/semantic.md 에서 확인할 수 있어요.
디자인 토큰 (Design Token)
컴포넌트 토큰 (InputNumber) (Component Token)
| 토큰 이름 (Token Name) | 설명 (Description) | 타입 (Type) | 기본값 (Default Value) |
|---|---|---|---|
| activeBg | 입력 상자가 활성화될 때 배경색 | string | #ffffff |
| activeBorderColor | 활성 테두리 색 | string | #1677ff |
| activeShadow | 활성 시 box-shadow | string | 0 0 0 2px rgba(5,145,255,0.1) |
| addonBg | addon의 배경색 | string | rgba(0,0,0,0.02) |
| controlWidth | 입력 너비 | number | 90 |
| errorActiveShadow | 오류 상태에서 활성 시 box-shadow | string | 0 0 0 2px rgba(255,38,5,0.06) |
| filledHandleBg | filled 변형에서 핸들의 배경색 | string | #f0f0f0 |
| handleActiveBg | 핸들의 활성 배경색 | string | rgba(0,0,0,0.02) |
| handleBg | 핸들의 배경색 | string | #ffffff |
| handleBorderColor | 핸들의 테두리 색 | string | #d9d9d9 |
| handleFontSize | 컨트롤 버튼의 아이콘 크기 | number | 7 |
| handleHoverColor | 핸들의 hover 색 | string | #1677ff |
| handleVisible | 핸들 표시 여부 | true | "auto" | auto |
| handleWidth | 컨트롤 버튼의 너비 | number | 22 |
| hoverBg | 입력 상자를 hover할 때 배경색 | string | #ffffff |
| hoverBorderColor | hover 테두리 색 | string | #4096ff |
| inputFontSize | 글자 크기 | number | 14 |
| inputFontSizeLG | 큰 글자 크기 | number | 16 |
| inputFontSizeSM | 작은 글자 크기 | number | 14 |
| paddingBlock | 입력의 세로 패딩 | number | 4 |
| paddingBlockLG | 큰 입력의 세로 패딩 | number | 7 |
| paddingBlockSM | 작은 입력의 세로 패딩 | number | 0 |
| paddingInline | 입력의 가로 패딩 | number | 11 |
| paddingInlineLG | 큰 입력의 가로 패딩 | number | 11 |
| paddingInlineSM | 작은 입력의 가로 패딩 | number | 7 |
| warningActiveShadow | 경고 상태에서 활성 시 box-shadow | string | 0 0 0 2px rgba(255,215,5,0.1) |
글로벌 토큰 (Global Token)
| 토큰 이름 (Token Name) | 설명 (Description) | 타입 (Type) | 기본값 (Default Value) |
|---|---|---|---|
| borderRadius | 기본 컴포넌트의 테두리 반경 | number | |
| borderRadiusLG | LG 크기 테두리 반경이에요. Card, Modal 등 큰 테두리 반경을 가진 컴포넌트에 사용돼요. | number | |
| borderRadiusSM | SM 크기 테두리 반경이에요. Button, Input, Select 등 작은 크기의 입력 컴포넌트에 사용돼요. | number | |
| colorBgContainer | 컨테이너 배경색이에요. 기본 버튼, 입력 상자 등. colorBgElevated와 혼동하지 마세요. |
string | |
| colorBgContainerDisabled | 비활성 상태에서 컨테이너의 배경색을 제어해요. | string | |
| colorBorder | 기본 테두리 색이에요. 폼 구분선, 카드 구분선처럼 서로 다른 요소를 구분하는 데 사용돼요. | string | |
| colorBorderDisabled | 비활성 상태의 요소 테두리 색을 제어해요. | string | |
| colorError | 오류 Button, 오류 Result 컴포넌트 등 작업 실패의 시각적 요소를 나타내는 데 사용돼요. | string | |
| colorErrorAffix | 오류 상태에서 폼 컨트롤 접두사/접미사 색을 제어해요. | string | |
| colorErrorBg | 오류 상태의 배경색 | string | |
| colorErrorBgHover | 오류 상태의 hover 배경색 | string | |
| colorErrorBorderHover | 오류 상태의 hover 테두리 색 | string | |
| colorErrorText | 오류 색에서 텍스트의 기본 상태 | string | |
| colorFillSecondary | 2단계 fill 색으로 Rate, Skeleton 등 요소의 모양을 더 선명하게 나타낼 수 있어요. Table 등에서 3단계 fill 색의 Hover 상태로도 쓰여요. | string | |
| colorFillTertiary | 3단계 fill 색으로 Slider, Segmented 등 요소의 모양을 나타내는 데 사용돼요. 강조 요구가 없다면 3단계 fill 색을 기본 fill로 쓰는 걸 권장해요. | string | |
| colorIcon | 약한 동작이에요. allowClear나 Alert 닫기 버튼 같은 것. |
string | |
| colorText | W3C 표준을 따르는 기본 텍스트 색이에요. 가장 어두운 중성색이기도 해요. | string | |
| colorTextDisabled | 비활성 상태의 텍스트 색을 제어해요. | string | |
| colorTextPlaceholder | 플레이스홀더 텍스트 색을 제어해요. | string | |
| colorWarning | Notification, Alert 등 경고를 나타내는 map 토큰에 사용돼요. Alert나 Input 같은 컨트롤 컴포넌트도 이 map 토큰을 사용해요. | string | |
| colorWarningAffix | 경고 상태에서 폼 컨트롤 접두사/접미사 색을 제어해요. | string | |
| colorWarningBg | 경고 상태의 배경색 | string | |
| colorWarningBgHover | 경고 상태의 hover 배경색 | string | |
| colorWarningBorderHover | 경고 상태의 hover 테두리 색 | string | |
| colorWarningText | 경고 색에서 텍스트의 기본 상태 | string | |
| fontFamily | Ant Design의 글꼴은 시스템의 기본 인터페이스 글꼴을 우선시하고, 화면 표시에 적합한 대체 글꼴 라이브러리를 제공해 플랫폼과 브라우저에 따라 가독성을 유지하며 친근하고 안정적이며 전문적인 특성을 반영해요. | string | |
| fontSize | 디자인 시스템에서 가장 널리 쓰이는 글자 크기로, 여기서 텍스트 그라데이션이 파생돼요. | number | |
| lineHeight | 텍스트의 줄 높이예요. | number | |
| lineHeightLG | 큰 텍스트의 줄 높이예요. | number | |
| lineType | 기본 컴포넌트의 테두리 스타일 | string | |
| lineWidth | 기본 컴포넌트의 테두리 두께 | number | |
| motionDurationMid | 동작 속도, 중간 속도예요. 중간 요소의 애니메이션 상호작용에 사용돼요. | string | |
| paddingXXS | 요소의 아주 작은 패딩을 제어해요. | number |
참고 (Notes)
이슈 #21158, #17344, #9421과 입력에 관한 문서에 따라, 커뮤니티는 <Input /> 속성에 type="number"를 네이티브로 포함하는 것을 지원하지 않는 것으로 보여요. 필요하면 자유롭게 포함하되, 파워 유저가 클라이언트 측 검증을 편집할 수 있으므로 서버 측 검증을 사용하는 것을 강력히 권장해요.
FAQ
제어(control)에서 value가 min이나 max를 초과할 수 있는 이유는 무엇인가요? {#faq-controlled-range}
개발자가 제어 상태에서 데이터를 직접 관리해요. InputNumber가 표시 값을 바꾸면 데이터가 어긋날 수 있어요. 폼에서 사용할 때도 잠재적인 데이터 문제를 일으킬 수 있어요.
min이나 max를 동적으로 변경해 value가 범위 밖이 되는데 onChange가 트리거되지 않는 이유는 무엇인가요? {#faq-dynamic-range-change}
onChange는 사용자 트리거 이벤트예요. 자동 트리거되면 폼 라이브러리가 데이터 수정 소스를 감지하지 못해요.
왜 onBlur 같은 이벤트에서 올바른 값을 얻을 수 없나요? {#faq-onblur-value}
InputNumber의 값은 내부 로직으로 감싸져 있어요. onBlur 등에서 얻는 event.target.value는 DOM 요소의 value이지 InputNumber의 실제 값이 아니에요. 예를 들어 formatter나 decimalSeparator로 표시 형식을 바꾸면 DOM에서 포맷된 문자열을 얻게 돼요. 항상 onChange로 현재 값을 얻어야 해요.
changeOnWheel이 마우스 스크롤 휠이 값을 바꾸는지 제어하지 못하는 이유는 무엇인가요? {#faq-change-on-wheel}
type속성 사용은 더 이상 권장되지 않아요.
InputNumber 컴포넌트는 input 요소의 모든 속성을 사용할 수 있고 결국 input 요소에 전달돼요. type='number'를 전달하면 이 속성도 input 요소에 추가되어 네이티브 동작(마우스 휠이 값을 바꾸는 것)을 활성화해요. 그래서 changeOnWheel이 마우스 휠이 값을 바꾸는지 제어할 수 없는 거예요.