메인 콘텐츠로 건너뛰기

useDropup 훅

드래그 앤 드롭을 지원하는 파일 업로드의 메인 훅입니다.

임포트

import { useDropup } from '@samithahansaka/dropup';

기본 사용법

const {
files,
state,
actions,
getDropProps,
getInputProps,
openFileDialog,
} = useDropup(options);

매개변수

options (선택사항)

전체 세부사항은 옵션을 참조하세요.

interface UseDropupOptions {
// 유효성 검사
accept?: string | string[];
maxSize?: number;
minSize?: number;
maxFiles?: number;
maxWidth?: number;
maxHeight?: number;
minWidth?: number;
minHeight?: number;
customRules?: ValidationRule[];

// 동작
multiple?: boolean;
disabled?: boolean;
autoUpload?: boolean;
generatePreviews?: boolean;

// 업로드 설정
upload?: UploadConfig | CustomUploader;

// 콜백
onFilesAdded?: (files: DropupFile[]) => void;
onFileRemoved?: (file: DropupFile) => void;
onValidationError?: (errors: ValidationError[]) => void;
onUploadStart?: (file: DropupFile) => void;
onUploadProgress?: (file: DropupFile, progress: number) => void;
onUploadComplete?: (file: DropupFile, response: unknown) => void;
onUploadError?: (file: DropupFile, error: DropupError) => void;
onAllComplete?: (files: DropupFile[]) => void;
}

반환 값

전체 세부사항은 반환 값을 참조하세요.

interface UseDropupReturn {
// 상태
files: DropupFile[];
state: DropupState;

// 액션
actions: DropupActions;

// Props 게터
getDropProps: <E extends HTMLElement>(props?: HTMLAttributes<E>) => DropZoneProps<E>;
getInputProps: (props?: InputHTMLAttributes) => InputProps;

// 유틸리티
openFileDialog: () => void;
}

빠른 참조

Files 배열

const { files } = useDropup();

// 각 파일에는 다음이 있습니다:
files[0].id // 고유 ID
files[0].file // 원본 File 객체
files[0].name // 파일 이름
files[0].size // 바이트 단위 크기
files[0].type // MIME 타입
files[0].preview // 미리보기 URL (이미지)
files[0].status // 'idle' | 'uploading' | 'complete' | 'error'
files[0].progress // 0-100
files[0].uploadedUrl // 업로드 후 URL
files[0].error // 실패 시 오류

State 객체

const { state } = useDropup();

state.isDragging // 파일을 드래그 중인지
state.isDragAccept // 드래그된 파일이 유효한지
state.isDragReject // 드래그된 파일이 무효한지
state.isUploading // 파일 업로드 중인지
state.progress // 전체 진행률 0-100
state.status // 'idle' | 'uploading' | 'complete' | 'error'

Actions 객체

const { actions } = useDropup();

actions.upload(fileIds?) // 업로드 시작
actions.cancel(fileId?) // 업로드 취소
actions.remove(fileId) // 파일 제거
actions.reset() // 모두 초기화
actions.retry(fileIds?) // 실패한 것 재시도
actions.addFiles(files) // 프로그래밍 방식으로 파일 추가
actions.updateFileMeta(id, meta) // 메타데이터 업데이트

Props 게터

const { getDropProps, getInputProps } = useDropup();

// 드롭 영역에 적용
<div {...getDropProps()}>
<input {...getInputProps()} />
여기에 드롭
</div>

// 커스텀 props와 함께
<div {...getDropProps({ className: 'my-dropzone', onClick: handleClick })}>
...
</div>

예제

최소 예제

function Uploader() {
const { files, getDropProps, getInputProps } = useDropup();

return (
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>파일을 여기에 드롭하세요</p>
<ul>
{files.map(f => <li key={f.id}>{f.name}</li>)}
</ul>
</div>
);
}

업로드 포함

function Uploader() {
const { files, actions, getDropProps, getInputProps } = useDropup({
upload: {
url: '/api/upload',
method: 'POST',
headers: { 'Authorization': 'Bearer token' },
},
});

return (
<div>
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>파일을 여기에 드롭하세요</p>
</div>

{files.map(f => (
<div key={f.id}>
{f.name} - {f.status}
{f.status === 'uploading' && ` ${f.progress}%`}
</div>
))}

<button onClick={() => actions.upload()}>모두 업로드</button>
</div>
);
}

모든 옵션 포함

function FullFeaturedUploader() {
const {
files,
state,
actions,
getDropProps,
getInputProps,
} = useDropup({
// 유효성 검사
accept: 'image/*',
maxSize: 10 * 1024 * 1024,
maxFiles: 5,

// 동작
multiple: true,
autoUpload: true,
generatePreviews: true,

// 업로드
upload: {
url: '/api/upload',
method: 'POST',
},

// 콜백
onFilesAdded: (files) => console.log('추가됨:', files),
onUploadComplete: (file) => console.log('완료:', file.uploadedUrl),
onUploadError: (file, err) => console.error('오류:', err),
onAllComplete: () => console.log('모든 업로드 완료!'),
});

return (
<div>
<div
{...getDropProps()}
style={{
border: `2px dashed ${state.isDragAccept ? 'green' : state.isDragReject ? 'red' : 'gray'}`,
padding: 40,
}}
>
<input {...getInputProps()} />
<p>이미지를 여기에 드롭하세요 (최대 5개, 각 10MB)</p>
</div>

{files.map(file => (
<div key={file.id}>
{file.preview && <img src={file.preview} width={50} />}
<span>{file.name}</span>
<span>{file.status}</span>
{file.status === 'uploading' && <span>{file.progress}%</span>}
<button onClick={() => actions.remove(file.id)}>×</button>
</div>
))}

{state.isUploading && <p>업로드 중... {state.progress}%</p>}
</div>
);
}