컴포넌트/필드 컴포넌트

TimePicker (시각선택)

VanillaFront 2026. 9. 12. 19:07

Va.TimePicker — 시간(시·분·초) 선택 팝업 필드

시각 하나만 선택하는 컴포넌트입니다. 좌측에 마스킹 필드(__:__:__), 우측에 시계 아이콘이 있고, 아이콘 클릭 또는 Enter 시 시·분·초 드롭다운이 있는 팝업이 열립니다. DateTimePicker에서 시간 파트만 떼어낸 형태.

  • 클래스: Va.TimePicker  va_component.js:9030
  • short name: timePicker
  • 상속: Va.PureField (DatePicker·MonthPicker와 형제)
  • isContainer: true
  • 베이스 CSS: va-timepicker (팝업 va-menu-pop, calendar-inner)


1. 기본 사용

{
    tagName: 'timePicker',
    value: '143000',              // HHMMSS 6자리
    onSelect: 'onTimeChange'
}
  • 화면 표시: 14:30:00
  • 저장 값(getValue()): 143000
  • 팝업: 시·분·초 각각의 <select> 드롭다운 + "지금" / "Confirm" 버튼

2. 다른 PureField 계열과의 차이

항목Va.DatePickerVa.DateTimePickerVa.TimePicker

입력 대상 년-월-일 년-월-일 + 시분초 시분초만
저장 값 길이 8자리 14/12자리 6/4자리
마스킹 ____-__-__ ____-__-__ __:__:__ __:__:__
팝업 UI 일 단위 캘린더 캘린더 + 시분초 시·분·초 3개 드롭다운
아이콘 캘린더 (📅) 캘린더 시계 (🕐 ico_clock_fill)
timeFormat 옵션
popWidth 기본 260 260 240

한 줄 요약: "시각만 필요한 상황을 위한 심플한 팝업 필드."


3. 주요 속성

시간 포맷

속성기본값설명

timeFormat 'hhmmss' 시간 표시 형식. hhmmss(초 포함) / hhmm(분까지) / hm(분까지 축약)
timeSeperator ':' 시간 구분자
valueTimeFormat 저장 시간 형식
valueTimeSeperator 저장 시간 구분자 (기본 없음)
masking 자동 (__:__:__ 또는 __:__) timeFormat에 따라 자동

팝업

속성기본값설명

popWidth 240 팝업 폭
expanded false 초기 상태

PureField 상속

value, placeholder, readonly, disabled, size, appearance, stopPropagation 등 표준.


4. 저장 값 길이 — timeFormat에 따라 달라짐

timeFormat화면 표시저장 값 (getValue())길이

hhmmss (기본) 14:30:00 143000 6자리
hhmm / hm 14:30 1430 4자리

valueTimeSeperator 지정 시 구분자가 저장 값에 삽입:

{
    valueTimeSeperator: ':'
}
// 저장: '14:30:00' (초 포함) 또는 '14:30' (분까지)

5. 이벤트

이벤트시그니처발생 시점

select (component, element, value, evt) 팝업의 "Confirm" 버튼 클릭 시. value는 원시 값(구분자 없음)
beforePop / afterPop / hidePop 팝업 표시/숨김  
expand / collapse 팝업 확장/축소  
focus / blur 표준  

⚠️ 다른 Picker와 다른 점 — DatePicker는 날짜 클릭 시 즉시 select 발생인데, TimePicker는 드롭다운 선택 후 "Confirm" 버튼 클릭 시 select 발생. 팝업이 바로 닫히지 않고 사용자가 확정 버튼을 눌러야 완료.


6. 메서드 — 두 벌의 setter/getter

DatePicker·DateTimePicker와 동일한 패턴:

메서드설명

setValue(value) valueTimeSeperator가 있으면 제거 후 세팅
getValue() valueTimeSeperator 규약 형태로 반환. 마스킹 언더스코어 남으면 ''
setRawValue(value) 원시 값(구분자 없이) 그대로 세팅
getRawValue() 원시 값(구분자 없이) 그대로 반환

팝업 제어

메서드설명

