diff --git a/docker/dev/docker-compose.backend.mac.yml b/docker/dev/docker-compose.backend.mac.yml index 48e1fa09..63d2e3ed 100644 --- a/docker/dev/docker-compose.backend.mac.yml +++ b/docker/dev/docker-compose.backend.mac.yml @@ -1,3 +1,5 @@ +name: vexplor_rps + services: # Node.js 백엔드 backend: diff --git a/docker/dev/docker-compose.frontend.mac.yml b/docker/dev/docker-compose.frontend.mac.yml index 4103d2ca..cf7dd3bc 100644 --- a/docker/dev/docker-compose.frontend.mac.yml +++ b/docker/dev/docker-compose.frontend.mac.yml @@ -1,3 +1,5 @@ +name: vexplor_rps + services: # Next.js 프론트엔드만 frontend: diff --git a/frontend/app/(main)/admin/menu/page.tsx b/frontend/app/(main)/admin/menu/page.tsx index a07da807..db89394f 100644 --- a/frontend/app/(main)/admin/menu/page.tsx +++ b/frontend/app/(main)/admin/menu/page.tsx @@ -873,17 +873,18 @@ export default function MenuPage() { } return ( -
-
- {/* 페이지 헤더 */} -
-

메뉴 관리

-

시스템 메뉴를 관리하고 화면을 할당합니다

+
+
+ {/* 페이지 헤더 — 고정 */} +
+

메뉴 관리

+

시스템 메뉴를 관리하고 화면을 할당합니다

