컴포넌트/필드 컴포넌트

File (파일)

VanillaFront 2026. 9. 13. 23:59

Va.File — 파일 업로드 입력 (선택 표시 + 리셋 버튼 통합)

파일 선택 다이얼로그를 여는 표준 파일 입력 컴포넌트. 선택된 파일명 표시·File 버튼·Reset 버튼이 하나의 필드로 통합되어 있어, 일반 <input type="file">보다 사용자 UX가 정리되어 있습니다.

  • 클래스: Va.File  va_component.js:9844
  • short name: file
  • 상속: Va.PureField (Input·Textarea와 형제)
  • isContainer: true
  • 베이스 CSS: va-file


1. 기본 사용

{
    tagName: 'file',
    onChange: 'onFileSelected'
}

렌더 결과:

  • 좌측 표시 영역: Choose file (기본) → 선택 후 파일명
  • File 버튼: 클릭 시 파일 선택 다이얼로그
  • Reset 버튼: 선택 초기화

2. Va.File vs Va.FileButton vs 관련 컴포넌트

VanillaFront에는 파일 관련 컴포넌트가 여러 개 있습니다. 상황별 차이:

항목Va.FileVa.FileButtonVa.FileDropUpload

UI 형태 표시 + 두 버튼(File/Reset) 버튼 하나 드래그앤드롭 영역
선택 파일명 표시 ✓ (기본) ✕ (별도 처리)
Reset 버튼 내장 관련 없음
기본 아이콘 없음 ico_attach 없음
드래그앤드롭
폼 필드로 취급 ✓ (PureField) ✕ (Component)

한 줄 요약: "폼 안 표준 파일 선택 필드 — 파일명 표시와 리셋까지 포함."


3. 주요 속성

File 전용

속성기본값설명

multiSelect false 다중 파일 선택 (properties에 선언 있으나 실제 반영 여부는 확인 필요)
fileButtonText 'File' File 버튼 텍스트
resetButtonText 'reset' Reset 버튼 텍스트
displayText 'Choose file' 초기 표시 텍스트

⚠️ multiSelect의 실제 동작 주의 — 옵션에 선언되어 있지만 코드가 <input>의 multiple 속성을 자동 세팅하지는 않아 보입니다. 필요 시 component.fieldElement.setAttribute('multiple', 'multiple')로 직접 설정.

PureField 상속

속성설명

readonly / disabled 상태 (두 버튼 모두 자동 전파)
size / appearance / shape 시각 스타일
stopPropagation 이벤트 버블링

⚠️ 파일 관련 표준 옵션 누락 — accept(.jpg,.png 등 타입 필터), capture(모바일 카메라 직접) 같은 옵션이 노출되어 있지 않음. 필요하면 component.fieldElement.setAttribute('accept', 'image/*') 직접 세팅.


4. 이벤트

이벤트시그니처발생 시점

change (component, element, files, evt) 파일 선택 완료 시  files는 FileList
reset (component, fieldElement, evt) Reset 버튼 클릭 시
click (component, element, evt) File 버튼 클릭 시 (다이얼로그 열기 직전)
focus / blur (component, element, evt) 표준

change 콜백 예시