showTimePickerPop(evt) / hideTimePickerPop() 팝업 제어
showPop() / expand() drawCalendar() 호출 (레거시 별칭)
hidePop() / collapse() 팝업 숨김
drawCalendar(value) 팝업 UI 렌더링 (이름은 Calendar지만 시간 드롭다운을 그림 — DatePicker에서 복사된 잔재)

상태

메서드설명

setDisabled(bool) / setReadOnly(bool) PureField 상속
focus() / blur() 포커스

7. 팝업 UI 구조

drawCalendar()가 렌더링 (이름은 Calendar지만 실제로는 시간 드롭다운):

┌─────────────────────────────┐
│  [ 14 ▼ ] : [ 30 ▼ ] : [ 00 ▼ ]  │  ← 3개 <select> 드롭다운
│    (0~23)   (0~59)   (0~59)      │
├─────────────────────────────┤
│      지금       Confirm         │  ← 하단 버튼
└─────────────────────────────┘
  • : 0 ~ 23 (24시간 형식, 앞자리 0 패딩)
  • 분·초: 0 ~ 59
  • "지금" 버튼: 현재 시각으로 세팅
  • "Confirm" 버튼: 값 확정 + select 이벤트 + 팝업 닫힘

⚠️ 12시간 형식(AM/PM)은 지원 안 됨 — 항상 24시간 형식. 12시간 UI가 필요하면 커스터마이즈.


8. 내부 구조

<div elname="element" class="va-timepicker [size]..." tag-name="timePicker" field="true">
  <div elname="fieldWrapper" class="field-wrapper">
    <input elname="field" type="text" placeholder="__:__:__" style="border:0px">
    <div elname="focusLine" class="focus-line"></div>
    <div elname="clockIconWrapper">
      <span elname="clockIcon" class="icon menu ico_clock_fill">🕐</span>
    </div>
  </div>
  <div elname="popDiv" class="menu-button-pop-div">
    <div elname="pop" class="va-menu-pop" style="position:absolute; display:none">
      <div elname="popInner" class="calendar-inner">
        <!-- 시·분·초 select + 하단 버튼 -->
      </div>
    </div>
  </div>
</div>

9. 팝업 상태머신 연동

프레임워크 공용 팝업 상태머신 참여:

  • Va.addAutoHide(this.popElement) — 외부 클릭 시 자동 닫힘
  • Va.hideOtherComponents(this) — 다른 팝업 자동 닫음
  • getHiddenAreaElement()로 팝업 이동 → 부모 stacking 회피
  • 뷰포트 넘침 시 위로 자동 뒤집기

10. getValue()의 부분 입력 방어

DateTimePicker와 마찬가지로 필드에 마스킹 언더스코어가 남아 있으면 getValue()가 '' 반환:

  • timeFormat: 'hhmmss' → 완전한 값은 8자리 (구분자 포함), 미완성이면 ''
  • timeFormat: 'hhmm' / 'hm' → 완전한 값은 5자리, 미완성이면 ''

의미: 14:__:__ 같은 미완성 상태로 폼 제출해도 서버로 부분 값이 안 감. 다만 잘못된 형식(99:99:99)은 걸러지지 않음 — 별도 검증 필요.


11. 언제 쓰나

TimePicker가 맞을 때

  • 근태 출근·퇴근 시각 입력
  • 회의 시간대 선택 (14:00~15:30 같은 필드에)
  • 알람·리마인더 시각
  • 영업 시간 설정 (09:00 ~ 18:00)
  • 시각만 필요하고 날짜는 무의미할 때

다른 걸 쓸 때

  • 날짜도 필요 → Va.DateTimePicker (같이 다룸)
  • 날짜만 필요 → Va.DatePicker
  • 시작~종료 시각 → 두 TimePicker 조합 (전용 Range 컴포넌트는 별도 확인)
  • 라벨 붙은 폼 필드 → Va.TimeField
  • 12시간 AM/PM UI 필요 → 커스텀 (기본 지원 안 됨)
  • 15분·30분 단위만 (스텝) → Va.ComboboxField에 옵션 배열 (분 단위 스텝 옵션 없음)

12. 흔한 조합 예시

// 초까지 (표준)
{ tagName: 'timePicker', value: '143000' }

// 분까지만
{
    tagName: 'timePicker',
    timeFormat: 'hhmm',
    value: '1430'                // 화면: 14:30, 저장: 1430
}

