File Upload
파일 선택·드롭·붙여넣기·폴더 선택·제약 위반·업로드 상태를 소비 프로젝트가 같은 구조로 구현하게 하는 파일 업로드 표면 계약입니다.
마지막 업데이트 2026-07-02
AI / Consumer Implementation Contract#
AI 작업자와 소비 프로젝트는 File Upload를 “파일을 고르는 UI”가 아니라 선택 트리거, 네이티브 입력 fallback, 제약 안내, 선택 목록, 상태 피드백의 계약으로 취급합니다.
세부 설명보다 patterns/file-upload.contract.json과 @wds/ui-web/testing의
assertFileUploadPatternCompliance(preset, screen) 또는
assertPatternCompliance('file-upload', preset, screen)을 우선합니다.
MUST#
- 파일 선택 트리거 — 한 업로드 그룹 안에 접근성 이름이 있는 파일 선택 트리거를 둡니다.
- 네이티브 fallback — 커스텀 버튼·드롭존 뒤에는 반드시
input[type="file"]fallback을 유지합니다. - 드래그 대체 경로 — 드롭존을 쓰더라도 클릭·키보드로 같은 파일 선택을 할 수 있어야 합니다.
- 선택 목록 — 선택된 파일은 파일명이 보이는 목록 또는 카드에 남기고, 파일별 제거 버튼을 둡니다.
- 제약 위반 — 형식·용량·개수·총량 오류는 화면 안의 인라인
role="alert"로 남깁니다. 중복 파일은 현재FileUpload동작처럼 인라인 오류 없이onReject(FILE_EXISTS)로만 통지될 수 있습니다. - 상태 피드백 — 업로드 상태를 제어할 때는
role="status", 실제 진행률은role="progressbar"와aria-valuenow로 노출합니다. - 파일별 액션 라벨 — 제거·재시도 버튼의 접근성 이름에는 대상 파일명이 포함되어야 합니다.
SHOULD#
- 모바일·짧은 폼은
variant="button"을 기본 선택으로 둡니다. - 데스크톱 업무 화면에서 다중 파일을 자주 다루는 경우에만 큰 드롭존을 씁니다.
- 허용 형식, 용량, 개수, 폴더, 붙여넣기 가능 여부는 선택 전에 보이게 합니다.
- 진행률은 실제 전송률을 알 때만 결정형으로 표시하고, 알 수 없으면 상태 문구나 indeterminate 진행 상태를 씁니다.
- CSV·XLSX처럼 검증·부분 실패·복구가 필요한 흐름은 File Import / Upload Review로 분리합니다.
NEVER#
- 드래그앤드롭을 유일한 파일 추가 방법으로 만들지 않습니다.
- 모바일 1차 제어로 큰 드롭존을 유지하지 않습니다.
- Toast만으로 파일 거부 사유를 알리지 않습니다.
- 퍼센트 텍스트만 표시하고
progressbar를 생략하지 않습니다. - 업로드 중·성공·오류·차단 상태를 색상만으로 구분하지 않습니다.
items={[]}처럼 제어형으로 잠근 상태에서 선택 후 아무 시각 피드백이 없는 picker를 노출하지 않습니다.
프리셋별 구현 계약#
| 프리셋 | 반드시 있어야 하는 요소 | 절대 없어야 하는 요소 |
|---|---|---|
| 빠른 첨부 | FileUpload variant="button", 접근성 이름이 있는 선택 버튼, input[type=file] fallback | 라벨 없는 native-only picker, 드래그 전용 업로드, 전송 전 fake progress |
| 데스크톱 드롭존 | 이름 있는 dropzone, 클릭·키보드 fallback, input[type=file] fallback | input 없는 dropzone, drag-only 동작, 모바일 전용 대형 드롭존 |
| 제약 위반 | 선택 트리거, input fallback, 인라인 role="alert" 오류 | Toast-only 오류, 조용한 거부, 업로드 그룹과 분리된 오류 문구 |
| 업로드 상태 | 제어형 items, role="status" 요약, 결정형 Progress, 성공·오류 텍스트, 파일별 재시도 | 색상 전용 상태, progressbar 없는 퍼센트, 재시도 없는 복구 가능 실패 |
| 이미지/문서 카드 | listType="picture-card", 파일명, 썸네일 또는 확장자 fallback, 파일별 제거 | 파일명 없는 썸네일 카드, generic 제거 라벨, 색상 전용 카드 상태 |
| 폴더/붙여넣기 수집 | directory, enablePaste, 폴더 선택 affordance, 붙여넣기 안내, multiple | 보이지 않는 implicit paste capture, 단일 파일 폴더 업로드, drag-only 폴더 수집 |
구현 기준#
FileUpload는 파일 선택·표시·상태 표현까지만 담당합니다. 실제 업로드 전송, 스캔, 서버 검증, 재시도 정책은 소비 프로젝트가 소유합니다.
WDS 계약은 사용자가 선택하기 전 무엇을 할 수 있는지 알고, 선택 후 무슨 일이 일어났는지 화면 안에서 복구할 수 있게 만드는 데 집중합니다.
공식 접근성 기준도 같은 방향입니다. WCAG 2.2는 드래그 동작에 단일 포인터 대체 수단을 요구하고, USWDS는 파일 제한을 hint text로 먼저 알려 주며, Carbon은 drop target을 버튼처럼 키보드로 활성화하고 오류를 보조기술에 노출합니다. Apple·Microsoft 계열 가이드는 장치와 입력 방식이 달라져도 같은 작업 연속성을 유지하는 것을 강조합니다. 그래서 WDS는 “웹/모바일 소스 재활용”보다 모바일은 버튼, 데스크톱은 선택적 드롭존, 오류는 인라인 복구를 우선합니다.
React 소비 레시피#
QuickAttachRecipe#
import { FileUpload } from '@wds/ui-web';
import { assertFileUploadPatternCompliance } from '@wds/ui-web/testing';
export function QuickAttachRecipe() {
return (
<FileUpload
variant="button"
multiple
aria-label="첨부 파일 선택"
label="파일 첨부"
/>
);
}
// test
assertFileUploadPatternCompliance('quick-attach', screen);
DesktopDropzoneRecipe#
import { FileUpload } from '@wds/ui-web';
export function DesktopDropzoneRecipe() {
return (
<FileUpload
multiple
accept=".pdf,.doc,.docx"
maxFiles={10}
aria-label="계약 파일 업로드"
label="계약 파일을 끌어다 놓거나 클릭해 선택"
/>
);
}
ValidationErrorRecipe#
import { FileUpload } from '@wds/ui-web';
export function ValidationErrorRecipe() {
return (
<FileUpload
multiple
accept="image/*"
maxFiles={3}
maxFileSize={1048576}
aria-label="이미지 첨부"
label="이미지만 첨부 (최대 3개, 각 1MB)"
onReject={(rejections) => {
for (const rejection of rejections) {
console.info(rejection.code, rejection.reason);
}
}}
/>
);
}
UploadStatusRecipe#
import { FileUpload, type FileUploadItem } from '@wds/ui-web';
const items: FileUploadItem[] = [
{ file: new File(['a,b'], 'uploading.csv', { type: 'text/csv' }), status: 'uploading', progress: 40 },
{ file: new File(['ok'], 'done.csv', { type: 'text/csv' }), status: 'success' },
{ file: new File(['bad'], 'failed.csv', { type: 'text/csv' }), status: 'error', errorMessage: '서버 검증 실패' },
];
export function UploadStatusRecipe() {
return (
<FileUpload
multiple
items={items}
aria-label="CSV 업로드"
onRetry={(file) => retryUpload(file)}
/>
);
}
PictureCardRecipe#
import { FileUpload } from '@wds/ui-web';
export function PictureCardRecipe() {
return (
<FileUpload
multiple
listType="picture-card"
accept="image/*,.pdf"
aria-label="이미지와 문서 첨부"
label="이미지·문서를 끌어다 놓거나 클릭해 선택"
/>
);
}
FolderPasteRecipe#
import { FileUpload } from '@wds/ui-web';
export function FolderPasteRecipe() {
return (
<FileUpload
multiple
directory
enablePaste
aria-label="자료실 파일 수집"
label="파일을 끌어다 놓거나, 클릭하거나, 붙여넣기"
/>
);
}
접근성#
- 트리거는 text,
aria-label,aria-labelledby중 하나로 접근성 이름을 가져야 합니다. - 숨김
input[type=file]은 fallback으로 유지하되 별도 탭 스톱이 되지 않아야 합니다. - 드롭존은 pointer, keyboard, click 경로가 모두 같은 선택 흐름을 열어야 합니다.
- 인라인 파일 거부 메시지는
role="alert"로 낭독되고 사용자가 수정할 때까지 화면에 남아야 합니다. 중복 파일처럼 의도적으로 조용히 무시하는 거부는onReject로만 처리할 수 있습니다. - 제어형 업로드 상태는
role="status"에서 요약하고, 진행률 tick마다 과도하게 라이브 리전을 갱신하지 않습니다. - 결정형 진행률은 파일별 이름이 있는
role="progressbar"와aria-valuenow를 가져야 합니다. - 제거·재시도는 “제거”, “재시도”만 말하지 않고 대상 파일명을 포함합니다.
- 폴더·붙여넣기 수집은 사용자가 행동하기 전 해당 affordance를 볼 수 있어야 합니다.
기계 판독 계약#
- 계약 파일:
patterns/file-upload.contract.json - 패키지 export:
@wds/ui-web/patterns/file-upload - 원본 JSON export:
@wds/ui-web/patterns/file-upload.contract.json - 테스트 helper:
@wds/ui-web/testing - MCP lookup:
get_pattern_contract({ "name": "file-upload", "preset": "upload-status" })
import { assertPatternCompliance } from '@wds/ui-web/testing';
assertPatternCompliance('file-upload', 'quick-attach', screen);
assertPatternCompliance('file-upload', 'desktop-dropzone', screen);
assertPatternCompliance('file-upload', 'validation-error', screen);
assertPatternCompliance('file-upload', 'upload-status', screen);
assertPatternCompliance('file-upload', 'picture-card', screen);
assertPatternCompliance('file-upload', 'folder-paste', screen);
사용 컴포넌트#
FileUpload · Button · Progress · Alert · Toast
참고 기준#
- USWDS File input — 제한 안내, 다중 파일 사용 주의, native input 기반 progressive enhancement
- WCAG 2.2 Dragging Movements — 드래그 동작의 대체 입력 경로
- Carbon File uploader accessibility — keyboard activation, error announcement, file-specific delete label
- Fluent accessibility — WCAG 기반 접근성 foundation
- Apple Design System, WWDC25 — 장치·입력 방식 간 적응성과 연속성