diff --git a/app/api/lotto/_nas.ts b/app/api/lotto/_nas.ts index c9ce43c..e801efa 100644 --- a/app/api/lotto/_nas.ts +++ b/app/api/lotto/_nas.ts @@ -15,9 +15,9 @@ function nasBase() { return base; } -export async function nasGet(path: string): Promise { +export async function nasGet(path: string, timeoutMs = 25000): Promise { const res = await fetch(`${nasBase()}${path}`, { - headers: nasHeaders(), signal: AbortSignal.timeout(10000), + headers: nasHeaders(), signal: AbortSignal.timeout(timeoutMs), }); if (!res.ok) throw new Error(`NAS_${res.status}`); return res.json(); @@ -28,7 +28,7 @@ export async function nasPost(path: string, body: unknown): Promise { method: 'POST', headers: { ...nasHeaders(), 'Content-Type': 'application/json' }, body: JSON.stringify(body), - signal: AbortSignal.timeout(10000), + signal: AbortSignal.timeout(25000), }); if (!res.ok) throw new Error(`NAS_${res.status}`); return res.json(); @@ -39,7 +39,7 @@ export async function nasPut(path: string, body: unknown): Promise { method: 'PUT', headers: { ...nasHeaders(), 'Content-Type': 'application/json' }, body: JSON.stringify(body), - signal: AbortSignal.timeout(10000), + signal: AbortSignal.timeout(25000), }); if (!res.ok) throw new Error(`NAS_${res.status}`); return res.json(); @@ -47,7 +47,7 @@ export async function nasPut(path: string, body: unknown): Promise { export async function nasDelete(path: string): Promise { const res = await fetch(`${nasBase()}${path}`, { - method: 'DELETE', headers: nasHeaders(), signal: AbortSignal.timeout(10000), + method: 'DELETE', headers: nasHeaders(), signal: AbortSignal.timeout(25000), }); if (!res.ok) throw new Error(`NAS_${res.status}`); return res.json(); diff --git a/app/api/lotto/report/history/route.ts b/app/api/lotto/report/history/route.ts index cf3823a..bc641b6 100644 --- a/app/api/lotto/report/history/route.ts +++ b/app/api/lotto/report/history/route.ts @@ -1,6 +1,8 @@ import { NextResponse } from 'next/server'; import { nasGet, requireSubscription, handleNasError } from '../../_nas'; +export const maxDuration = 60; + export async function GET(request: Request) { try { const auth = await requireSubscription(); diff --git a/app/api/lotto/report/latest/route.ts b/app/api/lotto/report/latest/route.ts index 8b3c3ed..88758d9 100644 --- a/app/api/lotto/report/latest/route.ts +++ b/app/api/lotto/report/latest/route.ts @@ -1,6 +1,8 @@ import { NextResponse } from 'next/server'; import { nasGet, requireSubscription, handleNasError } from '../../_nas'; +export const maxDuration = 60; + export async function GET() { try { const auth = await requireSubscription(); diff --git a/app/api/lotto/stats/performance/route.ts b/app/api/lotto/stats/performance/route.ts index 20696d4..7d856f7 100644 --- a/app/api/lotto/stats/performance/route.ts +++ b/app/api/lotto/stats/performance/route.ts @@ -2,6 +2,8 @@ import { NextResponse } from 'next/server'; import { createClient } from '@/lib/supabase/server'; import { nasGet, handleNasError } from '../../_nas'; +export const maxDuration = 60; + export async function GET() { try { const supabase = await createClient(); diff --git a/app/services/lotto/recommend/PatternTab.tsx b/app/services/lotto/recommend/PatternTab.tsx index fc3a329..97c5864 100644 --- a/app/services/lotto/recommend/PatternTab.tsx +++ b/app/services/lotto/recommend/PatternTab.tsx @@ -65,7 +65,10 @@ export default function PatternTab() { useEffect(() => { fetch('/api/lotto/analysis/personal').then(r => r.json()) - .then(setData) + .then(d => { + if (d?.error) { setError(d.error === 'NAS_TIMEOUT' ? 'NAS 서버 응답 시간 초과.' : '패턴 분석을 불러오지 못했습니다.'); return; } + setData(d); + }) .catch(() => setError('패턴 분석을 불러오지 못했습니다.')) .finally(() => setLoading(false)); }, []); diff --git a/app/services/lotto/recommend/PurchaseTab.tsx b/app/services/lotto/recommend/PurchaseTab.tsx index 3af6b37..7589fb8 100644 --- a/app/services/lotto/recommend/PurchaseTab.tsx +++ b/app/services/lotto/recommend/PurchaseTab.tsx @@ -49,9 +49,10 @@ export default function PurchaseTab() { fetch('/api/lotto/purchase').then(r => r.json()), fetch('/api/lotto/purchase/stats').then(r => r.json()), ]); + if (recRes?.error || statRes?.error) throw new Error(recRes?.error ?? statRes?.error); setRecords(recRes.records ?? []); setStats(statRes); - } finally { setLoading(false); } + } catch { /* 에러 시 빈 상태 유지 */ } finally { setLoading(false); } }; useEffect(() => { load(); }, []); diff --git a/app/services/lotto/recommend/ReportTab.tsx b/app/services/lotto/recommend/ReportTab.tsx index 23e639e..18cabd7 100644 --- a/app/services/lotto/recommend/ReportTab.tsx +++ b/app/services/lotto/recommend/ReportTab.tsx @@ -78,8 +78,14 @@ export default function ReportTab() { fetch('/api/lotto/report/latest').then(r => r.json()), fetch('/api/lotto/report/history?limit=10').then(r => r.json()), ]).then(([rep, hist]) => { + if (rep?.error) { + setError(rep.error === 'NAS_TIMEOUT' + ? 'NAS 서버 응답 시간 초과. 잠시 후 다시 시도해주세요.' + : '리포트를 불러오지 못했습니다. (' + rep.error + ')'); + return; + } setReport(rep); - setHistory(hist.reports ?? []); + setHistory(hist?.reports ?? []); }).catch(() => setError('리포트를 불러오지 못했습니다.')) .finally(() => setLoading(false)); }, []); @@ -101,7 +107,7 @@ export default function ReportTab() {
{error}
); - if (!report) return null; + if (!report || !report.confidence_factors || !report.recommended_sets) return null; const strategyColors = ['#fbbf24', '#60a5fa', '#a78bfa'];