// 서버가 콜론 포함 원할 때
{
    tagName: 'timePicker',
    valueTimeSeperator: ':',
    value: '14:30:00'            // 화면: 14:30:00, 저장: 14:30:00
}

// 오전 9시로 초기화
mounted() {
    this.getRef('startTime').setValue('090000');
}

// 지금 시각으로 초기화
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: 'timePicker',
    popWidth: 320,
    value: '143000'
}

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

  1. value는 4자리 또는 6자리 문자열 — timeFormat에 따라. 숫자 넘기면 .substring 크래시.
  2. setValue() null 안전성 없음 — null 넘기면 .replaceAll 호출에서 크래시. Date/DateTimePicker처럼 조기 return 없음.
  3. getValue()가 미완성 값에 '' 반환 — 마스킹 언더스코어 있으면 빈 문자열.
  4. getValue()는 잘못된 형식 검증 안 함  99:99:99 같은 값도 완성되면 그대로 반환.
  5. 12시간 AM/PM 미지원 — 24시간 형식 고정.
  6. 분·초 세밀 단위 제한 없음 — 1분 단위. "15분 스텝" 등 옵션 없음.
  7. 팝업 즉시 확정 아님 — "Confirm" 버튼 클릭해야 select 이벤트 + 값 반영.
  8. timeFormat 코드가 대소문자 혼용 — 예: 'HHmm' (대문자 H, 소문자 mm) 조건이 코드에 있음 (va_component.js:9294). 실제로는 hhmm/hm 소문자를 씀. 일관성 부족.
  9. drawCalendar() 이름 부적절 — DatePicker에서 복사된 잔재. 실제로는 시간 드롭다운을 그림.
  10. "지금" 버튼 코드에 버그성 잔재  Va.Util.getNowYear() + getNowMonth() + getNowDay()로 날짜를 세팅함 (va_component.js:9282-9284). DatePicker에서 복사됨. 실제 클릭 시 예상과 다른 값이 들어갈 수 있으니 확인 필요.
  11. 팝업 상태머신 참여 — 다른 팝업과 자동 상호 배타.
  12. 아이콘이 시계 — DatePicker의 캘린더 아이콘과 다름 (ico_clock_fill).
  13. timezone 없음 — 로컬 시각 문자열만.

14. 실전 예 — 근태 출퇴근 시각

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 || !end) {
            new Va.Alert({
                title: '오류',
                message: '출근·퇴근 시각을 모두 입력하세요'
            }).show(this);
            return;
        }

        // 서버로 전송 (6자리 문자열)
        AttendanceService.save(this, {
            date:      '20241225',
            startTime: start,   // '090000'
            endTime:   end      // '180000'
        }, 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-center gap-m',
                        tags: [
                            {
                                tagName: 'div',
                                layout: 'ds-flex fd-column gap-xs',
                                tags: [
                                    { tagName: 'label', innerHTML: '출근' },
                                    { tagName: 'timePicker', ref: 'startTime' }
                                ]
                            },
                            {
                                tagName: 'div',
                                layout: 'ds-flex fd-column gap-xs',
                                tags: [
                                    { tagName: 'label', innerHTML: '퇴근' },
                                    { tagName: 'timePicker', ref: 'endTime' }
                                ]
                            }
                        ]
                    },
                    {
                        tagName: 'button',
                        text: '저장',
                        appearance: 'primary',
                        onClick: 'onSubmit'
                    }
                ]
            }]
        };
    }
}

15. TimeField가 필요하면

라벨·검증까지 필요한 폼 필드로 쓰려면 Va.TimeField (Field 계열 래퍼)를 사용하세요:

{
    tagName: 'timeField',
    label: '출근 시각',
    value: '090000',
    required: true
}

DateField·MonthField와 같은 Composition 패턴으로, 내부에 Va.TimePicker를 소유합니다.

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

Checkbox (체크박스)  (0) 2026.09.12
TimeField (시각필드)  (0) 2026.09.12
MonthField (년월필드)  (0) 2026.09.12
MonthPicker (월선택)  (0) 2026.09.12
YearField (연도필드)  (1) 2026.09.11