컴포넌트/필드 컴포넌트

FileField (파일필드)

VanillaFront 2026. 9. 14. 00:02

Va.FileField — 라벨 + 파일 업로드 필드

Va.File이 순수 파일 입력이라면, Va.FileField는 그 위에 폼 라벨·필수 표시·검증 메시지를 얹은 완성 폼 필드입니다. Field 계열 아키텍처 그대로, 내부에 Va.File을 소유하는 Composition 구조. 첨부파일이 있는 폼에 정석.

  • 클래스: Va.FileField  va_component.js:9491
  • short name: fileField
  • 상속: Va.Field (다른 Field 형제들과 같음)
  • 내부 컴포넌트: Va.File 인스턴스 (fieldComponent)
  • isContainer: true
  • 베이스 CSS: va-field


1. 기본 사용

{
    tagName: 'fileField',
    label: '첨부파일',
    fileButtonText: '파일 선택',
    resetButtonText: '초기화',
    displayText: '파일을 선택하세요',
    required: true,
    onChange: 'onFileSelected'
}

라벨 + 검증 + 파일 표시 + 두 버튼(File/Reset)이 한 번에 세팅.


2. File / 다른 Field와의 차이

항목Va.FileVa.FileFieldVa.InputField

폼 라벨
검증 메시지
필수 표시(별표)
info 툴팁
UI 형태 표시 + 두 버튼 표시 + 두 버튼 (내부 위임) 텍스트 입력
files 자동 저장 ✓ (this.files)

한 줄 요약: "폼 안 라벨 붙은 파일 첨부 필드."


3. 주요 속성

File 전용 (계승)

속성기본값설명

fileButtonText 'File' File 버튼 텍스트
resetButtonText 'reset' Reset 버튼 텍스트
displayText 'Choose file' 초기 표시 텍스트
multiSelect false 다중 파일 선택 (실질 동작 확인 필요)

라벨 관련 (Field 상속)

속성설명

label 폼 라벨
labelPosition top / bottom / left / right
labelWidth 라벨 폭
noLabel 폼 라벨 숨김
infoButton info 아이콘
required 필수 표시

검증

속성설명

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

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

{
    tagName: 'fileField',
    label: '첨부',
    file: {                         // ← 내부 File에 직접 전달
        stopPropagation: false
    }
}

각 Field 계열 옵션 키:

  • InputField → input
  • CheckboxField → checkbox
  • FileField → file

⚠️ accept / multiple 등 표준 파일 속성은 옵션으로 노출 안 됨 — Va.File과 동일하게 필요 시 component.fieldComponent.fieldElement.setAttribute('accept', 'image/*') 직접 세팅.


4. 이벤트

File의 이벤트를 재발화:

이벤트시그니처발생 시점

change (component, element, files, evt) 파일 선택 완료 시  files는 FileList. this.files에도 자동 저장
reset (component, element, evt) Reset 버튼 클릭 시
click (component, element, evt) File 버튼 클릭

change 콜백 예시

onFileSelected(field, el, files, evt) {
    console.log('선택된 파일:', files);   // FileList

    // field.files에도 저장되어 있음 (편의)
    console.log('저장된 files:', field.files);

    // FormData로 서버 전송
    const formData = new FormData();
    for (let i = 0; i < files.length; i++) {
        formData.append('file', files[i]);
    }
    // ...
}

FileField 특유 동작: change 이벤트 리스너 내부에서 this.files = files 자동 저장 (va_component.js:9534). Field 컴포넌트 자체에서도 파일 접근이 편리.


5. 메서드

파일 조회

메서드설명

getFiles() 내부 File의 getFiles() 위임 (FileList 반환)

상태 (Field 상속)

메서드설명

setDisabled(bool) / getDisabled() 비활성화 (내부 File의 두 버튼 자동 전파)
setReadOnly(bool) / setReadonly(bool) 읽기 전용
setLabel(label) 폼 라벨 변경
setSize(size) 크기

검증

메서드설명

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

포커스

메서드설명

focus() / blur() Field 상속 (실효 제한적)

주의: setValue() / getValue()는 파일 필드 특성상 실질 의미 제한적. 파일 조회는 항상 getFiles() 사용.


6. 내부 구조

<div elname="element" class="va-field [vertical|horizontal]" 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-file">                    ← 내부 Va.File
        <div class="field-wrapper">
          <input type="file" style="display:none">
          <div class="display">파일을 선택하세요</div>
          <div class="va-button">파일 선택</div>
          <div class="va-button">초기화</div>
        </div>
      </div>
    </div>
  </div>
  <div elname="validationDiv" style="display:none">
    <div class="va-validation">...</div>
  </div>
</div>

7. 언제 쓰나

FileField가 맞을 때

  • 폼 안 첨부파일 — 이력서, 증빙자료, 리뷰 이미지
  • 라벨·필수·검증이 필요한 파일 필드
  • 표준 UX (파일명 확인 + 리셋)
  • 정렬된 폼 안에서 다른 필드들과 라벨 통일

다른 걸 쓸 때

  • 라벨 없이 인라인 → Va.File
  • 단순 파일 첨부 버튼 → Va.FileButton
  • 드래그앤드롭 지원 → Va.FileDropUpload
  • 이미지 미리보기 → Va.ImageFile / Va.ImageFileField

8. 흔한 조합 예시

// 표준
{
    tagName: 'fileField',
    label: '첨부파일',
    required: true,
    onChange: 'onChange'
}

