From a8aa159cf0cc02dd2faf594dcaab53a6061315eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Sun, 22 Mar 2026 08:58:58 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[document]=20PDF=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=8B=9C=20cross-origin=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /api/image-proxy 프록시 라우트 추가 (CORS 우회) - convertImagesToBase64에서 cross-origin 이미지를 프록시로 fetch --- src/app/api/image-proxy/route.ts | 46 +++++++++++++++++++ .../document-system/viewer/DocumentViewer.tsx | 7 ++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/app/api/image-proxy/route.ts diff --git a/src/app/api/image-proxy/route.ts b/src/app/api/image-proxy/route.ts new file mode 100644 index 00000000..4b42d1ab --- /dev/null +++ b/src/app/api/image-proxy/route.ts @@ -0,0 +1,46 @@ +import { NextRequest, NextResponse } from 'next/server'; + +/** + * 이미지 프록시 API + * GET /api/image-proxy?url= + * + * CORS 우회를 위한 서버 사이드 이미지 프록시. + * PDF 생성 시 클라이언트에서 cross-origin 이미지를 base64로 변환할 때 사용. + */ +export async function GET(request: NextRequest) { + const url = request.nextUrl.searchParams.get('url'); + + if (!url) { + return NextResponse.json({ error: 'url parameter is required' }, { status: 400 }); + } + + try { + const response = await fetch(url, { + headers: { 'Accept': 'image/*' }, + signal: AbortSignal.timeout(10000), + }); + + if (!response.ok) { + return NextResponse.json( + { error: `Failed to fetch image: ${response.status}` }, + { status: response.status } + ); + } + + const contentType = response.headers.get('content-type') || 'image/jpeg'; + const buffer = await response.arrayBuffer(); + + return new NextResponse(buffer, { + status: 200, + headers: { + 'Content-Type': contentType, + 'Cache-Control': 'public, max-age=3600', + }, + }); + } catch { + return NextResponse.json( + { error: 'Failed to proxy image' }, + { status: 502 } + ); + } +} diff --git a/src/components/document-system/viewer/DocumentViewer.tsx b/src/components/document-system/viewer/DocumentViewer.tsx index ccf088f7..bbea8f23 100644 --- a/src/components/document-system/viewer/DocumentViewer.tsx +++ b/src/components/document-system/viewer/DocumentViewer.tsx @@ -173,8 +173,13 @@ export function DocumentViewer({ const src = img.src; if (!src || src.startsWith('data:')) return; + // cross-origin 이미지는 서버 프록시를 통해 fetch (CORS 우회) + const isSameOrigin = src.startsWith(window.location.origin) || src.startsWith('/'); + const fetchUrl = isSameOrigin ? src : `/api/image-proxy?url=${encodeURIComponent(src)}`; + try { - const response = await fetch(src); + const response = await fetch(fetchUrl); + if (!response.ok) throw new Error(`HTTP ${response.status}`); const blob = await response.blob(); const dataUrl = await new Promise((resolve, reject) => { const reader = new FileReader();