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

@@ -42,6 +42,7 @@ import { toast } from 'sonner';
import { CommentSection } from '../CommentSection';
import { deletePost } from '../actions';
import type { Post, Comment } from '../types';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
interface BoardDetailProps {
post: Post;
@@ -77,6 +78,7 @@ export function BoardDetail({ post, comments: initialComments, currentUserId }:
toast.error(result.error || '게시글 삭제에 실패했습니다.');
}
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('게시글 삭제 오류:', error);
toast.error('게시글 삭제에 실패했습니다.');
} finally {

View File

@@ -49,6 +49,7 @@ import type { Post, Attachment } from '../types';
import { createPost, updatePost } from '../actions';
import { getBoards } from '../BoardManagement/actions';
import type { Board } from '../BoardManagement/types';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
interface BoardFormProps {
mode: 'create' | 'edit';
@@ -102,6 +103,7 @@ export function BoardForm({ mode, initialData }: BoardFormProps) {
setBoards(result.data);
}
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('게시판 목록 조회 오류:', error);
toast.error('게시판 목록을 불러오지 못했습니다.');
} finally {
@@ -190,6 +192,7 @@ export function BoardForm({ mode, initialData }: BoardFormProps) {
toast.error(result?.error || '게시글 저장에 실패했습니다.');
}
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('게시글 저장 오류:', error);
toast.error('게시글 저장에 실패했습니다.');
} finally {

View File

@@ -12,7 +12,8 @@
import { useState, useMemo, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { format } from 'date-fns';
import { FileText, Plus, Pencil, Trash2, Loader2 } from 'lucide-react';
import { FileText, Plus, Pencil, Trash2 } from 'lucide-react';
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { TableRow, TableCell } from '@/components/ui/table';
@@ -36,6 +37,7 @@ import { ListMobileCard, InfoField } from '@/components/organisms/ListMobileCard
import { toast } from 'sonner';
import type { Post, SortOption } from '../types';
import { getBoards } from '../BoardManagement/actions';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
import { getPosts, getMyPosts, deletePost } from '../actions';
import type { Board } from '../BoardManagement/types';
@@ -122,6 +124,7 @@ export function BoardList() {
}
}
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('게시글 조회 오류:', error);
setPosts([]);
} finally {
@@ -404,11 +407,7 @@ export function BoardList() {
// ===== 로딩 상태 =====
if (isLoading && posts.length === 0) {
return (
<div className="flex items-center justify-center h-64">
<Loader2 className="h-8 w-8 animate-spin text-gray-400" />
</div>
);
return <ContentLoadingSpinner text="게시글을 불러오는 중..." />;
}
return (

View File

@@ -3,6 +3,7 @@
import { useState, useMemo, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { ClipboardList, Edit, Trash2, Plus, Loader2 } from 'lucide-react';
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
import { getBoards, deleteBoard, deleteBoardsBulk } from './actions';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
@@ -349,11 +350,7 @@ export function BoardManagement() {
// 로딩 상태
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<Loader2 className="w-8 h-8 animate-spin text-muted-foreground" />
</div>
);
return <ContentLoadingSpinner text="게시판 목록을 불러오는 중..." />;
}
// 에러 상태

View File

@@ -29,6 +29,7 @@ import {
} from '@/components/ui/popover';
import { Input } from '@/components/ui/input';
import { useState } from 'react';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
interface MenuBarProps {
editor: Editor | null;
@@ -73,6 +74,7 @@ export function MenuBar({ editor, onImageUpload }: MenuBarProps) {
const url = await onImageUpload(file);
editor.chain().focus().setImage({ src: url }).run();
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('Image upload failed:', error);
}
} else {