컴포넌트/필드 컴포넌트

DateTimeField (일시필드)

VanillaFront 2026. 9. 11. 14:07

Va.DateTimeField — 라벨 + 캘린더 팝업 + 시간까지

Va.DateTimePicker가 순수 날짜+시간 입력이라면, Va.DateTimeField는 그 위에 라벨·필수 표시·검증 메시지까지 얹은 완성 폼 필드입니다. Field 계열 아키텍처 그대로, 내부에 Va.DateTimePicker를 소유하는 Composition 구조.

  • 클래스: Va.DateTimeField  va_component.js:8301
  • short name: dateTimeField
  • 상속: Va.Field (다른 Field 형제들과 같음)
  • 내부 컴포넌트: Va.DateTimePicker 인스턴스 (fieldComponent)
  • isContainer: true
  • 베이스 CSS: va-field (role="date-field" 자동 부여)


1. 기본 사용

{
    tagName: 'dateTimeField',
    label: '예약 시각',
    value: '20241225143000',              // YYYYMMDDHHMMSS
    required: true,
    onSelect: 'onReservationTimeChange'
}

라벨 + 검증 + 캘린더 팝업 + 시분초 입력이 한 번에 세팅. 사용법은 DateField와 거의 동일하지만 저장 값이 14자리 (또는 12자리) 로 늘어난다는 게 차이.


2. Field 계열에서의 위치

Va.Field
   ├─ Va.InputField          ← Va.Input
   ├─ Va.SearchField         ← Va.Search
   ├─ Va.NumberField         ← Va.Number
   ├─ Va.ComboboxField       ← Va.Combobox
   ├─ Va.DateField           ← Va.DatePicker         (날짜만)
   ├─ Va.DateTimeField       ← Va.DateTimePicker     ← 이 문서 (날짜+시간)
   └─ ...

DateTimeField의 정체: Field 베이스 + 내부에 Va.DateTimePicker 인스턴스.

⚠️ 이름 주의: DateField ← DatePicker, DateTimeField ← DateTimePicker — 다른 Field 형제와 달리 "Picker"가 이름에서 생략됩니다. 등록명은 dateTimeField.


3. Va.DateTimePicker / Va.DateField와의 차이

항목Va.DateTimePickerVa.DateTimeFieldVa.DateField

라벨
검증 메시지
info 툴팁
날짜 입력
시간 입력
저장 값 길이 14/12자리 14/12자리 8자리
role="date-field" 자동

한 줄 요약: "폼 안 라벨 붙은 날짜+시간 필드."


4. 주요 속성

날짜 포맷 (DateField와 공통)

속성기본값설명

dateFormat 'ymd' 화면 날짜 순서 — ymd / mdy / dmy
dateSeperator '-' 화면 날짜 구분자
valueDateFormat 'ymd' 저장 값의 날짜 순서
valueDateSeperator '' 저장 값의 날짜 구분자

시간 포맷 (DateTimeField 전용)

속성기본값설명

timeFormat 'hhmmss' 시간 표시 형식. hhmmss(초 포함) / hhmm(분까지) / hm(분까지 축약)
timeSeperator ':' 시간 구분자
valueTimeFormat 저장 시간 형식
valueTimeSeperator 저장 시간 구분자

날짜-시간 결합

속성기본값설명

dateTimeSeperator ' ' (공백) 화면에서 날짜와 시간 사이
valueDateTimeSeperator '' 저장 값에서 날짜와 시간 사이

팝업

속성기본값설명

popWidth 240 팝업 폭 (⚠️ DatePicker/DateTimePicker는 260)
expanded false 초기 상태
min / max 최소/최대 날짜시간

라벨 (Field 상속)

속성설명

label 라벨 텍스트 또는 객체
labelPosition top / bottom / left / right
labelWidth 라벨 폭
noLabel 라벨 숨김
infoButton info 아이콘
required 필수 표시

검증

속성설명

validation {state, size, message}
validationState success / warning / error
validationMessage 메시지

세부 커스터마이즈 (datePicker 옵션 키)

{
    tagName: 'dateTimeField',
    label: '예약 시각',
    datePicker: {                 // ← 내부 DateTimePicker에 전달
        additionalInfo: '...'
    }
}

⚠️ 옵션 키가 datePicker — DateField와 같은 키를 씀. dateTimePicker가 아님. 소스에서 복사되며 남은 흔적으로 보임.

각 Field 계열 옵션 키:

  • InputField → input
  • ComboboxField → combobox
  • TagField → tag
  • DateField / DateTimeField → datePicker (두 개가 같은 키!)

5. 저장 값 길이 — 핵심 개념

timeFormat에 따라 저장 값 길이가 달라집니다:

