Merge pull request 'feature/v2-unified-renewal' (#386) from feature/v2-unified-renewal into main
Reviewed-on: http://39.117.244.52:3000/kjs/ERP-node/pulls/386
This commit is contained in:
commit
420d9c141c
|
|
@ -2230,8 +2230,9 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
||||||
// value가 objid (숫자 또는 숫자 문자열)인 경우 파일 API URL 사용
|
// value가 objid (숫자 또는 숫자 문자열)인 경우 파일 API URL 사용
|
||||||
// 🔑 download 대신 preview 사용 (공개 접근 허용)
|
// 🔑 download 대신 preview 사용 (공개 접근 허용)
|
||||||
const isObjid = /^\d+$/.test(String(value));
|
const isObjid = /^\d+$/.test(String(value));
|
||||||
|
// 🔑 상대 경로(/api/...) 대신 전체 URL 사용 (Docker 환경에서 Next.js rewrite 의존 방지)
|
||||||
const imageUrl = isObjid
|
const imageUrl = isObjid
|
||||||
? `/api/files/preview/${value}`
|
? getFilePreviewUrl(String(value))
|
||||||
: getFullImageUrl(String(value));
|
: getFullImageUrl(String(value));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { apiClient } from "./client";
|
import { apiClient, API_BASE_URL } from "./client";
|
||||||
|
|
||||||
export interface FileInfo {
|
export interface FileInfo {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -231,10 +231,10 @@ export const getLinkedFiles = async (
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 파일 미리보기 URL 생성
|
* 파일 미리보기 URL 생성
|
||||||
|
* 🔑 상대 경로(/api) 대신 API_BASE_URL 사용 (Docker 환경에서 Next.js rewrite 의존 방지)
|
||||||
*/
|
*/
|
||||||
export const getFilePreviewUrl = (fileId: string): string => {
|
export const getFilePreviewUrl = (fileId: string): string => {
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_API_URL || "/api";
|
return `${API_BASE_URL}/files/preview/${fileId}`;
|
||||||
return `${baseUrl}/files/preview/${fileId}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -163,17 +163,12 @@ export const FileManagerModal: React.FC<FileManagerModalProps> = ({
|
||||||
const ext = file.fileExt.toLowerCase().replace('.', '');
|
const ext = file.fileExt.toLowerCase().replace('.', '');
|
||||||
if (imageExtensions.includes(ext) || file.isImage) {
|
if (imageExtensions.includes(ext) || file.isImage) {
|
||||||
try {
|
try {
|
||||||
// 🔑 이미 previewUrl이 있으면 바로 사용
|
|
||||||
if (file.previewUrl) {
|
|
||||||
setPreviewImageUrl(file.previewUrl);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이전 Blob URL 해제
|
// 이전 Blob URL 해제
|
||||||
if (previewImageUrl) {
|
if (previewImageUrl) {
|
||||||
URL.revokeObjectURL(previewImageUrl);
|
URL.revokeObjectURL(previewImageUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔑 항상 apiClient를 통해 Blob 다운로드 (Docker 환경에서 상대 경로 문제 방지)
|
||||||
const { apiClient } = await import("@/lib/api/client");
|
const { apiClient } = await import("@/lib/api/client");
|
||||||
const response = await apiClient.get(`/files/preview/${file.objid}`, {
|
const response = await apiClient.get(`/files/preview/${file.objid}`, {
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
|
|
@ -184,13 +179,8 @@ export const FileManagerModal: React.FC<FileManagerModalProps> = ({
|
||||||
setPreviewImageUrl(blobUrl);
|
setPreviewImageUrl(blobUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("이미지 로드 실패:", error);
|
console.error("이미지 로드 실패:", error);
|
||||||
// 🔑 에러 발생 시에도 previewUrl이 있으면 사용
|
|
||||||
if (file.previewUrl) {
|
|
||||||
setPreviewImageUrl(file.previewUrl);
|
|
||||||
} else {
|
|
||||||
setPreviewImageUrl(null);
|
setPreviewImageUrl(null);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setPreviewImageUrl(null);
|
setPreviewImageUrl(null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { uploadFiles, downloadFile, deleteFile, getComponentFiles, getFileInfoByObjid } from "@/lib/api/file";
|
import { uploadFiles, downloadFile, deleteFile, getComponentFiles, getFileInfoByObjid, getFilePreviewUrl } from "@/lib/api/file";
|
||||||
import { GlobalFileManager } from "@/lib/api/globalFile";
|
import { GlobalFileManager } from "@/lib/api/globalFile";
|
||||||
import { formatFileSize } from "@/lib/utils";
|
import { formatFileSize } from "@/lib/utils";
|
||||||
import { apiClient } from "@/lib/api/client";
|
import { apiClient } from "@/lib/api/client";
|
||||||
|
|
@ -205,9 +205,7 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previewUrl = `/api/files/preview/${objidStr}`;
|
// 🔑 실제 파일 정보 조회 (previewUrl 제거 - apiClient blob 다운로드 방식으로 통일)
|
||||||
|
|
||||||
// 🔑 실제 파일 정보 조회
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const fileInfoResponse = await getFileInfoByObjid(objidStr);
|
const fileInfoResponse = await getFileInfoByObjid(objidStr);
|
||||||
|
|
@ -220,15 +218,14 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
realFileName: realFileName,
|
realFileName: realFileName,
|
||||||
fileExt: fileExt,
|
fileExt: fileExt,
|
||||||
fileSize: fileSize,
|
fileSize: fileSize,
|
||||||
filePath: previewUrl,
|
filePath: getFilePreviewUrl(objidStr),
|
||||||
regdate: regdate,
|
regdate: regdate,
|
||||||
isImage: true,
|
isImage: true,
|
||||||
previewUrl: previewUrl,
|
|
||||||
isRepresentative: isRepresentative,
|
isRepresentative: isRepresentative,
|
||||||
};
|
};
|
||||||
|
|
||||||
setUploadedFiles([fileInfo]);
|
setUploadedFiles([fileInfo]);
|
||||||
setRepresentativeImageUrl(previewUrl);
|
// representativeImageUrl은 loadRepresentativeImage에서 blob으로 로드됨
|
||||||
} else {
|
} else {
|
||||||
// 파일 정보 조회 실패 시 최소 정보로 추가
|
// 파일 정보 조회 실패 시 최소 정보로 추가
|
||||||
console.warn("🖼️ [FileUploadComponent] 파일 정보 조회 실패, 최소 정보 사용");
|
console.warn("🖼️ [FileUploadComponent] 파일 정보 조회 실패, 최소 정보 사용");
|
||||||
|
|
@ -237,14 +234,13 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
realFileName: `image_${objidStr}.jpg`,
|
realFileName: `image_${objidStr}.jpg`,
|
||||||
fileExt: '.jpg',
|
fileExt: '.jpg',
|
||||||
fileSize: 0,
|
fileSize: 0,
|
||||||
filePath: previewUrl,
|
filePath: getFilePreviewUrl(objidStr),
|
||||||
regdate: new Date().toISOString(),
|
regdate: new Date().toISOString(),
|
||||||
isImage: true,
|
isImage: true,
|
||||||
previewUrl: previewUrl,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setUploadedFiles([minimalFileInfo]);
|
setUploadedFiles([minimalFileInfo]);
|
||||||
setRepresentativeImageUrl(previewUrl);
|
// representativeImageUrl은 loadRepresentativeImage에서 blob으로 로드됨
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("🖼️ [FileUploadComponent] 파일 정보 조회 오류:", error);
|
console.error("🖼️ [FileUploadComponent] 파일 정보 조회 오류:", error);
|
||||||
|
|
@ -875,14 +871,8 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔑 이미 previewUrl이 설정된 경우 바로 사용 (API 호출 스킵)
|
|
||||||
if (file.previewUrl) {
|
|
||||||
setRepresentativeImageUrl(file.previewUrl);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// API 클라이언트를 통해 Blob으로 다운로드 (인증 토큰 포함)
|
// API 클라이언트를 통해 Blob으로 다운로드 (인증 토큰 포함)
|
||||||
// 🔑 download 대신 preview 사용 (공개 접근)
|
// 🔑 previewUrl 상대 경로 대신 apiClient를 사용하여 Docker 환경에서도 정상 동작
|
||||||
const response = await apiClient.get(`/files/preview/${file.objid}`, {
|
const response = await apiClient.get(`/files/preview/${file.objid}`, {
|
||||||
params: {
|
params: {
|
||||||
serverFilename: file.savedFileName,
|
serverFilename: file.savedFileName,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { entityJoinApi } from "@/lib/api/entityJoin";
|
||||||
import { codeCache } from "@/lib/caching/codeCache";
|
import { codeCache } from "@/lib/caching/codeCache";
|
||||||
import { useEntityJoinOptimization } from "@/lib/hooks/useEntityJoinOptimization";
|
import { useEntityJoinOptimization } from "@/lib/hooks/useEntityJoinOptimization";
|
||||||
import { getFullImageUrl } from "@/lib/api/client";
|
import { getFullImageUrl } from "@/lib/api/client";
|
||||||
|
import { getFilePreviewUrl } from "@/lib/api/file";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { v2EventBus, V2_EVENTS, V2ErrorBoundary } from "@/lib/v2-core";
|
import { v2EventBus, V2_EVENTS, V2ErrorBoundary } from "@/lib/v2-core";
|
||||||
|
|
||||||
|
|
@ -4066,8 +4067,9 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
// 🔑 download 대신 preview 사용 (공개 접근 허용)
|
// 🔑 download 대신 preview 사용 (공개 접근 허용)
|
||||||
const strValue = String(value);
|
const strValue = String(value);
|
||||||
const isObjid = /^\d+$/.test(strValue);
|
const isObjid = /^\d+$/.test(strValue);
|
||||||
|
// 🔑 상대 경로(/api/...) 대신 전체 URL 사용 (Docker 환경에서 Next.js rewrite 의존 방지)
|
||||||
const imageUrl = isObjid
|
const imageUrl = isObjid
|
||||||
? `/api/files/preview/${strValue}`
|
? getFilePreviewUrl(strValue)
|
||||||
: getFullImageUrl(strValue);
|
: getFullImageUrl(strValue);
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,14 @@ const nextConfig = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// API 프록시 설정 - 백엔드로 요청 전달
|
// API 프록시 설정 - 백엔드로 요청 전달
|
||||||
|
// Docker 환경: SERVER_API_URL=http://backend:3001 사용
|
||||||
|
// 로컬 개발: http://localhost:8080 사용
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
|
const backendUrl = process.env.SERVER_API_URL || "http://localhost:8080";
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
source: "/api/:path*",
|
source: "/api/:path*",
|
||||||
destination: "http://localhost:8080/api/:path*",
|
destination: `${backendUrl}/api/:path*`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue