feat: 이미지 뷰잉 작업

This commit is contained in:
2025-04-04 18:40:30 +09:00
parent b0f8638df2
commit bc6ee47734
7 changed files with 88 additions and 27 deletions
+11 -1
View File
@@ -1,4 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config";
const serverApiUrl =
process.env.NEXT_PUBLIC_SERVER_API_URL ?? "http://localhost:3000";
@@ -16,8 +17,17 @@ export function middleware(req: NextRequest) {
}
if (pathname.startsWith("/attach-service")) {
const requestHeaders = new Headers(req.headers);
const token = req.cookies.get(CLAIM_NAME)?.value;
const userId = req.cookies.get(AUTH_USER_ID)?.value;
const newUrl = `${serverApiUrl}${pathname}`;
return NextResponse.rewrite(new URL(newUrl));
if (token) requestHeaders.set(CLAIM_NAME, token);
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
return NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders,
});
}
return NextResponse.next();
@@ -0,0 +1,17 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
Attachment,
AttachmentService,
} from "../model/attachment-interface";
export class AttachmentApiService implements AttachmentService {
public async getAttachments(atflCd: string): Promise<Attachment[]> {
const response = await client.get<Attachment[]>(
`${ENDPOINT.ATTACH_SERVICE.ATTACHMENTS}/${atflCd}`,
);
return response.data;
}
}
export const AttachmentApi = new AttachmentApiService();
+1
View File
@@ -0,0 +1 @@
export { AttachmentApi } from "./api/attachment-service";
@@ -0,0 +1,18 @@
export interface Attachment {
atflCd: string;
atflSn: number;
atflId: string;
physFileNm: string;
orgnlFileNm: string;
atflFlieSz: number;
atflType: string;
dwnldNocs: number;
delYn: string;
lnkgDmnId: string;
lnkgDmnNm: string;
regDt: string;
}
export interface AttachmentService {
getAttachments: (atflCd: string) => Promise<Attachment[]>;
}
+38 -25
View File
@@ -1,7 +1,9 @@
"use client";
import { useState } from "react";
import Image from "next/image";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import type { UseFormReturn } from "react-hook-form";
import type { HttpResponse, Upload } from "tus-js-client";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
@@ -20,10 +22,11 @@ import {
TableRow,
} from "@common/components";
import { CodeSelect } from "@/entities/code";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
import { AttachmentApi } from "@/entities/attachment";
import { ENDPOINT } from "@/shared/config";
import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form";
import { handleUpload } from "../lib/utils";
import type { ImageFormData } from "../model/image-interface";
import { handleUpload } from "../lib/utils";
type ImageFormProps = {
form: UseFormReturn<ImageFormData>;
@@ -38,49 +41,50 @@ function ImageForm({ form, imgSn, handleSave, handleDelete }: ImageFormProps) {
const router = useRouter();
const { confirm } = useConfirm();
const [onConfirm, setOnConfirm] = useState(false);
const [attachmentFileId, setAttachmentFileId] = useState("");
const [upload, setUpload] = useState<Upload | null>(null);
const [progress, setProgress] = useState(0);
const handleConfirm = async (callback: () => Promise<void>) => {
setOnConfirm(true);
await callback();
setOnConfirm(false);
};
const { data: atflId } = useQuery({
queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS],
queryFn: () => AttachmentApi.getAttachments(form.getValues("atflCd")!),
enabled: !!form.getValues("atflCd"),
select: (data) => data?.[data.length - 1]?.atflId,
});
const onSubmit = async (data: ImageFormData) => {
const confirmMessage = isModifyMode
? "수정하시겠습니까?"
: "저장하시겠습니까?";
await handleConfirm(async () => {
if (await confirm(confirmMessage)) {
setOnConfirm(false);
await handleSave(data);
}
});
if (await confirm(confirmMessage)) {
await handleSave(data);
}
};
const onDelete = async () => {
await handleConfirm(async () => {
if (await confirm("삭제하시겠습니까?")) {
setOnConfirm(false);
await handleDelete?.();
}
});
if (await confirm("삭제하시겠습니까?")) {
await handleDelete?.();
}
};
const handleProgress = (percentage: string) => {
setProgress(Number(percentage));
};
const handleUploadResponse = (res: HttpResponse) => {
const handleUploadResponse = async (res: HttpResponse) => {
const fileInfo = res.getHeader("X-File-Info");
if (fileInfo === undefined || fileInfo === null) return;
form.setValue("atflCd", JSON.parse(fileInfo)?.atflCd);
const { atflCd, atflId } = JSON.parse(fileInfo) ?? {};
if (atflCd) form.setValue("atflCd", atflCd);
if (atflId) setAttachmentFileId(atflId);
};
useEffect(() => {
if (atflId) setAttachmentFileId(atflId);
}, [atflId]);
return (
<>
<Form {...form}>
@@ -204,6 +208,7 @@ function ImageForm({ form, imgSn, handleSave, handleDelete }: ImageFormProps) {
<FileUploader
id="atflCd"
className="!h-auto !max-w-none flex-col !items-start"
accept=".jpeg,.jpg,.png"
onUpload={(e) => {
const files = e.target.files;
if (!files) return;
@@ -221,7 +226,16 @@ function ImageForm({ form, imgSn, handleSave, handleDelete }: ImageFormProps) {
<p className="absolute bottom-3 left-0 p-2 text-base leading-none">
{(upload?.file as File)?.name}
</p>
<div></div>
<div className="relative h-full w-full">
{attachmentFileId && (
<Image
src={`${ENDPOINT.ATTACH_SERVICE.IMAGES}/${attachmentFileId}`}
alt="업로드된 이미지"
fill
objectFit="contain"
/>
)}
</div>
<Progress
value={progress}
className="absolute bottom-0 left-0"
@@ -320,7 +334,6 @@ function ImageForm({ form, imgSn, handleSave, handleDelete }: ImageFormProps) {
</div>
</form>
</Form>
<ConfirmDialog open={onConfirm} />
</>
);
}
+2
View File
@@ -5,6 +5,8 @@ export const BASE_URL = `${PROXY_HOST}/server`;
export const ENDPOINT = {
ATTACH_SERVICE: {
TUS_UPLOAD: "/attach-service/api/v1/tus/upload",
IMAGES: "/attach-service/api/v1/images",
ATTACHMENTS: "/attach-service/api/v1/atfl/attachments",
},
USER_SERVICE: {
LOGIN: "/user-service/login",
@@ -26,8 +26,8 @@ export function FileUploader({
id={id}
type="file"
className="sr-only"
{...props}
onChange={onUpload}
{...props}
/>
{customPreview || (
<div className="file-uploader-preview">