timeFormat저장 값 형태길이

hhmmss (기본) YYYYMMDDHHMMSS 14자리
hhmm / hm YYYYMMDDHHMM 12자리

valueDateSeperator / valueTimeSeperator / valueDateTimeSeperator 지정 시 각 자리에 구분자 삽입:

{
    valueDateSeperator: '-',
    valueTimeSeperator: ':',
    valueDateTimeSeperator: ' '
}
// 저장 값: '2024-12-25 14:30:00'

6. 이벤트

DateTimePicker의 이벤트를 재발화:

이벤트시그니처발생 시점

select (component, element, evt) 팝업에서 값 선택 시
beforePop / afterPop / hidePop 팝업 표시/숨김  
expand / collapse 팝업 확장/축소  
focus / blur 표준  
change / keydown Field 표준 (검증 자동 리셋)  

⚠️ select 콜백 시그니처가 짧음 — (component, element, evt) 3개 인자만. 값이 필요하면 component.getValue() 별도 호출.

⚠️ DateField와 달리 select 이벤트가 두 번 dispatch되지 않음 — DateField는 중복 dispatch 있었지만 DateTimeField는 한 번만. 실무엔 이 차이가 미미하지만 코드 이식할 때 유의.


7. 메서드 — 오버라이드된 setter/getter

DateField와 동일한 두 벌 setter/getter 패턴:

메서드설명

setValue(value) valueDateFormat/valueTimeFormat 규약 값 세팅
getValue() 위 규약 형태로 반환. 마스킹 언더스코어 남아 있으면 '' 반환

상태 (Field 상속)

메서드설명

setDisabled(bool) / getDisabled() 비활성화
setReadOnly(bool) / setReadonly(bool) 읽기 전용
setLabel(label) 라벨 변경
setPlaceholder(text) 플레이스홀더
setSize(size) 크기

검증

메서드설명

setValidation(state, message) 검증 표시 + aria
clearValidation() 검증 해제

포커스

메서드설명

focus() / blur() 내부 DateTimePicker의 fieldElement에 위임

주의: setRawValue, getRawValue, showPop, hidePop 등 DateTimePicker의 세부 메서드는 위임 없음. 필요하면 component.fieldComponent.xxx()로 직접.


8. 내부 구조

<div elname="element" class="va-field [vertical|horizontal]"
     role="date-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-input">            ← 내부 Va.DateTimePicker
        <div class="field-wrapper">
          <input type="text" placeholder="____-__-__ __:__:__">
          <div class="focus-line"></div>
          <div class="calendar-icon-wrapper">
            <span class="icon menu ico_calender_ltr">📅</span>
          </div>
        </div>
        <!-- 팝업은 hiddenArea로 이동 -->
      </div>
    </div>
  </div>
  <div elname="validationDiv" style="display:none">
    <div class="va-validation">...</div>
  </div>
</div>

9. getValue()의 특별한 동작 — 부분 입력 방어

DateTimePicker에서 상속받은 동작. 필드에 마스킹 언더스코어가 남아 있으면(_) getValue()가 '' 반환:

  • timeFormat: 'hhmmss' → 완전한 값은 19자리(구분자 포함), 부족하면 ''
  • timeFormat: 'hhmm' → 완전한 값은 16자리, 부족하면 ''

의미: 사용자가 2024-12-25 14:__:__처럼 미완성 상태로 제출해도 서버로 이상한 값이 안 감. 다만 잘못된 형식(9999-99-99 99:99:99)은 걸러지지 않음 — 별도 검증 필요.


10. 언제 쓰나

DateTimeField가 맞을 때

  • 폼 안 예약·일정 시각 — 회의 시작 시간, 배송 시각, 게시글 예약 발행
  • 로그·이벤트 타임스탬프 편집
  • 계약·마감 정확한 시각까지 필요한 필드
  • 라벨·필수·검증이 필요한 상황

다른 걸 쓸 때

  • 시간 필요 없음 → Va.DateField (8자리, 훨씬 간단)
  • 시간만 → Va.TimeField
  • 라벨 없는 인라인 → Va.DateTimePicker
  • 시작-종료 시각 범위 → Va.DateTimeRangeField(있다면) 또는 두 개 조합

11. 흔한 조합 예시

// 표준 (초까지)
{
    tagName: 'dateTimeField',
    label: '예약 시각',
    value: '20241225143000',
    required: true
}

// 분까지만
{
    tagName: 'dateTimeField',
    label: '회의 시작',
    timeFormat: 'hhmm',
    value: '202412251430'
}

// ISO 8601 저장
{
    tagName: 'dateTimeField',
    label: '이벤트 시각',
    valueDateSeperator: '-',
    valueTimeSeperator: ':',
    valueDateTimeSeperator: 'T'
    // 저장: '2024-12-25T14:30:00'
}

