컴포넌트/필드 컴포넌트

Number (숫자)

VanillaFront 2026. 9. 10. 14:56

Va.Number — 숫자 전용 입력 필드

<input type="number">를 감싸는 PureField 계열 컴포넌트입니다. min/max/step/소수점 자릿수 제한을 옵션 하나로 지정할 수 있고, 브라우저 표준 스피너(위/아래 화살표) UI가 자동으로 붙습니다.

  • 클래스: Va.Number  va_component.js:1257
  • short name: number
  • 상속: Va.PureField (Input/Search와 형제)
  • DOM 태그: <input type="number"> (하드코딩)
  • isContainer: true
  • 베이스 CSS: va-input (Input과 동일 클래스 공유)


1. 기본 사용

{
    tagName: 'number',
    value: 10,
    min: 0,
    max: 100,
    step: 1,
    onChange: 'onQtyChange'
}

렌더 결과: <input type="number" step="1" min="0" max="100"> — 브라우저가 우측에 위/아래 스피너 버튼을 자동 표시.


2. Va.Input / Va.Search와의 차이

항목Va.InputVa.SearchVa.Number

type 기본값 'text' (변경 가능) 'search' 고정 'number' 고정
전용 속성 numberComma min / max / step / decimalPlaces
초기 value undefined undefined 0
textAlign 기본 왼쪽 왼쪽 'right' (숫자 관행)
브라우저 스피너 ✓ (자동)
모바일 키보드 텍스트 검색 숫자 전용 키패드
소수점 강제 자릿수 ✓ (decimalPlaces)

한 줄 요약: "숫자만 입력받고, min/max/step으로 범위를 강제하는 필드."


3. 주요 속성

Number 전용

속성기본값설명

min 최솟값 (HTML min 속성으로 반영)
max 최댓값 (HTML max 속성으로 반영)
step 1 스피너 증감 단위. 소수 허용 예: 0.1, 0.01
decimalPlaces 강제 소수점 자릿수. keyup마다 자동 반올림 (예: 2  1.239 입력 시 1.24로)

PureField 상속

속성설명

value 필드 값 (숫자) — 기본 0
placeholder 플레이스홀더
name 폼 name
readonly / disabled 상태
required 필수 표시
textAlign 정렬 — 기본 'right'
size / appearance 크기·스타일
masking 마스킹 (숫자 필드에선 쓸 일 거의 없음)
maxLength 최대 글자 수 (숫자에도 적용)

⚠️ type 속성 변경 불가 — this.type = 'number'로 강제. text로 쓰려면 Va.Input 사용.

⚠️ numberComma 미지원 — Va.Input의 콤마 포맷 기능은 여기 없음. 콤마가 필요하면 Va.Input + numberComma:true + type:'number' 조합을 검토.


4. step — 증감 단위

브라우저 스피너 클릭 시(그리고 위/아래 화살표 키) 값이 얼마씩 바뀔지 결정합니다.

// 정수만 (기본)
{ tagName: 'number', step: 1 }

// 0.1 단위
{ tagName: 'number', step: 0.1, decimalPlaces: 1 }

// 100 단위 (금액 등)
{ tagName: 'number', step: 100, value: 1000 }

// 자유 입력 (검증만)
{ tagName: 'number', step: 'any' }

주의: step은 스피너/화살표 단위만 강제하는 것이지, 사용자가 키보드로 다른 값을 입력하는 걸 막지는 않습니다. 브라우저는 form submit 시에만 step에 어긋난 값을 invalid로 표시합니다.


5. decimalPlaces — 소수점 자릿수 강제

Number가 Input/Search와 크게 다른 지점입니다. keyup마다 값을 지정 자릿수로 반올림해 다시 세팅합니다 (va_component.js:1278-1284):

this._bindEvent(this.fieldElement,'keyup', (evt) => {
    let value = this.fieldElement.value;
    if (Va.Util.isNotBlank(this.decimalPlaces)) {
        this.value = Va.Util.getFixedNumber(value, this.decimalPlaces);
        this.setValue(this.value);
    }
});

사용 예

// 소수 둘째자리까지
{ tagName: 'number', decimalPlaces: 2, step: 0.01, value: 3.14 }

// 정수만
{ tagName: 'number', decimalPlaces: 0 }

// 소수 넷째자리까지 (환율 등)
{ tagName: 'number', decimalPlaces: 4, step: 0.0001 }

setValue()도 자동으로 자릿수를 강제합니다 (va_component.js:1307-1312).

⚠️ 입력 중 사용자 경험이 어색할 수 있음 — 사용자가 1.239를 다 못 치기 전에 반올림이 일어나면 입력이 튈 수 있음. 실시간 반올림이 문제라면 blur 시점으로 옮기는 커스터마이즈 검토.


6. min / max — 범위 제한

HTML 표준 속성으로 반영됩니다. 브라우저 스피너와 form validation에만 실효가 있고, 키보드 직접 입력은 막지 못합니다.

{
    tagName: 'number',
    min: 0,
    max: 100,
    value: 50
}

정확한 범위 강제가 필요하면:

  • change 이벤트에서 component.getValue()를 체크 후 벗어나면 clamp
  • 또는 NumberField로 감싸 setValidation()으로 에러 메시지 표시