- {/* 메인 컨텐츠 */} + {/* 메인 컨텐츠 — 남는 공간 차지 */} +
-
+
{/* 좌측 사이드바 - 메뉴 타입 선택 (20%) */}
@@ -933,8 +934,8 @@ export default function MenuPage() {
{/* 우측 메인 영역 - 메뉴 목록 (80%) */} -
-
+
+
{/* 상단 헤더: 제목 + 검색 + 버튼 */}
{/* 왼쪽: 제목 */} @@ -1104,6 +1105,7 @@ export default function MenuPage() {
+
- {/* 컴팩트 탑바 (52px) */} -
-
- -

+
+ {/* 컴팩트 탑바 (40px) */} +
+
+ +

{getTextFromUI(TABLE_MANAGEMENT_KEYS.PAGE_TITLE, "테이블 타입 관리")}

@@ -1298,7 +1298,7 @@ export default function TableManagementPage() { setCreateTableModalOpen(true); }} size="sm" - className="h-8 gap-1.5 text-xs" + className="h-7 gap-1 px-2 text-[11px]" > 새 테이블 @@ -1317,7 +1317,7 @@ export default function TableManagementPage() { variant="outline" size="sm" disabled={selectedTableIds.size !== 1} - className="h-8 gap-1.5 text-xs" + className="h-7 gap-1 px-2 text-[11px]" > 복제 @@ -1327,7 +1327,7 @@ export default function TableManagementPage() { onClick={() => setAddColumnModalOpen(true)} variant="outline" size="sm" - className="h-8 gap-1.5 text-xs" + className="h-7 gap-1 px-2 text-[11px]" > 컬럼 추가 @@ -1337,7 +1337,7 @@ export default function TableManagementPage() { onClick={() => setDdlLogViewerOpen(true)} variant="ghost" size="sm" - className="h-8 gap-1.5 text-xs" + className="h-7 gap-1 px-2 text-[11px]" > DDL @@ -1358,21 +1358,21 @@ export default function TableManagementPage() { {/* 3패널 메인 */}
- {/* 좌측: 테이블 목록 (240px) */} -
+ {/* 좌측: 테이블 목록 (컴팩트 240px) */} +
{/* 검색 */} -
+
- + setSearchTerm(e.target.value)} - className="bg-background h-[34px] pl-8 text-xs" + className="bg-background h-7 pl-7 text-[11px]" />
{isSuperAdmin && ( -
+
{selectedTableIds.size > 0 ? `${selectedTableIds.size}개` : "전체"} @@ -1392,9 +1392,9 @@ export default function TableManagementPage() { variant="destructive" size="sm" onClick={handleBulkDeleteClick} - className="h-6 gap-1 px-2 text-[10px]" + className="h-5 gap-1 px-1.5 text-[10px]" > - + 삭제 )} @@ -1423,13 +1423,13 @@ export default function TableManagementPage() { return (
{showDivider && ( -
+
{isKo ? "한글" : "ENGLISH"}
)}
{isActive && ( -
+
)} {isSuperAdmin && ( handleTableCheck(table.tableName, checked as boolean)} aria-label={`${table.displayName || table.tableName} 선택`} - className="h-3.5 w-3.5 flex-shrink-0" + className="h-3 w-3 flex-shrink-0" onClick={(e) => e.stopPropagation()} /> )}
{table.displayName || table.tableName}
-
+
{table.tableName}
{/* 하단 정보 */} -
+
{filteredTables.length} / {tables.length} 테이블
@@ -1501,42 +1501,42 @@ export default function TableManagementPage() {
) : ( <> - {/* 중앙 헤더: 테이블명 + 라벨 입력 + 저장 */} -
-
-
+ {/* 중앙 헤더: 테이블명 + 표시명/설명 + 저장 — 한 줄 컴팩트 */} +
+
+
{tableLabel || selectedTable}
-
+
{selectedTable}
-
+
setTableLabel(e.target.value)} placeholder="표시명" - className="h-8 max-w-[160px] text-xs" + className="h-7 text-[11px]" /> setTableDescription(e.target.value)} placeholder="설명" - className="h-8 max-w-[200px] text-xs" + className="h-7 text-[11px]" />
diff --git a/frontend/components/admin/MenuTable.tsx b/frontend/components/admin/MenuTable.tsx index 644c84f3..11077e69 100644 --- a/frontend/components/admin/MenuTable.tsx +++ b/frontend/components/admin/MenuTable.tsx @@ -148,10 +148,10 @@ export const MenuTable: React.FC = ({ }; return ( -
- {title &&

{title}

} -
-
+
+ {title &&

{title}

} +
+
diff --git a/frontend/components/admin/table-type/ColumnGrid.tsx b/frontend/components/admin/table-type/ColumnGrid.tsx index 825dbd36..3f04263c 100644 --- a/frontend/components/admin/table-type/ColumnGrid.tsx +++ b/frontend/components/admin/table-type/ColumnGrid.tsx @@ -80,21 +80,24 @@ export function ColumnGrid({ const totalFiltered = filteredAndGrouped.basic.length + filteredAndGrouped.reference.length + filteredAndGrouped.meta.length; + const GRID_TEMPLATE = "3px minmax(180px,1fr) minmax(160px,1.4fr) 90px 124px 28px"; + return ( -
+
+ {/* 컬럼 헤더 — 컴팩트 */}
라벨 · 컬럼명 - 참조/설정 + 참조 / 기본값 타입 - PK / NN / IDX / UQ + PK · NN · IDX · UQ
-
+
{totalFiltered === 0 ? (
{typeFilter ? "해당 타입의 컬럼이 없습니다." : "컬럼이 없습니다."} @@ -105,16 +108,18 @@ export function ColumnGrid({ if (list.length === 0) return null; const { icon: Icon, label } = GROUP_LABELS[groupKey]; return ( -
-
- - +
+ {/* 그룹 헤더 — 한 줄 컴팩트 */} +
+ + {label} - + {list.length}
+
{list.map((column) => { const typeConf = INPUT_TYPE_COLORS[column.inputType || "text"] || INPUT_TYPE_COLORS.text; const idxState = getIdxState(column.columnName); @@ -133,31 +138,40 @@ export function ColumnGrid({ } }} className={cn( - "grid min-h-12 cursor-pointer items-center gap-2 rounded-md border px-4 py-2 transition-colors", - "grid-cols-[4px_140px_1fr_100px_160px_40px]", - "bg-card border-transparent hover:border-border hover:shadow-sm", - isSelected && "border-primary/30 bg-primary/5 shadow-sm", + "group grid h-9 cursor-pointer items-center gap-2 px-3 transition-colors", + "hover:bg-muted/40", + isSelected && "bg-primary/5 ring-1 ring-inset ring-primary/30", )} + style={{ gridTemplateColumns: GRID_TEMPLATE }} > - {/* 4px 색상바 (타입별 진한 색) */} -
+ {/* 3px 색상바 (타입별 진한 색) */} +
- {/* 라벨 + 컬럼명 (한글라벨 (영어명) 동시 표시) */} -
-
- {column.displayName && column.displayName !== column.columnName - ? `${column.displayName} (${column.columnName})` - : column.columnName} -
+ {/* 라벨 + 컬럼명 — 한글 라벨이 우선, 영문명은 옆에 모노폰트 */} +
+ {column.displayName && column.displayName !== column.columnName ? ( + <> + + {column.displayName} + + + {column.columnName} + + + ) : ( + + {column.columnName} + + )}
- {/* 참조/설정 칩 */} -
- {column.inputType === "entity" && column.referenceTable && column.referenceTable !== "none" && ( + {/* 참조/설정 칩 — 한 줄 nowrap, 넘치면 잘림 */} +
+ {column.inputType === "entity" && column.referenceTable && column.referenceTable !== "none" ? ( <> { @@ -171,10 +185,10 @@ export function ColumnGrid({ > {column.referenceTable} - + { @@ -190,42 +204,36 @@ export function ColumnGrid({ {column.referenceColumn || "—"} - )} - {column.inputType === "code" && ( - - {column.codeCategory ?? "—"} · {column.defaultValue ?? ""} + ) : column.inputType === "code" ? ( + + {column.codeCategory ?? "—"}{column.defaultValue ? ` · ${column.defaultValue}` : ""} - )} - {column.inputType === "numbering" && column.numberingRuleId && ( - + ) : column.inputType === "numbering" && column.numberingRuleId ? ( + {column.numberingRuleId} + ) : column.defaultValue ? ( + {column.defaultValue} + ) : ( + )} - {column.inputType !== "entity" && - column.inputType !== "code" && - column.inputType !== "numbering" && - (column.defaultValue ? ( - {column.defaultValue} - ) : ( - - ))}
- {/* 타입 뱃지 */} -
- + {/* 타입 뱃지 — 컴팩트 */} +
+ {typeConf.label}
- {/* PK / NN / IDX / UQ (클릭 토글) */} -
+ {/* PK / NN / IDX / UQ (클릭 토글) — 한 줄 nowrap */} +
); })} +
); })