본문으로 건너뛰기
Amineslab UI

Component

FileUpload

클릭·키보드·드래그앤드롭·미리보기를 하나의 파일 수명주기로 묶습니다.

Playground

옵션을 조절하며 화면과 사용 코드를 함께 확인하세요.

PC 1280px
Controls

허용 확장자 또는 MIME type · 비우면 모든 형식

파일당 최대 MB · 비우면 제한 없음

최대 파일 개수 · 비우면 제한 없음

화면에 표시할 확장자 안내 · 검증과 별개

Code
import { useState } from 'react'

import { type FileItem, FileUpload } from '@amineslab/ui'

function optionalLimit(value: string) {
    return value.trim() && Number.isFinite(Number(value)) ? Math.max(0, Number(value)) : undefined;
}

type FileDemoConstraints = {
    accept?: string;
    maxSize?: string;
    maxFiles?: string;
};

function FileUploadExample({ disabled = false, uploading = false, invalid = false, accept = '.jpg,.png,.pdf', maxSize = '10', maxFiles = '4', opaque = false, placeholder = '파일을 끌어다 놓거나 클릭해서 선택', acceptLabel = 'JPG, PNG, PDF', itemsPlacement, multiple, size, variant, }: FileDemoConstraints & {
    disabled?: boolean;
    uploading?: boolean;
    invalid?: boolean;
    opaque?: boolean;
    placeholder?: string;
    acceptLabel?: string;
    itemsPlacement: string;
    multiple: boolean;
    size: string;
    variant: string;
}) {
    const [files, setFiles] = useState<FileItem[]>([]);
    const [error, setError] = useState('');
    const resolvedSize = size === 'sm' || size === 'lg' ? size : 'md';
    const resolvedVariant = variant === 'outline' ? 'outline' : 'filled';
    const resolvedItemsPlacement = itemsPlacement === 'inside' ? 'inside' : 'outside';
    return (<div className="grid w-full max-w-lg gap-2">
      <FileUpload disabled={disabled} uploading={uploading} invalid={invalid} opaque={opaque} accept={accept} aria-label="자료 업로드" placeholder={placeholder} acceptLabel={acceptLabel} itemsPlacement={resolvedItemsPlacement} maxFiles={optionalLimit(maxFiles)} maxSize={optionalLimit(maxSize)} multiple={multiple} size={resolvedSize} variant={resolvedVariant} value={files} onValueChange={setFiles} onError={(errors) => setError(errors[0]?.message ?? '')}/>
      {error ? (<p className="m-0 text-body-3 text-danger-700" role="alert">
          {error}
        </p>) : null}
    </div>);
}

export default function ExamplePreview() {
  return (<FileUploadExample {...({"disabled":false,"uploading":false,"invalid":false,"accept":".jpg,.png,.pdf","maxSize":"10","maxFiles":"4","placeholder":"파일을 끌어다 놓거나 클릭해서 선택","acceptLabel":"JPG, PNG, PDF","opaque":false,"itemsPlacement":"outside","multiple":true,"size":"md","variant":"filled"} as const)}/>)
}

Props

@amineslab/ui에서 FileUpload, fileDropAreaVariants를 가져옵니다.

import { FileUpload, fileDropAreaVariants } from '@amineslab/ui'

FileUpload

드롭 영역, 검증, 로컬/원격 미리보기를 합성합니다.

NameTypeDefaultDescription
value / defaultValueFileItem[]-파일 목록입니다.
onValueChange(files: FileItem[]) => void-검증을 통과한 목록입니다.
itemsPlacement'outside' | 'inside''outside'파일 카드 목록 위치입니다. 너비를 넘으면 +N 타일로 접고 클릭하면 전체 목록을 확인합니다.
placeholderReactNode-드롭 영역의 안내 문구를 변경합니다.
acceptLabelstring-화면에 표시할 확장자 안내입니다. 예: JPG, PNG, PDF. 실제 검증은 accept로 지정합니다.
hintReactNode-자동 확장자·용량 안내를 대체합니다.
acceptstring-허용 확장자 또는 MIME type입니다.
maxSizenumber-파일당 최대 용량(MB)입니다. 숫자로 지정했을 때만 용량 안내를 표시합니다. 생략하면 제한하지 않습니다.
maxFilesnumber-기존 파일을 포함한 최대 파일 개수입니다.
onError(errors: FileError[]) => void-형식·용량·개수 제한 오류를 전달합니다.
uploading / disabled / invalidboolean-진행·비활성·오류 상태입니다.
variant'filled' | 'outline''filled'드롭 영역 표면입니다.
size'sm' | 'md' | 'lg''md'드롭 영역과 카드 크기입니다.
invalidbooleanfalse오류 테두리를 표시하며 focus·open 상태에서도 유지합니다. aria-invalid도 함께 전달합니다.
opaquebooleanfalseelevation 레이어 아래 불투명한 테마 바탕을 추가합니다. hover·focus·invalid 표현은 유지합니다.

