diff --git a/lib/swp/table/swp-revision-list-dialog.tsx b/lib/swp/table/swp-revision-list-dialog.tsx
new file mode 100644
index 00000000..74402bd9
--- /dev/null
+++ b/lib/swp/table/swp-revision-list-dialog.tsx
@@ -0,0 +1,310 @@
+"use client";
+
+import React, { useState } from "react";
+import {
+ useReactTable,
+ getCoreRowModel,
+ getExpandedRowModel,
+ flexRender,
+ ExpandedState,
+} from "@tanstack/react-table";
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogDescription,
+} from "@/components/ui/dialog";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { Button } from "@/components/ui/button";
+import { Loader2 } from "lucide-react";
+import { swpRevisionColumns, swpFileColumns, type RevisionRow, type FileRow } from "./swp-table-columns";
+import type { SwpDocumentWithStats } from "../actions";
+
+interface SwpRevisionListDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ document: SwpDocumentWithStats | null;
+ revisions: RevisionRow[];
+ fileData: Record;
+ loadingRevisions: boolean;
+ loadingFiles: Set;
+ onLoadFiles: (revisionId: number) => void;
+ onLoadAllFiles: () => void;
+}
+
+export function SwpRevisionListDialog({
+ open,
+ onOpenChange,
+ document,
+ revisions,
+ fileData,
+ loadingRevisions,
+ loadingFiles,
+ onLoadFiles,
+ onLoadAllFiles,
+}: SwpRevisionListDialogProps) {
+ return (
+
+ );
+}
+
+// ============================================================================
+// 문서 상세 뷰 (Dialog용)
+// ============================================================================
+
+interface DocumentDetailViewProps {
+ revisions: RevisionRow[];
+ fileData: Record;
+ loadingFiles: Set;
+ onLoadFiles: (revisionId: number) => void;
+ onLoadAllFiles: () => void;
+}
+
+function DocumentDetailView({
+ revisions,
+ fileData,
+ loadingFiles,
+ onLoadFiles,
+ onLoadAllFiles,
+}: DocumentDetailViewProps) {
+ const [expandedRevisions, setExpandedRevisions] = useState({});
+ const [allExpanded, setAllExpanded] = useState(false);
+
+ const revisionTable = useReactTable({
+ data: revisions,
+ columns: swpRevisionColumns,
+ state: {
+ expanded: expandedRevisions,
+ },
+ onExpandedChange: setExpandedRevisions,
+ getCoreRowModel: getCoreRowModel(),
+ getExpandedRowModel: getExpandedRowModel(),
+ getRowCanExpand: () => true,
+ });
+
+ const handleExpandAll = () => {
+ if (allExpanded) {
+ setExpandedRevisions({});
+ } else {
+ const expanded: ExpandedState = {};
+ revisions.forEach((_, index) => {
+ expanded[index] = true;
+ });
+ setExpandedRevisions(expanded);
+ onLoadAllFiles();
+ }
+ setAllExpanded(!allExpanded);
+ };
+
+ const handleRevisionExpand = (revisionId: number) => {
+ onLoadFiles(revisionId);
+ };
+
+ return (
+
+ {/* 전체 펼치기/접기 버튼 */}
+
+
+
+
+ {/* 리비전 테이블 */}
+
+
+
+ {revisionTable.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ ))}
+
+ ))}
+
+
+ {revisionTable.getRowModel().rows.map((row) => (
+
+ {/* 리비전 행 */}
+
+ {row.getVisibleCells().map((cell) => (
+
+ {cell.column.id === "expander" ? (
+ {
+ row.toggleExpanded();
+ if (!row.getIsExpanded()) {
+ handleRevisionExpand(row.original.id);
+ }
+ }}
+ className="cursor-pointer"
+ >
+ {flexRender(
+ cell.column.columnDef.cell,
+ cell.getContext()
+ )}
+
+ ) : (
+ flexRender(cell.column.columnDef.cell, cell.getContext())
+ )}
+
+ ))}
+
+
+ {/* 파일 행들 (확장 시) */}
+ {row.getIsExpanded() && (
+
+
+ {loadingFiles.has(row.original.id) ? (
+
+
+ 파일 로딩 중...
+
+ ) : fileData[row.original.id]?.length ? (
+
+ ) : (
+
+ 파일 없음
+
+ )}
+
+
+ )}
+
+ ))}
+
+
+
+
+ );
+}
+
+// ============================================================================
+// 파일 서브 테이블
+// ============================================================================
+
+interface FileSubTableProps {
+ files: FileRow[];
+}
+
+function FileSubTable({ files }: FileSubTableProps) {
+ const fileTable = useReactTable({
+ data: files,
+ columns: swpFileColumns,
+ getCoreRowModel: getCoreRowModel(),
+ });
+
+ return (
+
+
+
+ {fileTable.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ ))}
+
+ ))}
+
+
+ {fileTable.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))}
+
+
+
+ );
+}
+
diff --git a/lib/swp/table/swp-table-toolbar.tsx b/lib/swp/table/swp-table-toolbar.tsx
index fc8337f5..e7a2ef30 100644
--- a/lib/swp/table/swp-table-toolbar.tsx
+++ b/lib/swp/table/swp-table-toolbar.tsx
@@ -227,10 +227,6 @@ export function SwpTableToolbar({
{isSyncing ? "동기화 중..." : "SWP 동기화"}
-
-
- SWP 문서 관리 시스템
-
{/* 벤더만 파일 업로드 기능 사용 가능 */}
diff --git a/lib/swp/table/swp-table.tsx b/lib/swp/table/swp-table.tsx
index 8ae90bdd..47c9905a 100644
--- a/lib/swp/table/swp-table.tsx
+++ b/lib/swp/table/swp-table.tsx
@@ -17,16 +17,9 @@ import {
TableRow,
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
-import {
- Dialog,
- DialogContent,
- DialogHeader,
- DialogTitle,
- DialogDescription,
-} from "@/components/ui/dialog";
-import { Loader2 } from "lucide-react";
-import { swpDocumentColumns, swpRevisionColumns, swpFileColumns, type RevisionRow, type FileRow } from "./swp-table-columns";
+import { swpDocumentColumns, type RevisionRow, type FileRow } from "./swp-table-columns";
import { fetchDocumentRevisions, fetchRevisionFiles, type SwpDocumentWithStats } from "../actions";
+import { SwpRevisionListDialog } from "./swp-revision-list-dialog";
interface SwpTableProps {
initialData: SwpDocumentWithStats[];
@@ -227,259 +220,17 @@ export function SwpTable({
{/* 문서 상세 Dialog */}
-
-
- );
-}
-
-// ============================================================================
-// 문서 상세 뷰 (Dialog용)
-// ============================================================================
-
-interface DocumentDetailViewProps {
- revisions: RevisionRow[];
- fileData: Record