// 한글 버튼 텍스트
{
    tagName: 'fileField',
    label: '이력서',
    fileButtonText: '파일 선택',
    resetButtonText: '초기화',
    displayText: 'PDF 파일 선택 (최대 5MB)'
}

// 좌측 라벨 (폼 정렬)
{
    tagName: 'fileField',
    label: '첨부',
    labelPosition: 'left',
    labelWidth: 100,
    fileButtonText: '파일 선택'
}

// info 툴팁
{
    tagName: 'fileField',
    label: '증빙자료',
    infoButton: {
        tooltip: 'PDF, JPG, PNG 형식만 허용됩니다 (최대 10MB)'
    },
    required: true
}

// 이미지만 (accept 직접 세팅)
mounted() {
    this.getRef('file').fieldComponent.fieldElement.setAttribute('accept', 'image/*');
}

// 다중 파일
mounted() {
    this.getRef('file').fieldComponent.fieldElement.setAttribute('multiple', 'multiple');
}

9. 실전 예 — 이력서 첨부 폼

class ResumeForm extends Va.View {
    mounted() {
        // PDF만 허용
        this.getRef('resume').fieldComponent.fieldElement.setAttribute('accept', '.pdf');
    }

    onFileSelected(field, el, files, evt) {
        if (files.length === 0) return;

        const file = files[0];

        // 크기 검증
        if (file.size > 5 * 1024 * 1024) {
            field.setValidation('error', '5MB를 초과할 수 없습니다');
            // Reset 강제
            field.fieldComponent.resetButtonComponent.element.click();
            return;
        }

        // 확장자 검증
        if (!file.name.toLowerCase().endsWith('.pdf')) {
            field.setValidation('error', 'PDF 파일만 첨부 가능합니다');
            field.fieldComponent.resetButtonComponent.element.click();
            return;
        }

        field.clearValidation();
        // 자동 업로드는 안 함 — 폼 제출 시 함께 전송
    }

    onSubmit(btn, el, evt) {
        const name  = this.getRef('name').getValue();
        const files = this.getRef('resume').getFiles();

        if (!name) {
            this.getRef('name').setValidation('error', '이름을 입력하세요');
            return;
        }
        if (files.length === 0) {
            this.getRef('resume').setValidation('error', '이력서 파일을 첨부하세요');
            return;
        }

        const formData = new FormData();
        formData.append('name', name);
        formData.append('resume', files[0]);

        this.showMasking();
        ResumeService.submit(this, formData, (view, ok, res) => {
            view.hideMasking();
            if (ok) new Va.Alert({ title: '완료', message: '제출 완료' }).show(view);
        });
    }

    config() {
        return {
            tagName: 'page',
            tags: [{
                tagName: 'panel',
                tags: [
                    { tagName: 'h2', innerHTML: '지원서 접수' },
                    {
                        tagName: 'inputField',
                        ref: 'name',
                        label: '이름',
                        required: true
                    },
                    {
                        tagName: 'fileField',
                        ref: 'resume',
                        label: '이력서',
                        required: true,
                        fileButtonText: '파일 선택',
                        resetButtonText: '초기화',
                        displayText: 'PDF 파일 선택 (최대 5MB)',
                        infoButton: {
                            tooltip: 'PDF 파일만 첨부 가능, 최대 5MB'
                        },
                        onChange: 'onFileSelected'
                    },
                    {
                        tagName: 'button',
                        text: '제출',
                        appearance: 'primary',
                        onClick: 'onSubmit'
                    }
                ]
            }]
        };
    }
}

흐름:

  1. mounted()에서 accept 속성 세팅 (PDF만)
  2. 파일 선택 시 크기·확장자 검증 + 실패 시 자동 리셋
  3. 제출 시 getFiles()로 조회 → FormData에 첨부 → 서버 전송

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

  1. change  this.files 자동 저장 — Field 컴포넌트 자체에서도 파일 접근 편리.
  2. getFiles() 반환은 FileList — 배열 아님. Array.from(files) 변환 후 map/filter.
  3. change 취소 시 안 옴 — 다이얼로그 그냥 닫으면 이벤트 없음.
  4. accept / multiple 옵션 미노출 — DOM 직접 세팅 (fieldComponent.fieldElement).
  5. 같은 파일 재선택 안 될 수 있음 — HTML 표준 특성.
  6. 옵션 키 file — 세부 커스터마이즈용.
  7. 필수 검증 자동화 없음  required: true는 별표 표시만. getFiles().length 체크 후 setValidation 직접.
  8. multiSelect 실질 동작 확인 필요 — 옵션 노출은 되지만 실제 반영 여부 불확실.
  9. Reset 버튼 강제 클릭 트릭 — 검증 실패 시 fieldComponent.resetButtonComponent.element.click()로 자동 초기화.
  10. focus() 실효 제한적 — 파일 필드의 특성상 표준 focus가 큰 의미 없음.
  11. 드래그앤드롭 지원 안 됨 — 필요 시 Va.FileDropUpload 사용.
  12. disabled/readonly 시 두 버튼 모두 비활성 — File 컴포넌트가 자동 전파.

11. fileField vs imageFileField vs fileDropUpload 선택

상황추천

폼 안 파일 첨부 (범용) fileField
이미지 첨부 + 미리보기 imageFileField
드래그앤드롭 필요 fileDropUpload
간단한 첨부 버튼 fileButton
여러 파일 순차 업로드 UI fileDropUpload

참고