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'
}
]
}]
};
}
}
흐름:
- mounted()에서 accept 속성 세팅 (PDF만)
- 파일 선택 시 크기·확장자 검증 + 실패 시 자동 리셋
- 제출 시 getFiles()로 조회 → FormData에 첨부 → 서버 전송
10. 알아두면 좋을 주의사항
- change 시 this.files 자동 저장 — Field 컴포넌트 자체에서도 파일 접근 편리.
- getFiles() 반환은 FileList — 배열 아님. Array.from(files) 변환 후 map/filter.
- change 취소 시 안 옴 — 다이얼로그 그냥 닫으면 이벤트 없음.
- accept / multiple 옵션 미노출 — DOM 직접 세팅 (fieldComponent.fieldElement).
- 같은 파일 재선택 안 될 수 있음 — HTML 표준 특성.
- 옵션 키 file — 세부 커스터마이즈용.
- 필수 검증 자동화 없음 — required: true는 별표 표시만. getFiles().length 체크 후 setValidation 직접.
- multiSelect 실질 동작 확인 필요 — 옵션 노출은 되지만 실제 반영 여부 불확실.
- Reset 버튼 강제 클릭 트릭 — 검증 실패 시 fieldComponent.resetButtonComponent.element.click()로 자동 초기화.
- focus() 실효 제한적 — 파일 필드의 특성상 표준 focus가 큰 의미 없음.
- 드래그앤드롭 지원 안 됨 — 필요 시 Va.FileDropUpload 사용.
- disabled/readonly 시 두 버튼 모두 비활성 — File 컴포넌트가 자동 전파.
11. fileField vs imageFileField vs fileDropUpload 선택
상황추천
| 폼 안 파일 첨부 (범용) | fileField |
| 이미지 첨부 + 미리보기 | imageFileField |
| 드래그앤드롭 필요 | fileDropUpload |
| 간단한 첨부 버튼 | fileButton |
| 여러 파일 순차 업로드 UI | fileDropUpload |
참고
- API 문서 페이지: https://vanillafront.com/docs.html?theme=light#main#apifilefield
- 연관: Va.File(내부), Va.ImageFileField(이미지 특화)
'컴포넌트 > 필드 컴포넌트' 카테고리의 다른 글
| File (파일) (0) | 2026.09.13 |
|---|---|
| TextareaField (텍스트영역필드) (0) | 2026.09.13 |
| Textarea (텍스트영역) (0) | 2026.09.13 |
| SwitchField (스위치필드) (0) | 2026.09.13 |
| Switch (스위치) (0) | 2026.09.13 |