Examples

자주 쓰는 조합을 살펴보고, 필요한 예시의 코드를 펼쳐 확인하세요.

공통 필드 표면

variant는 filled·outline 표현을, opaque는 elevation 아래의 불투명 바탕을 정합니다. focus와 hover는 중립 elevation을 사용하고 invalid 테두리는 포커스 중에도 유지합니다. NumberInput·PhoneInput·PasswordInput은 Input의 아이콘·문자열·ReactNode 슬롯을 상속합니다. SelectField에서는 같은 표면 props를 직접 지정하거나 triggerProps로 세부 설정할 수 있습니다.

투명 바탕
불투명 바탕
Outline + 불투명 바탕
<FileUpload />

<FileUpload opaque />

<FileUpload variant="outline" opaque />

클릭·키보드·드래그앤드롭

세 입력 수단이 같은 검증과 파일 수명주기를 거칩니다. 이미지와 원격 파일은 같은 카드 계약을 사용합니다.

import { useState } from 'react'

import { type FileItem, FileUpload } from '@amineslab/ui'

function optionalLimit(value: string) {
    return value.trim() && Number.isFinite(Number(value)) ? Math.max(0, Number(value)) : undefined;
}

type FileDemoConstraints = {
    accept?: string;
    maxSize?: string;
    maxFiles?: string;
};

function FileUploadExample({ disabled = false, uploading = false, invalid = false, accept = '.jpg,.png,.pdf', maxSize = '10', maxFiles = '4', opaque = false, placeholder = '파일을 끌어다 놓거나 클릭해서 선택', acceptLabel = 'JPG, PNG, PDF', itemsPlacement, multiple, size, variant, }: FileDemoConstraints & {
    disabled?: boolean;
    uploading?: boolean;
    invalid?: boolean;
    opaque?: boolean;
    placeholder?: string;
    acceptLabel?: string;
    itemsPlacement: string;
    multiple: boolean;
    size: string;
    variant: string;
}) {
    const [files, setFiles] = useState<FileItem[]>([]);
    const [error, setError] = useState('');
    const resolvedSize = size === 'sm' || size === 'lg' ? size : 'md';
    const resolvedVariant = variant === 'outline' ? 'outline' : 'filled';
    const resolvedItemsPlacement = itemsPlacement === 'inside' ? 'inside' : 'outside';
    return (<div className="grid w-full max-w-lg gap-2">
      <FileUpload disabled={disabled} uploading={uploading} invalid={invalid} opaque={opaque} accept={accept} aria-label="자료 업로드" placeholder={placeholder} acceptLabel={acceptLabel} itemsPlacement={resolvedItemsPlacement} maxFiles={optionalLimit(maxFiles)} maxSize={optionalLimit(maxSize)} multiple={multiple} size={resolvedSize} variant={resolvedVariant} value={files} onValueChange={setFiles} onError={(errors) => setError(errors[0]?.message ?? '')}/>
      {error ? (<p className="m-0 text-body-3 text-danger-700" role="alert">
          {error}
        </p>) : null}
    </div>);
}

export default function ExamplePreview() {
  return (<FileUploadExample itemsPlacement="outside" multiple size="md" variant="filled"/>)
}

FileUploadField로 사용하기

FileUploadField는 기본 입력에 label·required·description·error를 함께 제공합니다. FormStack 안에 직접 넣으며 FormField로 다시 감싸지 않습니다.

라벨·설명·필수 표시를 Field가 함께 처리합니다.

import { useState } from 'react'

import { type FileItem, FileUploadField } from '@amineslab/ui'

const fieldDescription = '라벨·설명·필수 표시를 Field가 함께 처리합니다.';

function FileUploadFieldExample() {
    const [value, setValue] = useState<FileItem[]>([]);
    return (<FileUploadField label="첨부 자료" description={fieldDescription} required value={value} onValueChange={setValue} maxSize={10}/>);
}

export default function ExamplePreview() {
  return (<FileUploadFieldExample />)
}

사용 기준

권장하는 사용
  • 파일 값은 controlled 목록으로 보관하고, 업로드 API와 진행률은 소비자가 주입합니다.
  • itemsPlacement는 drop 영역 안/밖 배치만 정하며 검증 계약은 동일합니다.
피해야 할 사용
  • 겉모양만으로 사용을 결정하지 마세요.
  • 의미와 키보드 동작, 모바일에서의 흐름을 함께 확인하세요.

접근성

화면에 맞는 이름을 제공하고, 키보드만으로 작업을 마칠 수 있는지 확인하세요.

  • Enter / Space: 드롭 영역에서 파일 chooser를 엽니다.
  • Drag / Drop: 대상 영역을 표시하고 같은 검증 흐름으로 추가합니다.