feat: ESLint 정리 및 전체 코드 품질 개선

- eslint.config.mjs 규칙 강화 및 정리
- 전역 unused import/변수 제거 (312개 파일)
- next.config.ts, middleware, proxy route 개선
- CopyableCell molecule 추가
- 회계/결재/HR/생산/건설/품질/영업 등 전 도메인 lint 정리
- IntegratedListTemplateV2, DataTable, MobileCard 등 공통 컴포넌트 개선
- execute-server-action 에러 핸들링 보강
This commit is contained in:
유병철
2026-03-11 10:27:10 +09:00
parent 924726cba1
commit 81affdc441
315 changed files with 1977 additions and 1344 deletions

View File

@@ -33,7 +33,6 @@ import { useDeleteDialog } from '@/hooks/useDeleteDialog';
import { CommentSection } from '../CommentSection';
import { deletePost } from '../actions';
import type { Post, Comment } from '../types';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
import { useMenuStore } from '@/stores/menuStore';
import { sanitizeHTML } from '@/lib/sanitize';

View File

@@ -13,13 +13,11 @@
import { useState, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { format } from 'date-fns';
import { Loader2 } from 'lucide-react';
import { toast } from 'sonner';
import { FileDropzone } from '@/components/ui/file-dropzone';
import { FileList, type NewFile, type ExistingFile } from '@/components/ui/file-list';
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
import { boardCreateConfig, boardEditConfig } from './boardFormConfig';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
@@ -40,7 +38,6 @@ import {
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
@@ -50,7 +47,7 @@ import {
import dynamic from 'next/dynamic';
const RichTextEditor = dynamic(() => import('../RichTextEditor'), { ssr: false });
import type { Post, Attachment } from '../types';
import type { Post } from '../types';
import { createPost, updatePost } from '../actions';
import { getBoards } from '../BoardManagement/actions';
import type { Board } from '../BoardManagement/types';
@@ -107,7 +104,7 @@ export function BoardForm({ mode, initialData }: BoardFormProps) {
// 게시판 목록 상태
const [boards, setBoards] = useState<Board[]>([]);
const [isBoardsLoading, setIsBoardsLoading] = useState(true);
const [isSubmitting, setIsSubmitting] = useState(false);
const [, setIsSubmitting] = useState(false);
// ===== 게시판 목록 조회 =====
useEffect(() => {

View File

@@ -29,7 +29,6 @@ import type { Post } from '../types';
import { getBoards } from '../BoardManagement/actions';
import { getPosts, getMyPosts, deletePost } from '../actions';
import type { Board } from '../BoardManagement/types';
import { toast } from 'sonner';
import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog';
export function BoardListUnified() {
@@ -133,7 +132,7 @@ export function BoardListUnified() {
totalCount: 0,
};
},
deleteItem: async (id: string) => {
deleteItem: async (_id: string) => {
// 게시글 삭제는 boardCode가 필요하므로 별도 처리
// UniversalListPage에서는 사용하지 않고 커스텀 삭제 처리
return { success: false, error: 'Use custom delete handler' };
@@ -143,10 +142,10 @@ export function BoardListUnified() {
// ===== 테이블 컬럼 =====
columns: [
{ key: 'no', label: 'No.', className: 'w-[60px] text-center' },
{ key: 'title', label: '제목', className: 'min-w-[300px]' },
{ key: 'author', label: '작성자', className: 'w-[120px]' },
{ key: 'createdAt', label: '등록일', className: 'w-[120px]' },
{ key: 'viewCount', label: '조회수', className: 'w-[80px] text-center' },
{ key: 'title', label: '제목', className: 'min-w-[300px]', copyable: true },
{ key: 'author', label: '작성자', className: 'w-[120px]', copyable: true },
{ key: 'createdAt', label: '등록일', className: 'w-[120px]', copyable: true },
{ key: 'viewCount', label: '조회수', className: 'w-[80px] text-center', copyable: true },
{ key: 'actions', label: '작업', className: 'w-[100px] text-center' },
],
@@ -161,7 +160,7 @@ export function BoardListUnified() {
detailMode: 'none', // 커스텀 라우팅 사용 (boardCode 포함)
// ===== 헤더 액션 =====
headerActions: ({ onCreate }) => (
headerActions: ({ onCreate: _onCreate }) => (
<>
<DateRangeSelector
startDate={startDate}

View File

@@ -132,7 +132,7 @@ export function BoardList() {
}, []);
// ===== 액션 핸들러 =====
const handleRowClick = useCallback(
const _handleRowClick = useCallback(
(item: Post) => {
router.push(`/ko/board/${item.boardCode}/${item.id}?mode=view`);
},
@@ -224,10 +224,10 @@ export function BoardList() {
columns: [
{ key: 'no', label: 'No.', className: 'w-[60px] text-center' },
{ key: 'title', label: '제목', className: 'min-w-[300px]', sortable: true },
{ key: 'author', label: '작성자', className: 'w-[120px]', sortable: true },
{ key: 'createdAt', label: '등록일', className: 'w-[120px]', sortable: true },
{ key: 'viewCount', label: '조회수', className: 'w-[80px] text-center', sortable: true },
{ key: 'title', label: '제목', className: 'min-w-[300px]', sortable: true, copyable: true },
{ key: 'author', label: '작성자', className: 'w-[120px]', sortable: true, copyable: true },
{ key: 'createdAt', label: '등록일', className: 'w-[120px]', sortable: true, copyable: true },
{ key: 'viewCount', label: '조회수', className: 'w-[80px] text-center', sortable: true, copyable: true },
{ key: 'actions', label: '작업', className: 'w-[100px] text-center' },
],

View File

@@ -7,7 +7,7 @@
* 기존 BoardDetail, BoardForm 컴포넌트 활용
*/
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { Loader2 } from 'lucide-react';
import { BoardDetail } from './BoardDetail';
@@ -17,7 +17,6 @@ import { forceRefreshMenus } from '@/lib/utils/menuRefresh';
import type { Board, BoardFormData } from './types';
import { DetailPageSkeleton } from '@/components/ui/skeleton';
import { ErrorCard } from '@/components/ui/error-card';
import { Button } from '@/components/ui/button';
import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog';
import { useDeleteDialog } from '@/hooks/useDeleteDialog';
import { toast } from 'sonner';

View File

@@ -17,7 +17,7 @@ import {
SelectValue,
} from '@/components/ui/select';
import { Checkbox } from '@/components/ui/checkbox';
import { ClipboardList, ArrowLeft, Save, X } from 'lucide-react';
import { ClipboardList, Save, X } from 'lucide-react';
import { useMenuStore } from '@/stores/menuStore';
import type { Board, BoardFormData, BoardTarget, BoardStatus } from './types';
import { BOARD_TARGETS, BOARD_STATUS_LABELS } from './types';

View File

@@ -39,7 +39,7 @@ const getTargetDisplay = (board: Board) => {
};
// 탭 옵션 계산
const computeTabs = (data: Board[]): TabOption[] => {
const _computeTabs = (data: Board[]): TabOption[] => {
const activeCount = data.filter((b) => b.status === 'active').length;
const inactiveCount = data.filter((b) => b.status === 'inactive').length;
@@ -82,11 +82,11 @@ const createBoardManagementConfig = (router: ReturnType<typeof useRouter>): Univ
// 테이블 컬럼
columns: [
{ key: 'rowNumber', label: 'No.', className: 'w-[60px] text-center' },
{ key: 'target', label: '대상', className: 'min-w-[100px]' },
{ key: 'boardName', label: '게시판명', className: 'min-w-[150px]' },
{ key: 'target', label: '대상', className: 'min-w-[100px]', copyable: true },
{ key: 'boardName', label: '게시판명', className: 'min-w-[150px]', copyable: true },
{ key: 'status', label: '상태', className: 'min-w-[80px]' },
{ key: 'authorName', label: '작성자', className: 'min-w-[100px]' },
{ key: 'createdAt', label: '등록일시', className: 'min-w-[120px]' },
{ key: 'authorName', label: '작성자', className: 'min-w-[100px]', copyable: true },
{ key: 'createdAt', label: '등록일시', className: 'min-w-[120px]', copyable: true },
],
// 탭 설정 (클라이언트 사이드 계산)
@@ -135,7 +135,7 @@ const createBoardManagementConfig = (router: ReturnType<typeof useRouter>): Univ
// 테이블 행 렌더링
renderTableRow: (item, index, globalIndex, handlers) => {
const { isSelected, onToggle, onRowClick, onEdit, onDelete } = handlers;
const { isSelected, onToggle, onRowClick, onEdit: _onEdit, onDelete: _onDelete } = handlers;
return (
<TableRow
@@ -164,7 +164,7 @@ const createBoardManagementConfig = (router: ReturnType<typeof useRouter>): Univ
// 모바일 카드 렌더링
renderMobileCard: (item, index, globalIndex, handlers) => {
const { isSelected, onToggle, onRowClick, onEdit, onDelete } = handlers;
const { isSelected, onToggle, onRowClick, onEdit: _onEdit, onDelete: _onDelete } = handlers;
return (
<ListMobileCard

View File

@@ -25,7 +25,7 @@ interface CommentSectionProps {
}
export function CommentSection({
postId,
postId: _postId,
comments,
currentUserId,
onAddComment,