refactor: tus 업로드 커스텀 훅 생성

This commit is contained in:
2025-04-10 14:39:19 +09:00
parent e456595868
commit 1f99d450e9
3 changed files with 79 additions and 78 deletions
-61
View File
@@ -1,61 +0,0 @@
import { Upload, type HttpResponse } from "tus-js-client";
import { getCookie, setCookie } from "@/shared/lib";
import { AUTH_USER_ID, CLAIM_NAME, ENDPOINT } from "@/shared/config";
type UploadOptions = {
handleUploadStart?: (upload: Upload) => void;
handleProgress?: (percentage: string) => void;
handleSuccess?: (upload: Upload) => void;
handleResponse?: (res: HttpResponse) => void;
};
export const handleUpload = (files: FileList, options?: UploadOptions) => {
let metadata = {};
const file = files.item(0);
if (!file) return;
metadata = {
filename: file.name,
filetype: file.type,
};
const atflCd = getCookie("atfl-cd-key");
if (atflCd) {
metadata = {
...metadata,
atflCd,
};
}
const upload = new Upload(file, {
endpoint: `${ENDPOINT.ATTACH_SERVICE.TUS_UPLOAD}`,
chunkSize: 10 * 1024 * 1024,
headers: {
[CLAIM_NAME]: getCookie(CLAIM_NAME) ?? "",
[AUTH_USER_ID]: getCookie(AUTH_USER_ID) ?? "",
},
metadata,
onError(error) {
console.error(`Failed because: ${error}`);
},
onProgress(bytesUploaded, bytesTotal) {
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
options?.handleProgress?.(percentage);
},
onAfterResponse(req, res) {
const fileInfo = res.getHeader("X-File-Info");
if (fileInfo === undefined || fileInfo === null) return;
setCookie("atfl-cd-key", JSON.parse(fileInfo)?.atflCd ?? "");
options?.handleResponse?.(res);
},
onSuccess() {
options?.handleSuccess?.(upload);
},
});
upload.start();
options?.handleUploadStart?.(upload);
console.log("upload start >>>>>", upload);
};
+15 -17
View File
@@ -4,7 +4,7 @@ import Image from "next/image";
import { useEffect, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import type { UseFormReturn } from "react-hook-form";
import type { HttpResponse, Upload } from "tus-js-client";
import type { HttpResponse } from "tus-js-client";
import {
FileUploader,
Input,
@@ -22,10 +22,9 @@ import { FormWrapper } from "@/widgets/form-wrapper";
import { CodeSelect } from "@/entities/code";
import { AttachmentApi } from "@/entities/attachment";
import { ENDPOINT } from "@/shared/config";
import { setCookie } from "@/shared/lib/cookie";
import { useTus } from "@/shared/lib/use-tus";
import { FormControl, FormField, FormItem } from "@/shared/ui/form";
import type { ImageFormData } from "../model/image-interface";
import { handleUpload } from "../lib/utils";
type ImageFormProps = {
form: UseFormReturn<ImageFormData>;
@@ -38,9 +37,13 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
const atflYn = form.watch("atflYn");
const atflCd = form.watch("atflCd");
const {
file: uploadFile,
response: uploadResponse,
progress,
setFile,
} = useTus();
const [imageUrl, setImageUrl] = useState("");
const [upload, setUpload] = useState<Upload | null>(null);
const [progress, setProgress] = useState(0);
const { data: attachmentData } = useQuery({
queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, atflCd],
@@ -49,10 +52,6 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
enabled: !!atflCd,
});
const handleProgress = (percentage: string) => {
setProgress(Number(percentage));
};
const handleUploadResponse = async (res: HttpResponse) => {
const fileInfo = res.getHeader("X-File-Info");
if (fileInfo === undefined || fileInfo === null) return;
@@ -64,6 +63,11 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
await AttachmentApi.deleteAttachmentByAtflCd(attachmentData.atflCd);
};
useEffect(() => {
if (!uploadResponse) return;
handleUploadResponse(uploadResponse);
}, [uploadResponse]);
useEffect(() => {
(async () => {
setImageUrl(
@@ -197,13 +201,7 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
onUpload={(e) => {
const files = e.target.files;
if (!files) return;
setCookie("atfl-cd-key", "");
handleUpload(files, {
handleUploadStart: setUpload,
handleProgress,
handleSuccess: setUpload,
handleResponse: handleUploadResponse,
});
setFile(files[0] ?? null);
}}
customPreview={
<>
@@ -220,7 +218,7 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
)}
</div>
<p className="absolute bottom-3 left-0 z-10 p-2 text-base leading-none">
{(upload?.file as File)?.name}
{uploadFile?.name}
</p>
<Progress
value={progress}
+64
View File
@@ -0,0 +1,64 @@
import { useEffect, useState } from "react";
import { type HttpResponse, Upload } from "tus-js-client";
import { AUTH_USER_ID, CLAIM_NAME, ENDPOINT } from "../config";
import { useAttachmentStore } from "./use-attachment-store";
import { getCookie } from "./cookie";
export const useTus = () => {
const { attachmentFileCode, setAttachmentFileCode } = useAttachmentStore(
(state) => state,
);
const [file, setFile] = useState<File | null>(null);
const [upload, setUpload] = useState<Upload | null>(null);
const [response, setResponse] = useState<HttpResponse | null>(null);
const [progress, setProgress] = useState<number>(0);
const metadata = {
filename: file?.name ?? "",
filetype: file?.type ?? "",
atflCd: attachmentFileCode,
};
const createUpload = () => {
return new Upload(file!, {
endpoint: `${ENDPOINT.ATTACH_SERVICE.TUS_UPLOAD}`,
chunkSize: 10 * 1024 * 1024,
headers: {
[CLAIM_NAME]: getCookie(CLAIM_NAME) ?? "",
[AUTH_USER_ID]: getCookie(AUTH_USER_ID) ?? "",
},
metadata,
onError(error) {
throw new Error(`Failed because: ${error}`);
},
onProgress(bytesUploaded, bytesTotal) {
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
setProgress(Number(percentage));
},
onAfterResponse(req, res) {
const fileInfo = res.getHeader("X-File-Info");
if (fileInfo === undefined || fileInfo === null) return;
setAttachmentFileCode(JSON.parse(fileInfo)?.atflCd ?? "");
setResponse(res);
},
});
};
useEffect(() => {
if (!file) return;
const upload = createUpload();
upload.start();
setUpload(upload);
}, [file]);
return {
file,
upload,
response,
progress,
setFile,
};
};