fix(WEB): 토큰 만료 시 무한 로딩 대신 로그인 리다이렉트 처리

- 52개 이상의 컴포넌트에 isNextRedirectError 처리 추가
- Server Action의 redirect() 에러가 catch 블록에서 삼켜지는 문제 해결
- access_token + refresh_token 모두 만료 시 정상적으로 로그인 페이지로 리다이렉트

수정된 영역:
- accounting: 10개 컴포넌트
- production: 12개 컴포넌트
- hr: 5개 컴포넌트
- settings: 8개 컴포넌트
- approval: 5개 컴포넌트
- items: 20개+ 컴포넌트
- board: 5개 컴포넌트
- quality: 4개 컴포넌트
- material, outbound, quotes 등 기타 컴포넌트

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2026-01-11 17:19:11 +09:00
parent 8bc4b90fe9
commit e56b7d53a4
131 changed files with 3320 additions and 1979 deletions

View File

@@ -7,7 +7,8 @@
import { useState, useMemo, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { Package, AlertCircle, Loader2, List } from 'lucide-react';
import { Package, AlertCircle, List } from 'lucide-react';
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
@@ -28,6 +29,7 @@ import {
LOT_STATUS_LABELS,
} from './types';
import type { StockDetail, LotDetail } from './types';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
interface StockStatusDetailProps {
id: string;
@@ -55,6 +57,7 @@ export function StockStatusDetail({ id }: StockStatusDetailProps) {
setError(result.error || '재고 정보를 찾을 수 없습니다.');
}
} catch (err) {
if (isNextRedirectError(err)) throw err;
console.error('[StockStatusDetail] loadData error:', err);
setError('데이터를 불러오는 중 오류가 발생했습니다.');
} finally {
@@ -90,9 +93,7 @@ export function StockStatusDetail({ id }: StockStatusDetailProps) {
if (isLoading) {
return (
<PageLayout>
<div className="flex items-center justify-center min-h-[400px]">
<Loader2 className="w-8 h-8 animate-spin text-primary" />
</div>
<ContentLoadingSpinner text="재고 정보를 불러오는 중..." />
</PageLayout>
);
}

View File

@@ -15,8 +15,8 @@ import {
AlertCircle,
Download,
Eye,
Loader2,
} from 'lucide-react';
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { TableCell, TableRow } from '@/components/ui/table';
@@ -30,6 +30,7 @@ import {
import { ListMobileCard, InfoField } from '@/components/organisms/ListMobileCard';
import { getStocks, getStockStats, getStockStatsByType } from './actions';
import { ITEM_TYPE_LABELS, ITEM_TYPE_STYLES, STOCK_STATUS_LABELS } from './types';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
import type { StockItem, StockStats, ItemType } from './types';
// 페이지당 항목 수
@@ -84,6 +85,7 @@ export function StockStatusList() {
setTypeStats(typeStatsResult.data);
}
} catch (err) {
if (isNextRedirectError(err)) throw err;
console.error('[StockStatusList] loadData error:', err);
setError('데이터를 불러오는 중 오류가 발생했습니다.');
} finally {
@@ -332,11 +334,7 @@ export function StockStatusList() {
// 로딩 상태 표시 (모든 Hooks 선언 후)
if (isLoading && items.length === 0) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<Loader2 className="w-8 h-8 animate-spin text-primary" />
</div>
);
return <ContentLoadingSpinner text="재고 목록을 불러오는 중..." />;
}
// 에러 상태 표시