ComponentsP3 본문

FileUpload

파일 선택·드래그앤드롭 표면 — dropzone/button 변형, list/picture-card 목록. 업로드 전송은 소비자 책임(엔드포인트 결합 없음), items 주입으로 제어형 업로드 상태(진행률/성공/실패+재시도) 표현. 제약 위반은 인라인 오류(role=alert) + onReject(한국어 reason + 표준 code). 붙여넣기(enablePaste)·폴더(directory) 추가 지원 (DK File/* 사양의 결합 제거판).

마지막 업데이트 2026-06-12

한눈에#

파일 선택·드래그앤드롭 표면입니다 — 업로드 전송은 결합하지 않고(엔드포인트 비결합) 표현만 담당합니다.

파일을 끌어다 놓거나 클릭해 선택
  • dropzone·button 변형
  • list·picture-card 목록
  • 전송 비결합
  • 제약 위반 인라인 오류

선택 파일은 내부 목록으로 유지되고 onFilesChange로 통지 — items 주입 시 진행률/성공/실패 제어

업로드 전송은 결합하지 않습니다 — 선택된 파일은 내부 목록으로만 유지되고 onFilesChange로 통지합니다(데모는 핸들러 없이도 목록 추가/제거가 동작). 같은 파일(이름+크기 동일)을 다시 넣으면 목록에 추가되지 않고 onReject(코드 FILE_EXISTS)로만 통지됩니다.

플레이그라운드#

컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.

파일을 끌어다 놓거나 클릭해 선택
import { FileUpload } from '@wds/ui-web';

<FileUpload />

변형#

variant(표면)와 listType(목록 표현)이 변형 축입니다 — variant='dropzone'(기본)은 드래그앤드롭 영역 + 클릭 선택, 'button'은 클릭 선택만. listType='list'(기본)는 행 목록, 'picture-card'는 정사각 썸네일 카드 그리드입니다.

button — 클릭 선택만 (드롭 영역 없음)
파일을 끌어다 놓거나 클릭해 선택
제어형 상태(items) — 추가 시 0→100 업로드 시뮬레이션, 짝수 번째 파일은 실패→재시도

items를 주입하면 제어 모드가 됩니다 — 내부 목록 대신 items를 렌더하고, uploading은 Progress 합성+퍼센트, success는 체크 아이콘, error는 경고 아이콘+errorMessage+재시도 버튼(onRetry 제공 시)을 표시합니다. 전송·상태 전이는 전부 소비자 소유이며 컴포넌트는 표현만 담당합니다.

이미지·문서를 끌어다 놓거나 클릭해 선택
picture-card — 이미지는 Object URL 썸네일, 비이미지는 확장자 라벨 카드

picture-card의 썸네일 Object URL은 항목 제거·언마운트 시 자동으로 revokeObjectURL 정리됩니다. 카드에도 상태 오버레이(업로드 중/실패)와 제거 버튼이 합성됩니다.

파일을 끌어다 놓거나, 클릭하거나, 붙여넣기(Ctrl+V)
enablePaste + directory — 드롭존 포커스 중 Ctrl+V 붙여넣기, 폴더 선택/드롭 재귀 수집

enablePaste는 드롭존이 포커스된 상태의 붙여넣기를 받습니다 — 이름 없는 클립보드 이미지(스크린샷)는 clipboard-{n}.png로 보정됩니다. directory는 보조 버튼(webkitdirectory)과 폴더 드롭 재귀 수집을 켭니다 — 폴더 안의 accept 불일치 파일은 소음을 줄이기 위해 조용히 건너뜁니다. directorymultiple과 함께 쓰는 것을 권장합니다.

이미지만 (최대 3개 · 각 1MB)
제약 — accept/maxFiles/maxFileSize 위반은 인라인 오류로 거부

제약 위반은 role="alert" 인라인 오류로 안내되고 onReject로 통지됩니다. 거부 사유는 형식 불일치(accept) · 파일 용량 초과(maxFileSize) · 총 용량 초과(maxTotalSize) · 개수 초과(maxFiles) · 중복 파일이며, 프로그래밍 분기용 표준 코드(FILE_INVALID_TYPE · FILE_TOO_LARGE · TOTAL_TOO_LARGE · TOO_MANY_FILES · FILE_EXISTS)가 함께 실립니다. 단일 모드(기본, multiple 미지정)는 새 파일이 기존 선택을 대체합니다.

크기#

dropzone은 최소 높이 space.16에 점선 보더(radius.lg)이고, button 변형은 Button(secondary)을 그대로 씁니다. list 항목은 이름(말줄임)·크기·제거 버튼(+상태 블록)으로 구성되며 폭은 부모 100%, picture-card는 7rem 이상 정사각 카드(radius.md)의 자동 채움 그리드입니다.

상태#

  • Default — dropzone color.surface-muted 배경 + 점선 color.border-strong
  • Hovercolor.surface-hover
  • Focus — dropzone(role=button) :focus-visiblecolor.primary 2px 아웃라인
  • Dragging(드래그 오버)color.primary 실선 보더 + color.primary-subtle 배경
  • 오류role="alert" 영역에 color.error-text 메시지
  • 목록 항목color.surface 카드 + 제거 버튼(hover color.surface-hover)
  • 업로드 중(items) — Progress(primary 바) + 퍼센트
  • 업로드 완료(items)color.success-text 체크 아이콘
  • 업로드 실패(items)color.error-text 경고 아이콘 + 메시지 + 재시도(primary 텍스트 버튼)

Props#

Prop타입기본값설명
variant'dropzone' | 'button''dropzone'dropzone: 드래그앤드롭 + 클릭 / button: 클릭 선택만
listType'list' | 'picture-card''list'list: 행 목록 / picture-card: 썸네일 정사각 카드 그리드
multiplebooleanfalse다중 선택 — false면 새 파일이 기존 선택을 대체
acceptstring네이티브 accept 문법 — '.png', 'image/png', 'image/*' 콤마 구분
maxFileSizenumber파일당 최대 바이트 — 초과 시 '파일 용량 초과' 거부
maxTotalSizenumber목록 합계 최대 바이트 — 초과 시 '총 용량 초과' 거부
maxFilesnumber최대 파일 수 — 초과 시 '개수 초과' 거부
itemsReadonlyArray<FileUploadItem>제어형 업로드 상태 — 제공 시 내부 목록 대신 items를 렌더(상태는 소비자 소유)
onRetry(file: File) => voiderror 항목 재시도 — 제공 시에만 '재시도' 버튼 렌더
enablePastebooleanfalse드롭존 포커스 중 Ctrl+V 추가 — 이름 없는 이미지는 'clipboard-{n}.png' 보정
directorybooleanfalse폴더 업로드 — 보조 버튼(webkitdirectory) + 드롭 시 하위 폴더 재귀 수집
directoryLabelstring'폴더 선택'폴더 선택 보조 버튼 라벨
onFilesChange(files: File[]) => void목록 변경 통지 — 항상 새 배열 (제어 모드에서는 items 갱신의 단일 입력)
onReject(rejections: FileRejection[]) => void제약 위반 통지 — { file, reason, code }[]
labelstring변형별 기본 문구안내 라벨 (dropzone '파일을 끌어다 놓거나 클릭해 선택' / button '파일 선택')
removeFileLabel(file: File) => string'{파일명} 제거'제거 버튼 aria-label
…restHTMLAttributes<HTMLDivElement>aria-label · className 등은 루트 div로 전달

FileUploadItem(items 항목):

Prop타입기본값설명
fileFile네이티브 File
status'idle' | 'uploading' | 'success' | 'error'미지정이면 상태 표시 없음(비제어 목록과 동일한 외형)
progressnumber0-100 — 'uploading'일 때 진행률 바에 반영
errorMessagestring'업로드 실패''error'일 때 표시

FileRejection(onReject 인자):

Prop타입기본값설명
fileFile거부된 네이티브 File
reasonFileRejectionReason'파일 용량 초과' | '총 용량 초과' | '개수 초과' | '형식 불일치' | '중복 파일'
codeFileRejectionCode'FILE_TOO_LARGE' | 'TOTAL_TOO_LARGE' | 'TOO_MANY_FILES' | 'FILE_INVALID_TYPE' | 'FILE_EXISTS'

접근성#

dropzone은 role="button" + tabIndex=0으로 포커스·키보드 경로를 가지고, 네이티브 <input type="file">은 시각 숨김 + 탭 순서 제외(aria-hidden)로 영역/버튼 클릭과 연결됩니다.

동작
dropzone Enter / Space파일 선택 창 열기
dropzone Ctrl+V / ⌘V클립보드 파일 추가 (enablePaste일 때)
Tabdropzone(또는 button) → 폴더 선택 → 재시도/제거 버튼 순회
제거·재시도 버튼 Enter / Space해당 동작 실행 (네이티브 button)
  • 제약 위반 안내는 role="alert"로 즉시 낭독되고, 파일 목록은 aria-label="선택된 파일" 리스트입니다
  • 제어 모드 상태 변화는 role="status" 라이브 리전으로 요약 낭독됩니다 — 진행률 틱은 요약을 바꾸지 않아 소음이 없습니다(예: '업로드 중 1건, 업로드 실패 1건')
  • 업로드 진행률은 role="progressbar"(aria-valuenow, 라벨 '{파일명} 업로드 진행률')로 전달됩니다
  • 재시도 버튼은 '{파일명} 재시도', 제거 버튼은 '{파일명} 제거' 라벨을 갖고, 둘 다 시각 크기보다 넓은 44px 히트 영역으로 확장됩니다

토큰#

component 토큰 없이 semantic(+Button·Progress 합성)을 직접 소비합니다(신설 기준 §4 미충족).

속성토큰
dropzonecolor.surface-muted · 점선 color.border-strong · radius.lg · 최소 높이 space.16
dropzone hover/activecolor.surface-hover / color.surface-pressed
draggingcolor.primary(실선) · color.primary-subtle
Focuscolor.focus-ring(2px 아웃라인)
오류color.error-text · font-size.caption
목록 항목color.surface · color.border · radius.md
picture-cardcolor.surface-muted · color.border · radius.md · 카드명/오버레이 color.surface 반투명
업로드 완료color.success-text
업로드 실패color.error-text · 재시도 color.primary
제거 버튼color.text-muted — hover color.surface-hover + color.text → active color.surface-pressed
모션duration.fast + ease.standard