From ec9bd85ea8e4447604922995d8731eba06a796d4 Mon Sep 17 00:00:00 2001 From: gahusb Date: Mon, 16 Mar 2026 02:58:47 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20NAS=20=EB=B6=88=EA=B0=80=20=EC=8B=9C=20?= =?UTF-8?q?=EA=B5=AC=EB=8F=85=EC=9E=90=20=EC=B6=94=EC=B2=9C=EB=8F=84=20?= =?UTF-8?q?=ED=81=B4=EB=9D=BC=EC=9D=B4=EC=96=B8=ED=8A=B8=20Monte=20Carlo?= =?UTF-8?q?=20=ED=8F=B4=EB=B0=B1=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - recommend API: fetch 실패/503 시 NAS_UNAVAILABLE 반환 - 추천 페이지: 503 수신 시 클라이언트 Monte Carlo로 폴백 Co-Authored-By: Claude Sonnet 4.6 --- app/api/lotto/recommend/route.ts | 26 +++++++++++++++++++++----- app/services/lotto/recommend/page.tsx | 14 ++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/app/api/lotto/recommend/route.ts b/app/api/lotto/recommend/route.ts index 7d92b98..2fcb4ce 100644 --- a/app/api/lotto/recommend/route.ts +++ b/app/api/lotto/recommend/route.ts @@ -76,12 +76,31 @@ export async function GET(req: NextRequest) { ? '/api/lotto/best' : '/api/lotto/recommend'; - const nasRes = await nasGet(nasPath); + let nasRes: Response; + try { + nasRes = await nasGet(nasPath); + } catch (fetchErr: unknown) { + const e = fetchErr as { name?: string }; + console.warn('NAS unreachable:', fetchErr); + if (e?.name === 'TimeoutError') { + return NextResponse.json( + { error: 'NAS_UNAVAILABLE', plan: order.product_id, mode }, + { status: 503 } + ); + } + return NextResponse.json( + { error: 'NAS_UNAVAILABLE', plan: order.product_id, mode }, + { status: 503 } + ); + } if (!nasRes.ok) { const errText = await nasRes.text(); console.error('NAS API error:', nasRes.status, errText); - return NextResponse.json({ error: 'NAS_API_ERROR' }, { status: 502 }); + return NextResponse.json( + { error: 'NAS_UNAVAILABLE', plan: order.product_id, mode }, + { status: 503 } + ); } const nasData = await nasRes.json(); @@ -94,9 +113,6 @@ export async function GET(req: NextRequest) { }); } catch (err: unknown) { const e = err as { name?: string; message?: string }; - if (e?.name === 'TimeoutError') { - return NextResponse.json({ error: 'NAS_TIMEOUT' }, { status: 504 }); - } if (e?.message === 'NAS_URL_NOT_CONFIGURED') { return NextResponse.json({ error: 'NAS_URL_NOT_CONFIGURED' }, { status: 500 }); } diff --git a/app/services/lotto/recommend/page.tsx b/app/services/lotto/recommend/page.tsx index 88d13a7..5f34fc5 100644 --- a/app/services/lotto/recommend/page.tsx +++ b/app/services/lotto/recommend/page.tsx @@ -285,6 +285,20 @@ export default function LottoRecommendPage() { : '/api/lotto/recommend?mode=single'; const res = await fetch(url); if (res.status === 403) { setIsSubscribed(false); setProState('idle'); return; } + + // NAS 불가 시 클라이언트 Monte Carlo 폴백 + if (res.status === 503) { + const count = genMode === 'batch' ? Math.min(5, MAX_COMBOS - combos.length) : 1; + const newCombos: Combo[] = Array.from({ length: count }, () => { + idRef.current += 1; + const { numbers, metrics } = clientMonteCarlo(); + return { id: idRef.current, numbers, metrics, createdAt: new Date() }; + }); + setCombos((prev) => [...prev, ...newCombos].slice(-MAX_COMBOS)); + setProState('result'); + return; + } + if (!res.ok) { const e = await res.json(); throw new Error(e.error ?? 'API_ERROR'); } if (genMode === 'batch') {