// 미국식 화면 + 한국식 저장
{
    tagName: 'dateTimeField',
    label: 'Reservation',
    dateFormat: 'mdy',
    dateSeperator: '/',
    timeFormat: 'hhmm',
    valueDateFormat: 'ymd'
    // 화면: 12/25/2024 14:30
    // 저장: 202412251430
}

// 범위 제한 (오늘 이후만, 예약)
{
    tagName: 'dateTimeField',
    label: '희망 배송 시각',
    min: new Date().toISOString().slice(0,19).replace(/[-T:]/g,''),
    onSelect: 'onDeliveryChange'
}

// 좌측 라벨 + 필수
{
    tagName: 'dateTimeField',
    label: '시작 시각',
    labelPosition: 'left',
    labelWidth: 100,
    required: true
}

// info 툴팁
{
    tagName: 'dateTimeField',
    label: '마감 시각',
    infoButton: {
        tooltip: 'KST 기준 시각입니다'
    }
}

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

  1. 컴포넌트명 주의  dateTimeField (not dateTimePickerField).
  2. value는 문자열 — Date 객체 넘기면 크래시.
  3. 저장 값 길이가 timeFormat에 따라 달라짐 — 14자리(초) / 12자리(분) 헷갈리지 말 것.
  4. getValue()가 미완성 값에 '' 반환 — 마스킹 언더스코어 있으면 빈 문자열.
  5. 값 유효성 검증 없음  9999-99-99 99:99:99 같은 잘못된 값도 완성되면 그대로 반환. 별도 검증 필요.
  6. dateFormat과 valueDateFormat 혼동 — 화면 vs 저장.
  7. timeFormat과 valueTimeFormat 혼동 — 마찬가지로 화면 vs 저장.
  8. 옵션 키가 datePicker — DateField와 같은 키. 헷갈리지 말 것.
  9. popWidth 기본 240 — DatePicker/DateTimePicker(260)와 다름. 데이트타임 팝업은 좀 좁은 편.
  10. 일부 메서드 위임 누락  setRawValue/getRawValue/showPop/hidePop 등. fieldComponent로 직접.
  11. role="date-field" 자동 부여 — DateTime이지만 role은 date-field. ARIA 힌트.
  12. setValue() 로직에 버그성 코드 — DateTimePicker와 동일하게 valueDateFormat이 mdy/dmy일 때 조건문이 this.dateFormat을 검사 (va_component.js:8455, 8459). 특정 조합에서 오동작 가능.
  13. 팝업 상태머신 참여 — 다른 팝업과 자동 상호 배타.
  14. min/max는 팝업 UI에만 강제 — 필드 직접 타이핑으로 범위 밖 값 입력 가능.
  15. 타임존 없음 — 로컬 문자열만.

13. 실전 예 — 예약 시각 폼

class ReservationForm extends Va.View {
    mounted() {
        // 최소 시각을 지금으로 설정
        const now = new Date();
        const minStr = now.getFullYear() +
            String(now.getMonth()+1).padStart(2, '0') +
            String(now.getDate()).padStart(2, '0') +
            '000000';
        this.getRef('startTime').min = minStr;
    }

    onSubmit(btn, el, evt) {
        const start = this.getRef('startTime').getValue();
        if (!start) {
            this.getRef('startTime').setValidation('error', '시작 시각을 입력하세요');
            return;
        }
        // 서버로 전송 (14자리 문자열)
        ReservationService.save(this, { startTime: start }, 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: 'dateTimeField',
                        label: '시작 시각',
                        ref: 'startTime',
                        required: true,
                        timeFormat: 'hhmm',      // 분까지만
                        infoButton: { tooltip: '오늘 이후 시각으로 지정' }
                    },
                    {
                        tagName: 'inputField',
                        label: '메모',
                        ref: 'memo'
                    },
                    {
                        tagName: 'button',
                        text: '예약',
                        appearance: 'primary',
                        onClick: 'onSubmit'
                    }
                ]
            }]
        };
    }
}

14. dateTimeField vs dateField 선택 기준

상황추천

생년월일·계약일 (시간 무의미) dateField
예약 시각·회의 시작 (시간 중요) dateTimeField
시간대별 로그·이벤트 편집 dateTimeField
마감일 (하루 단위) dateField
마감 시각 (분·초까지) dateTimeField
서버가 시각도 요구하지만 UI는 날짜만 dateField + 서버 저장 시 T00:00:00 부착

필요 없으면 dateField를 쓰세요. 시각 입력 UI는 사용자에게 부담이 있어, 시간이 정말 필요할 때만 dateTimeField.