TimeField (시각필드)
Va.TimeField — 라벨 + 시간(시·분·초) 선택 필드
Va.TimePicker가 순수 시간 입력이라면, Va.TimeField는 그 위에 라벨·필수 표시·검증 메시지를 얹은 완성 폼 필드입니다. Field 계열 아키텍처 그대로, 내부에 Va.TimePicker를 소유하는 Composition 구조. 근태·회의 시간대·영업 시간 같은 폼 필드에 최적화되어 있어요.
- 클래스: Va.TimeField — va_component.js:8873
- short name: timeField
- 상속: Va.Field (다른 Field 형제들과 같음)
- 내부 컴포넌트: Va.TimePicker 인스턴스 (fieldComponent)
- isContainer: true
- 베이스 CSS: va-field (role="time-field" 자동 부여 — 다른 Date 계열과 다름!)

1. 기본 사용
{
tagName: 'timeField',
label: '출근 시각',
value: '090000', // HHMMSS 6자리
required: true,
onSelect: 'onTimeChange'
}
라벨 + 검증 + 시·분·초 드롭다운 팝업이 한 번에 세팅. 저장 값은 6자리 또는 4자리 문자열 (timeFormat에 따라).
2. Field 계열에서의 위치
Va.Field
├─ Va.InputField ← Va.Input
├─ Va.SearchField ← Va.Search
├─ Va.NumberField ← Va.Number
├─ Va.ComboboxField ← Va.Combobox
├─ Va.YearField ← Va.YearPicker (년만)
├─ Va.MonthField ← Va.MonthPicker (년-월)
├─ Va.DateField ← Va.DatePicker (년-월-일)
├─ Va.DateTimeField ← Va.DateTimePicker (년-월-일 + 시분초)
├─ Va.TimeField ← Va.TimePicker (시분초만) ← 이 문서
└─ ...
TimeField의 정체: Field 베이스 + 내부에 Va.TimePicker 인스턴스. Date 계열 Field 중 유일하게 role="time-field" 를 부여합니다.
3. Va.TimePicker / 다른 Date 계열 Field와의 차이
항목Va.TimePickerVa.TimeFieldVa.DateFieldVa.DateTimeField
| 라벨 | ✕ | ✓ | ✓ | ✓ |
| 검증 메시지 | ✕ | ✓ | ✓ | ✓ |
| info 툴팁 | ✕ | ✓ | ✓ | ✓ |
| 입력 대상 | 시분초 | 시분초 | 년-월-일 | 년-월-일 + 시분초 |
| 저장 값 길이 | 6/4자리 | 6/4자리 | 8자리 | 14/12자리 |
| 팝업 UI | 시·분·초 드롭다운 | 시·분·초 드롭다운 (내부 위임) | 캘린더 | 캘린더 + 시분초 |
| role="time-field" 자동 | ✕ | ✓ (유일) | date-field | date-field |
| change/keydown 시 검증 자동 리셋 | ✕ | ✓ | ✓ | ✓ |
한 줄 요약: "폼 안 라벨 붙은 시각 필드 — 근태·회의·영업 시간에 최적."
4. 주요 속성
시간 포맷
속성기본값설명
| timeFormat | 'hhmmss' | 시간 표시 형식. hhmmss / hhmm / hm |
| timeSeperator | ':' | 시간 구분자 |
| valueTimeFormat | — | 저장 시간 형식 |
| valueTimeSeperator | — | 저장 시간 구분자 |
| masking | 자동 (__:__:__ 또는 __:__) | timeFormat에 따라 자동 |
팝업
속성기본값설명
| popWidth | 240 | 팝업 폭 |
| expanded | false | 초기 상태 |
라벨 (Field 상속)
속성설명
| label | 라벨 텍스트 또는 객체 |
| labelPosition | top / bottom / left / right |
| labelWidth | 라벨 폭 |
| noLabel | 라벨 숨김 |
| infoButton | info 아이콘 |
| required | 필수 표시 |
필드 관련 (TimePicker로 위임)
속성설명
| value | 시각 값 (HHMMSS 또는 HHMM 문자열) |
| placeholder | 플레이스홀더 |
| readonly / disabled | 상태 |
| size / appearance / shape | 시각 스타일 |
| textAlign | 정렬 |
| stopPropagation | 이벤트 버블링 |
검증
속성설명
| validation | {state, size, message} |
| validationState | success / warning / error |
| validationMessage | 메시지 |
세부 커스터마이즈 (timePicker 옵션 키)
{
tagName: 'timeField',
label: '출근',
timePicker: { // ← 내부 TimePicker에 전달
popWidth: 320
}
}
TimeField는 옵션 키가 정확히 timePicker — 다른 Field 형제와 마찬가지로 자기 내부 Picker 이름을 씀. MonthField(monthPicker)와 같은 관행.
각 Field 계열 옵션 키:
- InputField → input
- ComboboxField → combobox
- DateField / DateTimeField → datePicker
- YearField → monthPicker (잔재)
- MonthField → monthPicker
- TimeField → timePicker
⚠️ 참고: TimeField는 생성자에서 내부적으로 Va.MenuButton도 하나 만들어둡니다 (va_component.js:8899-8910). 옵션 menuButton으로 세부 조정 가능 — 실사용에선 잘 안 씀.
5. 저장 값 길이
timeFormat에 따라 달라집니다:
timeFormat화면 표시저장 값길이
| hhmmss (기본) | 14:30:00 | 143000 | 6자리 |
| hhmm / hm | 14:30 | 1430 | 4자리 |
valueTimeSeperator 지정 시 저장 값에도 구분자 삽입:
{ valueTimeSeperator: ':' }
// 저장: '14:30:00' 또는 '14:30'
6. 이벤트
TimePicker의 이벤트를 재발화:
이벤트시그니처발생 시점
| select | (component, element, value, evt) | 팝업 "Confirm" 클릭 시. value는 원시 값 |
| beforePop / afterPop / hidePop | 팝업 표시/숨김 | |
| expand / collapse | 팝업 확장/축소 | |
| focus / blur | 표준 | |
| change / keydown | Field 표준 (검증 자동 리셋) |
⚠️ select 이벤트 dispatch가 두 번 — 소스 va_component.js:8961과 va_component.js:8991에서 각각 등록. 첫 번째는 짧은 인자(this, this.element, evt), 두 번째는 긴 인자(this, this.element, value, evt). 콜백 중복 실행 위험. 방어 코드 필요.
⚠️ TimePicker의 즉시 선택 아님 — 팝업의 시·분·초 드롭다운을 조작해도 이벤트 안 나옴. "Confirm" 버튼을 눌러야 select 발생 + 값 반영.
7. 메서드
값 관리 (Field 상속 그대로)
메서드설명
| getValue() | 내부 TimePicker의 getValue() 위임 (valueTimeSeperator 규약, 미완성이면 '') |
| setValue(value) | Field 상속 — 내부 TimePicker에 위임 |
주목: TimeField는 setValue() / getValue()를 오버라이드하지 않습니다. 값 처리 전체를 내부 TimePicker에 맡겨요.
상태 (Field 상속)
메서드설명
| setDisabled(bool) / getDisabled() | 비활성화 |
| setReadOnly(bool) / setReadonly(bool) | 읽기 전용 |
| setLabel(label) | 라벨 변경 |
| setPlaceholder(text) | 플레이스홀더 |
| setSize(size) | 크기 |
검증
메서드설명
| setValidation(state, message) | 검증 표시 + aria |
| clearValidation() | 검증 해제 |
포커스
메서드설명
| focus() / blur() | 내부 TimePicker의 fieldElement에 위임 |
주의: showTimePickerPop, hideTimePickerPop, setRawValue, getRawValue 같은 TimePicker 세부 메서드는 위임 없음. 필요하면 component.fieldComponent.xxx()로 직접.
8. 내부 구조
<div elname="element" class="va-field [vertical|horizontal]"
role="time-field" field="true">
<div elname="inner" class="field-inner">
<div elname="labelDiv" class="label-div">
<label cpname="label">출근 시각 <span class="required">*</span></label>
</div>
<div elname="comment" class="field-comment"></div>
<div elname="fieldDiv" class="field-div">
<div cpname="field" class="va-timepicker"> ← 내부 Va.TimePicker
<div class="field-wrapper">
<input type="text" placeholder="__:__:__">
<div class="focus-line"></div>
<div class="clock-icon-wrapper">
<span class="icon menu ico_clock_fill">🕐</span>
</div>
</div>
<!-- 팝업은 hiddenArea로 이동 -->
</div>
</div>
</div>
<div elname="validationDiv" style="display:none">
<div class="va-validation">...</div>
</div>
</div>
주목: role="time-field" — Date 계열 Field(date-field)와 유일하게 다름. 스크린리더가 시간 필드로 정확히 인식.
9. 언제 쓰나
TimeField가 맞을 때
- 폼 안 근태 출퇴근 시각 입력
- 회의 시간대 선택 (13:00 회의 등)
- 알람·리마인더 시각
- 영업 시간 설정 (오픈·마감)
- 알림 발송 시각 등록
- 라벨·필수·검증이 필요한 시각 필드
다른 걸 쓸 때
- 라벨 없이 인라인 → Va.TimePicker
- 날짜도 필요 → Va.DateTimeField (조합 컴포넌트)
- 시작~종료 시각 → 두 TimeField 조합
- 15분·30분 단위만 → Va.ComboboxField (스텝 옵션 없음)
- 12시간 AM/PM → 커스텀 (기본 지원 안 됨)
10. 흔한 조합 예시
// 표준 (초까지)
{
tagName: 'timeField',
label: '출근 시각',
value: '090000',
required: true
}
// 분까지만
{
tagName: 'timeField',
label: '알람 시각',
timeFormat: 'hhmm',
value: '0700'
}
// 서버가 콜론 포함 원할 때
{
tagName: 'timeField',
label: '오픈 시간',
valueTimeSeperator: ':',
value: '09:00:00'
}
// 좌측 라벨
{
tagName: 'timeField',
label: '퇴근 시각',
labelPosition: 'left',
labelWidth: 100,
value: '180000'
}
// info 툴팁
{
tagName: 'timeField',
label: '알림 시각',
infoButton: {
tooltip: 'KST 기준 시각으로 발송됩니다'
}
}
// 지금 시각으로 초기화
mounted() {
const now = new Date();
const hhmmss = String(now.getHours()).padStart(2, '0') +
String(now.getMinutes()).padStart(2, '0') +
String(now.getSeconds()).padStart(2, '0');
this.getRef('time').setValue(hhmmss);
}
// 검증
{
tagName: 'timeField',
label: '회의 시작',
ref: 'startTime',
required: true,
onSelect: 'onStartTimePicked'
}
11. 알아두면 좋을 주의사항
- 컴포넌트명 — timeField (not timePickerField).
- role="time-field"가 유일하게 다름 — Date 계열 Field는 모두 date-field인데 TimeField만 time-field. 스크린리더 대응은 오히려 이쪽이 정확.
- value는 4자리 또는 6자리 문자열 — timeFormat에 따라. Date 객체 넘기면 크래시.
- select 이벤트 두 번 dispatch — 콜백 방어 필요.
- "Confirm" 버튼 눌러야 값 확정 — 팝업 드롭다운 조작만으로는 안 됨. DatePicker와 UX 다름.
- getValue()가 미완성 값에 '' 반환 — 마스킹 언더스코어 있으면 빈 문자열.
- 잘못된 형식 검증 없음 — 99:99:99 같은 값도 완성되면 그대로 반환.
- 12시간 AM/PM 미지원 — 24시간 형식 고정.
- 15분·30분 스텝 미지원 — 1분 단위. 별도 스텝 옵션 없음.
- 옵션 키 timePicker — 내부 커스터마이즈 시 이 키 사용.
- popWidth 기본 240 — 시·분·초 3개 드롭다운엔 충분.
- change/keydown 시 검증 자동 리셋 — 사용자가 값 수정하면 이전 에러 자동으로 사라짐.
- 일부 메서드 위임 누락 — showPop/hidePop/setRawValue/getRawValue. fieldComponent로 직접.
- timezone 없음 — 로컬 시각 문자열만.
- fieldElement = this.tagName = 'timeField' 문법 오류성 코드 — va_component.js:8876-8877 에 this.fieldElement = this.tagName = 'timeField' — 표면적으로는 동작하나 이상한 초기화. 이후 this.fieldElement = this.fieldComponent.fieldElement로 재할당되어 실질적 문제는 없음.
12. 실전 예 — 근태 출퇴근 시각 (Field 버전)
TimePicker 편의 예제를 Field로 감싼 버전:
class Attendance extends Va.View {
mounted() {
this.getRef('startTime').setValue('090000');
this.getRef('endTime').setValue('180000');
}
onSubmit(btn, el, evt) {
const start = this.getRef('startTime').getValue();
const end = this.getRef('endTime').getValue();
if (!start) {
this.getRef('startTime').setValidation('error', '출근 시각을 입력하세요');
return;
}
if (!end) {
this.getRef('endTime').setValidation('error', '퇴근 시각을 입력하세요');
return;
}
if (Number(end) <= Number(start)) {
this.getRef('endTime').setValidation('error', '퇴근 시각이 출근 시각보다 늦어야 합니다');
return;
}
AttendanceService.save(this, {
date: '20241225',
startTime: start,
endTime: end
}, this.onSaved);
}
onSaved(view, ok, res) {
if (ok) new Va.Alert({ title: '완료', message: '저장되었습니다' }).show(view);
}
config() {
return {
tagName: 'page',
tags: [{
tagName: 'panel',
tags: [
{ tagName: 'h2', innerHTML: '근태 등록' },
{
tagName: 'div',
layout: 'ds-flex fd-row ai-end gap-m',
tags: [
{
tagName: 'timeField',
ref: 'startTime',
label: '출근 시각',
required: true,
style: { width: '180px' }
},
{
tagName: 'timeField',
ref: 'endTime',
label: '퇴근 시각',
required: true,
style: { width: '180px' }
}
]
},
{
tagName: 'button',
text: '저장',
appearance: 'primary',
onClick: 'onSubmit'
}
]
}]
};
}
}
TimePicker 단독 사용과 비교하면 라벨 + 필수 표시 + 검증 메시지 + ARIA 시각 필드 role이 자동 세팅되어 폼 UX가 자연스럽습니다.
13. timeField vs dateTimeField 선택 기준
상황추천
| 근태 출퇴근 시각 (날짜는 별도 필드) | timeField |
| 알람 시각, 리마인더 시각 | timeField |
| 영업 시간 설정 (오픈·마감) | timeField |
| 회의 시간 (요일 반복) | timeField |
| 특정 날짜의 특정 시각 (예약, 마감) | dateTimeField |
| 로그 타임스탬프 편집 | dateTimeField |
| 게시글 예약 발행 | dateTimeField |
"날짜가 매일 반복되는 시각"이면 timeField, "특정 날짜의 시각"이면 dateTimeField.