diff --git a/frontend/lib/registry/components/split-panel-layout2/ActionButtonConfigModal.tsx b/frontend/lib/registry/components/split-panel-layout2/ActionButtonConfigModal.tsx
new file mode 100644
index 00000000..aeff27c2
--- /dev/null
+++ b/frontend/lib/registry/components/split-panel-layout2/ActionButtonConfigModal.tsx
@@ -0,0 +1,674 @@
+"use client";
+
+import React, { useState, useEffect, useCallback } from "react";
+import {
+ DndContext,
+ closestCenter,
+ KeyboardSensor,
+ PointerSensor,
+ useSensor,
+ useSensors,
+ type DragEndEvent,
+} from "@dnd-kit/core";
+import {
+ arrayMove,
+ SortableContext,
+ sortableKeyboardCoordinates,
+ verticalListSortingStrategy,
+ useSortable,
+} from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
+import { Plus, GripVertical, Settings, X, Check, ChevronsUpDown } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+import { Input } from "@/components/ui/input";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/components/ui/command";
+import { Badge } from "@/components/ui/badge";
+import { cn } from "@/lib/utils";
+import { apiClient } from "@/lib/api/client";
+import type { ActionButtonConfig, ModalParamMapping, ColumnConfig } from "./types";
+
+interface ScreenInfo {
+ screen_id: number;
+ screen_name: string;
+ screen_code: string;
+}
+
+// 정렬 가능한 버튼 아이템
+const SortableButtonItem: React.FC<{
+ id: string;
+ button: ActionButtonConfig;
+ index: number;
+ onSettingsClick: () => void;
+ onRemove: () => void;
+}> = ({ id, button, index, onSettingsClick, onRemove }) => {
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
+
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ };
+
+ const getVariantColor = (variant?: string) => {
+ switch (variant) {
+ case "destructive":
+ return "bg-destructive/10 text-destructive";
+ case "outline":
+ return "bg-background border";
+ case "ghost":
+ return "bg-muted/50";
+ case "secondary":
+ return "bg-secondary text-secondary-foreground";
+ default:
+ return "bg-primary/10 text-primary";
+ }
+ };
+
+ const getActionLabel = (action?: string) => {
+ switch (action) {
+ case "add":
+ return "추가";
+ case "edit":
+ return "수정";
+ case "delete":
+ return "삭제";
+ case "bulk-delete":
+ return "일괄삭제";
+ case "api":
+ return "API";
+ case "custom":
+ return "커스텀";
+ default:
+ return "추가";
+ }
+ };
+
+ return (
+
+ {/* 드래그 핸들 */}
+
+
+
+
+ {/* 버튼 정보 */}
+
+
+
+ {button.label || `버튼 ${index + 1}`}
+
+
+
+
+ {getActionLabel(button.action)}
+
+ {button.icon && (
+
+ {button.icon}
+
+ )}
+ {button.showCondition && button.showCondition !== "always" && (
+
+ {button.showCondition === "selected" ? "선택시만" : "미선택시만"}
+
+ )}
+
+
+
+ {/* 액션 버튼들 */}
+
+
+
+
+
+ );
+};
+
+interface ActionButtonConfigModalProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ actionButtons: ActionButtonConfig[];
+ displayColumns?: ColumnConfig[]; // 모달 파라미터 매핑용
+ onSave: (buttons: ActionButtonConfig[]) => void;
+ side: "left" | "right";
+}
+
+export const ActionButtonConfigModal: React.FC = ({
+ open,
+ onOpenChange,
+ actionButtons: initialButtons,
+ displayColumns = [],
+ onSave,
+ side,
+}) => {
+ // 로컬 상태
+ const [buttons, setButtons] = useState([]);
+
+ // 버튼 세부설정 모달
+ const [detailModalOpen, setDetailModalOpen] = useState(false);
+ const [editingButtonIndex, setEditingButtonIndex] = useState(null);
+ const [editingButton, setEditingButton] = useState(null);
+
+ // 화면 목록
+ const [screens, setScreens] = useState([]);
+ const [screensLoading, setScreensLoading] = useState(false);
+ const [screenSelectOpen, setScreenSelectOpen] = useState(false);
+
+ // 드래그 센서
+ const sensors = useSensors(
+ useSensor(PointerSensor, {
+ activationConstraint: {
+ distance: 8,
+ },
+ }),
+ useSensor(KeyboardSensor, {
+ coordinateGetter: sortableKeyboardCoordinates,
+ })
+ );
+
+ // 초기값 설정
+ useEffect(() => {
+ if (open) {
+ setButtons(initialButtons || []);
+ }
+ }, [open, initialButtons]);
+
+ // 화면 목록 로드
+ const loadScreens = useCallback(async () => {
+ setScreensLoading(true);
+ try {
+ const response = await apiClient.get("/screen-management/screens?size=1000");
+
+ let screenList: any[] = [];
+ if (response.data?.success && Array.isArray(response.data?.data)) {
+ screenList = response.data.data;
+ } else if (Array.isArray(response.data?.data)) {
+ screenList = response.data.data;
+ }
+
+ const transformedScreens = screenList.map((s: any) => ({
+ screen_id: s.screenId ?? s.screen_id ?? s.id,
+ screen_name: s.screenName ?? s.screen_name ?? s.name ?? `화면 ${s.screenId || s.screen_id || s.id}`,
+ screen_code: s.screenCode ?? s.screen_code ?? s.code ?? "",
+ }));
+
+ setScreens(transformedScreens);
+ } catch (error) {
+ console.error("화면 목록 로드 실패:", error);
+ setScreens([]);
+ } finally {
+ setScreensLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ if (open) {
+ loadScreens();
+ }
+ }, [open, loadScreens]);
+
+ // 드래그 종료 핸들러
+ const handleDragEnd = (event: DragEndEvent) => {
+ const { active, over } = event;
+
+ if (over && active.id !== over.id) {
+ const oldIndex = buttons.findIndex((btn) => btn.id === active.id);
+ const newIndex = buttons.findIndex((btn) => btn.id === over.id);
+
+ if (oldIndex !== -1 && newIndex !== -1) {
+ setButtons(arrayMove(buttons, oldIndex, newIndex));
+ }
+ }
+ };
+
+ // 버튼 추가
+ const handleAddButton = () => {
+ const newButton: ActionButtonConfig = {
+ id: `btn-${Date.now()}`,
+ label: "새 버튼",
+ variant: "default",
+ action: "add",
+ showCondition: "always",
+ };
+ setButtons([...buttons, newButton]);
+ };
+
+ // 버튼 삭제
+ const handleRemoveButton = (index: number) => {
+ setButtons(buttons.filter((_, i) => i !== index));
+ };
+
+ // 버튼 업데이트
+ const handleUpdateButton = (index: number, updates: Partial) => {
+ const newButtons = [...buttons];
+ newButtons[index] = { ...newButtons[index], ...updates };
+ setButtons(newButtons);
+ };
+
+ // 버튼 세부설정 열기
+ const handleOpenDetailSettings = (index: number) => {
+ setEditingButtonIndex(index);
+ setEditingButton({ ...buttons[index] });
+ setDetailModalOpen(true);
+ };
+
+ // 버튼 세부설정 저장
+ const handleSaveDetailSettings = () => {
+ if (editingButtonIndex !== null && editingButton) {
+ handleUpdateButton(editingButtonIndex, editingButton);
+ }
+ setDetailModalOpen(false);
+ setEditingButtonIndex(null);
+ setEditingButton(null);
+ };
+
+ // 저장
+ const handleSave = () => {
+ onSave(buttons);
+ onOpenChange(false);
+ };
+
+ // 선택된 화면 정보
+ const getScreenInfo = (screenId?: number) => {
+ return screens.find((s) => s.screen_id === screenId);
+ };
+
+ return (
+ <>
+
+
+ {/* 버튼 세부설정 모달 */}
+
+ >
+ );
+};
+
+export default ActionButtonConfigModal;
+
diff --git a/frontend/lib/registry/components/split-panel-layout2/ColumnConfigModal.tsx b/frontend/lib/registry/components/split-panel-layout2/ColumnConfigModal.tsx
new file mode 100644
index 00000000..89866651
--- /dev/null
+++ b/frontend/lib/registry/components/split-panel-layout2/ColumnConfigModal.tsx
@@ -0,0 +1,805 @@
+"use client";
+
+import React, { useState, useEffect, useCallback } from "react";
+import {
+ DndContext,
+ closestCenter,
+ KeyboardSensor,
+ PointerSensor,
+ useSensor,
+ useSensors,
+ type DragEndEvent,
+} from "@dnd-kit/core";
+import {
+ arrayMove,
+ SortableContext,
+ sortableKeyboardCoordinates,
+ verticalListSortingStrategy,
+} from "@dnd-kit/sortable";
+import { Plus, Settings2 } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+import { Input } from "@/components/ui/input";
+import { Switch } from "@/components/ui/switch";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { cn } from "@/lib/utils";
+import { apiClient } from "@/lib/api/client";
+import { entityJoinApi } from "@/lib/api/entityJoin";
+import { Checkbox } from "@/components/ui/checkbox";
+import type { ColumnConfig, SearchColumnConfig, GroupingConfig, ColumnDisplayConfig, EntityReferenceConfig } from "./types";
+import { SortableColumnItem } from "./components/SortableColumnItem";
+import { SearchableColumnSelect } from "./components/SearchableColumnSelect";
+
+interface ColumnInfo {
+ column_name: string;
+ data_type: string;
+ column_comment?: string;
+ input_type?: string;
+ web_type?: string;
+ reference_table?: string;
+ reference_column?: string;
+}
+
+// 참조 테이블 컬럼 정보
+interface ReferenceColumnInfo {
+ columnName: string;
+ displayName: string;
+ dataType: string;
+}
+
+interface ColumnConfigModalProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ tableName: string;
+ displayColumns: ColumnConfig[];
+ searchColumns?: SearchColumnConfig[];
+ grouping?: GroupingConfig;
+ showSearch?: boolean;
+ onSave: (config: {
+ displayColumns: ColumnConfig[];
+ searchColumns: SearchColumnConfig[];
+ grouping: GroupingConfig;
+ showSearch: boolean;
+ }) => void;
+ side: "left" | "right"; // 좌측/우측 패널 구분
+}
+
+export const ColumnConfigModal: React.FC = ({
+ open,
+ onOpenChange,
+ tableName,
+ displayColumns: initialDisplayColumns,
+ searchColumns: initialSearchColumns,
+ grouping: initialGrouping,
+ showSearch: initialShowSearch,
+ onSave,
+ side,
+}) => {
+ // 로컬 상태 (모달 내에서만 사용, 저장 시 부모로 전달)
+ const [displayColumns, setDisplayColumns] = useState([]);
+ const [searchColumns, setSearchColumns] = useState([]);
+ const [grouping, setGrouping] = useState({ enabled: false, groupByColumn: "" });
+ const [showSearch, setShowSearch] = useState(false);
+
+ // 컬럼 세부설정 모달
+ const [detailModalOpen, setDetailModalOpen] = useState(false);
+ const [editingColumnIndex, setEditingColumnIndex] = useState(null);
+ const [editingColumn, setEditingColumn] = useState(null);
+
+ // 테이블 컬럼 목록
+ const [columns, setColumns] = useState([]);
+ const [columnsLoading, setColumnsLoading] = useState(false);
+
+ // 엔티티 참조 관련 상태
+ const [entityReferenceColumns, setEntityReferenceColumns] = useState