UNPKG

@cp949/mui

Version:

CP949 MUI React component library

146 lines (134 loc) 4.52 kB
import { useCallback, useRef, useState } from 'react'; import { useDebounceEffect } from './useDebounceEffect.js'; /** * 파일 업로드의 결과를 나타내는 데이터 구조. * * 파일 업로드가 성공적으로 완료된 후 반환되는 정보를 정의합니다. */ export interface FileUploadResult { /** * 업로드된 파일의 고유 ID. * 서버 또는 데이터베이스에서 파일을 식별하는 데 사용됩니다. */ id: string; /** * 업로드된 파일의 URL. * 업로드된 파일에 접근하거나 다운로드할 수 있는 경로를 제공합니다. */ url: string; /** * 업로드된 파일의 총 크기(바이트 단위). * 선택적 속성으로, 서버가 파일 크기를 반환하지 않는 경우 undefined일 수 있습니다. */ totalBytes?: number; /** * 업로드된 파일의 이름. * 파일의 원래 이름 또는 서버에서 생성한 이름일 수 있습니다. */ fileName?: string; /** * 업로드된 파일의 MIME 타입. * 예: `image/jpeg`, `application/pdf` 등 파일의 유형을 나타냅니다. */ contentType?: string; } /** * React 커스텀 훅으로 이미지를 업로드하고 업로드 상태를 관리합니다. * * 이 훅은 Blob 파일을 업로드하는 비동기 작업을 처리하며, 업로드 완료 시 결과를 콜백으로 반환합니다. * 또한, 업로드 상태(loading)를 관리합니다. * * @param file - 업로드할 파일(Blob). null 또는 undefined인 경우 업로드를 중단합니다. * @param handleFileUpload - 파일 업로드를 처리하는 비동기 함수. Blob 파일을 받아 서버에 업로드하고 업로드 결과를 반환해야 합니다. * @param callback - 업로드 완료 또는 실패 시 호출되는 콜백. 업로드 성공 시 결과(`FileUploadResult` 객체)를, 실패 또는 취소 시 null을 전달합니다. * * @returns 업로드가 진행 중인 상태를 나타내는 boolean 값. `true`면 업로드 중이고, `false`면 업로드가 완료되었거나 중단된 상태입니다. * * @example * ```tsx * import { useImageUpload } from './useImageUpload'; * * function FileUploader() { * const [file, setFile] = useState<Blob | null>(null); * const loading = useImageUpload( * file, * async (file) => { * // 서버에 파일 업로드 로직 * const response = await uploadFileToServer(file); * return { * id: response.id, * url: response.url, * totalBytes: response.size, * fileName: response.name, * contentType: response.type, * }; * }, * (result) => { * if (result) { * console.log('Upload success:', result.url); * } else { * console.log('Upload canceled or failed'); * } * } * ); * * const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { * const file = event.target.files?.[0]; * if (file) { * setFile(file); * } * }; * * return ( * <div> * <input type="file" onChange={handleChange} /> * {loading && <p>Uploading...</p>} * </div> * ); * } * ``` */ export function useImageUpload( file: Blob | null | undefined, handleFileUpload: (file: Blob) => Promise<FileUploadResult>, callback: (imageUrl: FileUploadResult | null) => void, ): boolean { const targetFile = file ?? null; const [loading, setLoading] = useState(false); const handleFileUploadRef = useRef(handleFileUpload); handleFileUploadRef.current = handleFileUpload; const callbackRef = useRef(callback); callbackRef.current = callback; const doUploadImageFile = useCallback( async (ctx: { canceled: boolean }, src: Blob): Promise<FileUploadResult | null> => { setLoading(true); try { const result = await handleFileUploadRef.current(src); if (ctx.canceled) return null; return result; } catch (err) { console.log('image load failed', err); } finally { setLoading(false); } return null; }, [], ); useDebounceEffect( () => { if (!targetFile) return; const ctx = { canceled: false }; doUploadImageFile(ctx, targetFile).then((result) => { if (ctx.canceled) return; callbackRef.current(result); }); return () => { ctx.canceled = true; }; }, [doUploadImageFile, targetFile], { wait: 100 }, ); return loading; }