onFileSelected(comp, el, files, evt) {
    // files는 FileList
    for (let i = 0; i < files.length; i++) {
        console.log(files[i].name, files[i].size, files[i].type);
    }

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

주의사항:

  • 취소 시 이벤트 안 옴 — 다이얼로그를 그냥 닫으면 change 발생 안 함 (표준 파일 input 동작)
  • 같은 파일 재선택 안 될 수 있음 — 표준 파일 input 동작. 필요 시 component.fieldElement.value = ''로 초기화

5. 메서드

파일 조회

메서드설명

getFiles() fieldElement.files (FileList) 반환

상태 (PureField 상속)

메서드설명

setDisabled(bool) / setReadOnly(bool) 상태 (두 버튼 자동 전파)
focus() / blur() 포커스

주의: setValue() / getValue()는 PureField 상속이지만 파일 필드 특성상 실질 활용 제한적. 파일 조회는 getFiles() 사용.


6. 내부 컴포넌트 (innerComponents)

Va.File은 두 개의 Va.Button을 내부에 소유:

내부 컴포넌트접근 방법역할

fileButtonComponent component.fileButtonComponent File 버튼 인스턴스
resetButtonComponent component.resetButtonComponent Reset 버튼 인스턴스

필요 시 각 버튼의 세부 옵션·이벤트를 직접 조작할 수 있습니다.

mounted() {
    // File 버튼 아이콘 추가
    this.getRef('file').fileButtonComponent.setIcon('ico_attach');
}

7. 내부 구조

<div elname="element" class="va-file [size]..." tag-name="file" field="true">
  <div elname="fieldWrapper" class="field-wrapper">
    <input elname="field" type="file" style="display:none">   ← 숨겨진 실제 input
    <div elname="display" class="display">Choose file</div>   ← 파일명 표시
    <div cpname="fileButton" class="va-button">File</div>     ← 파일 선택 버튼
    <div cpname="resetButton" class="va-button">reset</div>   ← 리셋 버튼
    <div elname="focusLine" class="focus-line"></div>
  </div>
</div>

핵심 트릭:

  • <input type="file">은 display:none — 실제 사용자 클릭은 File 버튼이 받음
  • File 버튼 클릭 시 → 프로그램적으로 fieldElement.dispatchEvent(new MouseEvent("click")) → 다이얼로그 열림
  • 표시 영역은 별도 div, 파일명을 콤마로 이어 표시

8. 언제 쓰나

Va.File이 맞을 때

  • 폼 안 파일 첨부 — 게시글 첨부, 프로필 이미지 등
  • 표준 UX (파일명 확인 + 리셋 가능)
  • 자주 리셋이 필요한 상황 (여러 번 재선택)

다른 걸 쓸 때

  • 단순 파일 첨부 버튼 (표시 없음) → Va.FileButton
  • 드래그앤드롭 지원 → Va.FileDropUpload
  • 이미지 미리보기 → Va.ImageFile
  • 라벨 붙은 폼 필드 → Va.FileField

9. 흔한 조합 예시

// 표준
{
    tagName: 'file',
    onChange: 'onFileSelected'
}

// 버튼 텍스트 한글로
{
    tagName: 'file',
    fileButtonText: '파일 선택',
    resetButtonText: '초기화',
    displayText: '파일을 선택하세요'
}

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

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

// 읽기 전용 (표시만)
{
    tagName: 'file',
    disabled: true,
    displayText: '업로드 불가'
}

10. 실전 예 — 첨부파일 업로드

class Attachment extends Va.View {
    mounted() {
        // PDF와 이미지만 허용
        this.getRef('file').fieldElement.setAttribute('accept', '.pdf,image/*');
    }

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

        // 크기 검증 (10MB 제한)
        for (let i = 0; i < files.length; i++) {
            if (files[i].size > 10 * 1024 * 1024) {
                new Va.Alert({
                    title: '용량 초과',
                    message: `${files[i].name}은 10MB를 초과합니다`
                }).show(this);
                comp.resetButtonComponent.element.click();   // 자동 리셋
                return;
            }
        }

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

        this.showMasking();
        FileService.upload(this, formData, (view, ok, res) => {
            view.hideMasking();
            if (ok) {
                new Va.Alert({ title: '완료', message: '업로드 완료' }).show(view);
            }
        });
    }

    onReset(comp, el, evt) {
        console.log('파일 선택 초기화');
    }

    config() {
        return {
            tagName: 'page',
            tags: [{
                tagName: 'panel',
                tags: [
                    { tagName: 'h2', innerHTML: '첨부파일' },
                    {
                        tagName: 'file',
                        ref: 'file',
                        fileButtonText: '파일 선택',
                        resetButtonText: '초기화',
                        displayText: 'PDF/이미지 (최대 10MB)',
                        onChange: 'onFileSelected',
                        onReset: 'onReset'
                    }
                ]
            }]
        };
    }
}

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

  1. change 취소 시 안 옴 — 사용자가 다이얼로그 그냥 닫으면 이벤트 없음.
  2. 같은 파일 재선택 안 될 수 있음 — HTML 표준 특성. fieldElement.value = ''로 초기화 후 재시도.
  3. multiSelect 옵션이 실제 반영되지 않을 수 있음 — properties 선언은 있지만 코드가 multiple 속성 세팅 로직 없음. 확실히 하려면 DOM 직접 조작.
  4. accept / capture 옵션 노출 안 됨 — DOM 직접 조작 필요.
  5. <input type="file">이 display:none — File 버튼 클릭 시 프로그램적으로 클릭 발사.
  6. 파일명 표시는 콤마로 연결 — 다중 파일 시 file1.pdf, file2.pdf 형태.
  7. Reset 버튼이 있어 편리 — 사용자가 실수로 잘못 선택했을 때 되돌리기 쉬움.
  8. getFiles()는 FileList 반환 — 배열이 아님. Array.from(files) 변환 후 map/filter 등 사용.
  9. preventParentFieldEvent: true — 부모 Field 이벤트 개입 방지.
  10. 드래그앤드롭 없음 — 필요 시 Va.FileDropUpload 사용.
  11. disabled/readonly 시 두 버튼 모두 비활성 — 자동 전파.
  12. click 이벤트 두 곳에서 dispatch — File 버튼 클릭 시 내부에서 두 번 이벤트 발생 가능. 콜백 방어.

12. file vs fileButton vs fileDropUpload 선택

상황추천

폼 안 표준 파일 첨부 file
간단한 첨부 버튼 (표시 없음) fileButton
드래그앤드롭 지원 fileDropUpload
이미지 미리보기 필요 imageFile
라벨 붙은 폼 필드 fileField
여러 파일 순차 업로드 fileDropUpload (진행률 표시)

참고

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

FileField (파일필드)  (1) 2026.09.14
TextareaField (텍스트영역필드)  (0) 2026.09.13
Textarea (텍스트영역)  (0) 2026.09.13
SwitchField (스위치필드)  (0) 2026.09.13
Switch (스위치)  (0) 2026.09.13