onChange(component, element, evt) {
    const v = Number(component.getValue());
    if (v < component.min) component.setValue(component.min);
    if (v > component.max) component.setValue(component.max);
}

7. 이벤트 (PureField 상속)

Input과 동일:

이벤트시그니처

change (component, element, evt)
focus / blur (component, element, evt) — blur 200ms debounce
click (component, element, evt)
keydown (component, element, keyCode, evt)
keyup (component, element, keyCode, evt)  decimalPlaces 반올림이 여기서 발생
keypress / contextmenu / mousedown 표준

주목: decimalPlaces가 켜져 있으면 keyup 때마다 값이 재세팅되면서 change도 함께 튀는 상황이 생길 수 있습니다.


8. 메서드 (PureField 상속 + 오버라이드)

메서드설명

getValue() 현재 값 반환 — 문자열로 반환됨 (fieldElement.value가 항상 string). 숫자 계산엔 Number(component.getValue()) 필요
setValue(value) 오버라이드됨 — decimalPlaces가 있으면 자동 반올림 후 세팅
setDisabled(bool) / disable() / enable() 상태
setReadOnly(bool) / setReadonly(bool) 읽기 전용
focus() / blur() 포커스 제어

9. 내부 구조

Input과 동일한 PureField 구조:

<div elname="element" class="va-input [size] [focused] ..." tag-name="input" field="true">
  <div elname="fieldWrapper" class="field-wrapper">
    <input elname="field" type="number" step="1" min="..." max="..." style="border:0px">
    <div elname="focusLine" class="focus-line"></div>
  </div>
</div>

차이는 <input>의 type="number" 속성과 step/min/max 속성이 붙는 것뿐.


10. 브라우저 스피너 (위/아래 화살표) 특성

<input type="number">의 표준 UI인데, 프로젝트에 따라 숨기고 싶을 때가 많습니다.

숨기는 CSS (자주 필요):

/* Chrome, Safari, Edge */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
/* Firefox */
input[type="number"] {
    -moz-appearance: textfield;
}

프로젝트 CSS(va.css 또는 테마)에 이미 정의되어 있을 수 있으니 확인해 보세요.


11. 언제 쓰나

Number가 맞을 때

  • 수량·개수·나이 입력 (정수)
  • 금액 입력 (스피너 없이 쓰거나, step:1000 등)
  • 소수점 자릿수를 강제해야 하는 값 (평점 3.5, 환율 등)
  • 모바일에서 숫자 키패드가 뜨면 좋을 때
  • 브라우저 스피너로 조작 편의 제공

다른 걸 쓸 때

  • 스피너 + 별도 -/+ 버튼이 필요하면 → Va.NumberWithButton (같은 계열의 확장 컴포넌트)
  • 라벨 붙은 폼 필드 → Va.NumberField
  • 금액에 콤마 포맷(1,234,567) → Va.Input + numberComma:true
  • 큰 정수·전화번호 등 숫자지만 계산 안 하는 값 → Va.Input (type:'tel' 검토)

12. 알아두면 좋을 주의사항

  1. getValue()는 문자열 반환 — 계산엔 Number() 변환 필수. 특히 + 연산자로 문자열 연결 사고 조심.
  2. min/max는 키보드 입력 못 막음 — 정확한 범위 강제는 change 콜백에서 처리 필요.
  3. decimalPlaces 실시간 반올림 UX — 사용자가 소수 입력 중일 때 값이 튀는 느낌이 있음. 필요하면 blur 시점 반올림으로 커스터마이즈.
  4. 초기 value가 0 — 옵션에 value를 안 넘기면 0이 표시됨. Input처럼 빈 값으로 시작하고 싶으면 value: '' 명시.
  5. 초기 textAlign이 'right' — 숫자 필드 관행. 왼쪽 정렬이 필요하면 textAlign: 'left' 명시.
  6. type 변경 불가 — 필요하면 Input 사용.
  7. numberComma 미지원 — 콤마 포맷은 여기 없음.
  8. 크롬 스피너가 폼 폭 잡아먹음 — 얇은 필드에선 스피너 CSS로 숨기는 게 낫습니다.
  9. 한글 IME/음성 입력  type="number"는 문자 입력을 아예 차단하는 브라우저가 많아 IME 조합 이슈 최소화 효과.
  10. step="any" — 자유 소수 입력 허용 (기본은 정수만 스피너로 조작).

13. 흔한 조합 예시

// 수량 (0~999)
{ tagName: 'number', min: 0, max: 999, step: 1, value: 1 }

// 평점 (0.0~5.0)
{ tagName: 'number', min: 0, max: 5, step: 0.1, decimalPlaces: 1, value: 3.5 }

// 금액 (1000원 단위)
{ tagName: 'number', min: 0, step: 1000, value: 10000, textAlign: 'right' }

// 환율 (소수 넷째자리)
{ tagName: 'number', step: 0.0001, decimalPlaces: 4, value: 1300.1234 }

// 나이 (정수만, 상한)
{ tagName: 'number', min: 0, max: 150, step: 1, value: 25 }

'컴포넌트 > 필드 컴포넌트' 카테고리의 다른 글

ColorPicker(색상선택)  (0) 2026.09.10
NumberField (숫자필드)  (0) 2026.09.10
SearchField (검색필드)  (0) 2026.09.10
Search (검색)  (0) 2026.09.10
InputField (입력필드)  (0) 2026.09.09