refactor: 코드 정리 및 불필요한 주석 제거

- EntityJoinController에서 중복 제거 설정 관련 주석 및 코드 삭제
- screenGroupController와 tableManagementController에서 AuthenticatedRequest 타입을 일반 Request로 변경
- 불필요한 로그 및 주석 제거로 코드 가독성 향상
- tableManagementController에서 에러 메시지 개선
This commit is contained in:
DDD1542
2026-01-15 17:36:38 +09:00
parent 9dc549be09
commit efa95af4b9
15 changed files with 1650 additions and 3070 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -365,7 +365,6 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
isInteractive={true}
formData={formData}
originalData={originalData || undefined}
initialData={(originalData && Object.keys(originalData).length > 0) ? originalData : formData} // 🆕 originalData가 있으면 사용, 없으면 formData 사용 (생성 모드에서 부모 데이터 전달)
onFormDataChange={handleFormDataChange}
screenId={screenInfo?.id}
tableName={screenInfo?.tableName}

View File

@@ -416,6 +416,10 @@ export function ScreenSettingModal({
<Database className="h-3 w-3" />
</TabsTrigger>
<TabsTrigger value="table-setting" className="gap-1 text-xs px-2" disabled={!mainTable}>
<Settings2 className="h-3 w-3" />
</TabsTrigger>
<TabsTrigger value="control-management" className="gap-1 text-xs px-2">
<Zap className="h-3 w-3" />
@@ -466,7 +470,22 @@ export function ScreenSettingModal({
/>
</TabsContent>
{/* 탭 2: 제어 관리 */}
{/* 탭 2: 테이블 설정 */}
<TabsContent value="table-setting" className="mt-0 min-h-0 flex-1 overflow-hidden p-0">
{mainTable && (
<TableSettingModal
isOpen={true}
onClose={() => {}} // 탭에서는 닫기 불필요
tableName={mainTable}
tableLabel={mainTableLabel}
screenId={currentScreenId}
onSaveSuccess={handleRefresh}
isEmbedded={true} // 임베드 모드
/>
)}
</TabsContent>
{/* 탭 3: 제어 관리 */}
<TabsContent value="control-management" className="mt-0 min-h-0 flex-1 overflow-auto p-3">
<ControlManagementTab
screenId={currentScreenId}
@@ -2198,17 +2217,6 @@ function OverviewTab({
<Database className="h-4 w-4 text-blue-500" />
</h3>
{mainTable && (
<Button
variant="outline"
size="sm"
className="h-7 gap-1 text-xs"
onClick={() => onOpenTableSetting?.(mainTable, mainTableLabel)}
>
<Settings2 className="h-3 w-3" />
</Button>
)}
</div>
{mainTable ? (
<TableColumnAccordion
@@ -3049,6 +3057,7 @@ interface ButtonControlInfo {
// 버튼 스타일
backgroundColor?: string;
textColor?: string;
borderRadius?: string;
// 모달/네비게이션 관련
modalScreenId?: number;
navigateScreenId?: number;
@@ -3215,6 +3224,7 @@ function ControlManagementTab({
// 버튼 스타일 (webTypeConfig 우선)
backgroundColor: webTypeConfig.backgroundColor || config.backgroundColor || style.backgroundColor,
textColor: webTypeConfig.textColor || config.textColor || style.color || style.labelColor,
borderRadius: webTypeConfig.borderRadius || config.borderRadius || style.borderRadius,
// 모달/네비게이션 관련 (화면 디자이너는 targetScreenId 사용)
modalScreenId: action.targetScreenId || action.modalScreenId,
navigateScreenId: action.navigateScreenId || action.targetScreenId,
@@ -3527,6 +3537,11 @@ function ControlManagementTab({
comp.style.color = values.textColor;
comp.style.labelColor = values.textColor;
}
if (values.borderRadius !== undefined) {
comp.webTypeConfig.borderRadius = values.borderRadius;
comp.componentConfig.borderRadius = values.borderRadius;
comp.style.borderRadius = values.borderRadius;
}
// 액션 타입 업데이트
if (values.actionType) {
@@ -3735,6 +3750,7 @@ function ControlManagementTab({
const currentLabel = editedValues[btn.id]?.label ?? btn.label;
const currentBgColor = editedValues[btn.id]?.backgroundColor ?? btn.backgroundColor ?? "#3b82f6";
const currentTextColor = editedValues[btn.id]?.textColor ?? btn.textColor ?? "#ffffff";
const currentBorderRadius = editedValues[btn.id]?.borderRadius ?? btn.borderRadius ?? "4px";
return (
<div key={btn.id} className="py-3 px-1">
@@ -3742,10 +3758,11 @@ function ControlManagementTab({
<div className="flex items-center gap-3 mb-3">
{/* 버튼 프리뷰 */}
<div
className="flex items-center justify-center px-3 py-1.5 rounded text-xs font-medium min-w-[60px] shrink-0"
className="flex items-center justify-center px-3 py-1.5 text-xs font-medium min-w-[60px] shrink-0"
style={{
backgroundColor: currentBgColor,
color: currentTextColor,
borderRadius: currentBorderRadius,
}}
>
{currentLabel || "버튼"}
@@ -3870,6 +3887,34 @@ function ControlManagementTab({
</div>
</div>
{/* 버튼 모서리 (borderRadius) */}
<div className="grid grid-cols-[80px_1fr] items-center gap-2">
<Label className="text-xs text-muted-foreground"></Label>
<div className="flex items-center gap-2">
<Select
value={editedValues[btn.id]?.borderRadius ?? btn.borderRadius ?? "4px"}
onValueChange={(val) => setEditedValues(prev => ({
...prev,
[btn.id]: { ...prev[btn.id], borderRadius: val }
}))}
>
<SelectTrigger className="h-7 w-[100px] text-xs">
<SelectValue placeholder="선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="0px" className="text-xs"> (0px)</SelectItem>
<SelectItem value="2px" className="text-xs"> (2px)</SelectItem>
<SelectItem value="4px" className="text-xs"> (4px)</SelectItem>
<SelectItem value="6px" className="text-xs"> (6px)</SelectItem>
<SelectItem value="8px" className="text-xs"> (8px)</SelectItem>
<SelectItem value="12px" className="text-xs"> (12px)</SelectItem>
<SelectItem value="9999px" className="text-xs"></SelectItem>
</SelectContent>
</Select>
<span className="text-[10px] text-muted-foreground"> </span>
</div>
</div>
{/* 확인 메시지 설정 (save/delete 액션에서만 표시) */}
{((editedValues[btn.id]?.actionType || btn.actionType) === "save" ||
(editedValues[btn.id]?.actionType || btn.actionType) === "delete") && (

View File

@@ -129,6 +129,7 @@ interface TableSettingModalProps {
columns?: ColumnInfo[];
filterColumns?: string[];
onSaveSuccess?: () => void;
isEmbedded?: boolean; // 탭 안에 임베드 모드로 표시
}
// 검색 가능한 Select 컴포넌트
@@ -256,6 +257,7 @@ export function TableSettingModal({
columns = [],
filterColumns = [],
onSaveSuccess,
isEmbedded = false,
}: TableSettingModalProps) {
const [activeTab, setActiveTab] = useState("columns");
const [loading, setLoading] = useState(false);
@@ -304,9 +306,19 @@ export function TableSettingModal({
// 초기 편집 상태 설정
const initialEdits: Record<string, Partial<ColumnTypeInfo>> = {};
columnsData.forEach((col) => {
// referenceTable이 설정되어 있으면 inputType은 entity여야 함
let effectiveInputType = col.inputType || "direct";
if (col.referenceTable && effectiveInputType !== "entity") {
effectiveInputType = "entity";
}
// codeCategory/codeValue가 설정되어 있으면 inputType은 code여야 함
if (col.codeCategory && effectiveInputType !== "code") {
effectiveInputType = "code";
}
initialEdits[col.columnName] = {
displayName: col.displayName,
inputType: col.inputType || "direct",
inputType: effectiveInputType,
referenceTable: col.referenceTable,
referenceColumn: col.referenceColumn,
displayColumn: col.displayColumn,
@@ -343,10 +355,10 @@ export function TableSettingModal({
try {
// 모든 화면 조회
const screensResponse = await screenApi.getScreens({ size: 1000 });
if (screensResponse.items) {
if (screensResponse.data) {
const usingScreens: ScreenUsingTable[] = [];
screensResponse.items.forEach((screen: any) => {
screensResponse.data.forEach((screen: any) => {
// 메인 테이블로 사용하는 경우
if (screen.tableName === tableName) {
usingScreens.push({
@@ -418,6 +430,35 @@ export function TableSettingModal({
},
}));
// 입력 타입 변경 시 관련 필드 초기화
if (field === "inputType") {
// 엔티티가 아닌 다른 타입으로 변경하면 참조 설정 초기화
if (value !== "entity") {
setEditedColumns((prev) => ({
...prev,
[columnName]: {
...prev[columnName],
inputType: value,
referenceTable: "",
referenceColumn: "",
displayColumn: "",
},
}));
}
// 코드가 아닌 다른 타입으로 변경하면 코드 설정 초기화
if (value !== "code") {
setEditedColumns((prev) => ({
...prev,
[columnName]: {
...prev[columnName],
inputType: value,
codeCategory: "",
codeValue: "",
},
}));
}
}
// 참조 테이블 변경 시 참조 컬럼 초기화
if (field === "referenceTable") {
setEditedColumns((prev) => ({
@@ -452,8 +493,18 @@ export function TableSettingModal({
// detailSettings 처리 (Entity 타입인 경우)
let finalDetailSettings = mergedColumn.detailSettings || "";
// referenceTable이 설정되어 있으면 inputType을 entity로 자동 설정
let currentInputType = (mergedColumn.inputType || "") as string;
if (mergedColumn.referenceTable && currentInputType !== "entity") {
currentInputType = "entity";
}
// codeCategory가 설정되어 있으면 inputType을 code로 자동 설정
if (mergedColumn.codeCategory && currentInputType !== "code") {
currentInputType = "code";
}
if (mergedColumn.inputType === "entity" && mergedColumn.referenceTable) {
if (currentInputType === "entity" && mergedColumn.referenceTable) {
// 기존 detailSettings를 파싱하거나 새로 생성
let existingSettings: Record<string, unknown> = {};
if (typeof mergedColumn.detailSettings === "string" && mergedColumn.detailSettings.trim().startsWith("{")) {
@@ -479,7 +530,7 @@ export function TableSettingModal({
}
// Code 타입인 경우 hierarchyRole을 detailSettings에 포함
if (mergedColumn.inputType === "code" && (mergedColumn as any).hierarchyRole) {
if (currentInputType === "code" && (mergedColumn as any).hierarchyRole) {
let existingSettings: Record<string, unknown> = {};
if (typeof finalDetailSettings === "string" && finalDetailSettings.trim().startsWith("{")) {
try {
@@ -502,7 +553,7 @@ export function TableSettingModal({
const columnSetting: ColumnSettings = {
columnName: columnName,
columnLabel: mergedColumn.displayName || originalColumn.displayName || "",
webType: mergedColumn.inputType || originalColumn.inputType || "text",
inputType: currentInputType || "text", // referenceTable/codeCategory가 설정된 경우 자동 보정된 값 사용
detailSettings: finalDetailSettings,
codeCategory: mergedColumn.codeCategory || originalColumn.codeCategory || "",
codeValue: mergedColumn.codeValue || originalColumn.codeValue || "",
@@ -593,6 +644,158 @@ export function TableSettingModal({
];
};
// 임베드 모드
if (isEmbedded) {
return (
<>
<div className="flex h-full flex-col">
{/* 헤더 */}
<div className="flex flex-shrink-0 items-center justify-between border-b pb-2 px-3 pt-2">
<div className="flex items-center gap-2">
<Table2 className="h-4 w-4 text-green-500" />
<span className="text-sm font-medium">{tableLabel || tableName}</span>
{tableName !== tableLabel && tableName !== (tableLabel || tableName) && (
<span className="text-xs text-muted-foreground">({tableName})</span>
)}
</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setShowTableManagementModal(true)}
className="h-7 gap-1 text-xs"
>
<Settings className="h-3 w-3" />
</Button>
<Button
variant="outline"
size="sm"
onClick={handleRefresh}
className="h-7 w-7 p-0"
disabled={loading}
>
<RefreshCw className={cn("h-3 w-3", loading && "animate-spin")} />
</Button>
<Button
size="sm"
onClick={handleSaveAll}
className="h-7 gap-1 text-xs"
disabled={saving || loading}
>
{saving ? (
<Loader2 className="h-3 w-3 animate-spin" />
) : (
<Save className="h-3 w-3" />
)}
</Button>
</div>
</div>
<div className="flex min-h-0 flex-1 gap-3 p-3">
{/* 좌측: 탭 (40%) */}
<div className="flex w-[40%] min-h-0 flex-col">
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="flex min-h-0 flex-1 flex-col"
>
<TabsList className="h-8 flex-shrink-0">
<TabsTrigger value="columns" className="gap-1 text-xs">
<Columns3 className="h-3 w-3" />
</TabsTrigger>
<TabsTrigger value="screens" className="gap-1 text-xs">
<Monitor className="h-3 w-3" />
</TabsTrigger>
<TabsTrigger value="references" className="gap-1 text-xs">
<Eye className="h-3 w-3" />
</TabsTrigger>
</TabsList>
<TabsContent value="columns" className="mt-2 min-h-0 flex-1 overflow-hidden">
<ColumnListTab
columns={tableColumns.map((col) => ({
...col,
isPK: col.columnName === "id" || col.columnName.endsWith("_id"),
isFK: (col.inputType as string) === "entity",
}))}
editedColumns={editedColumns}
selectedColumn={selectedColumn}
onSelectColumn={setSelectedColumn}
loading={loading}
/>
</TabsContent>
<TabsContent value="screens" className="mt-2 min-h-0 flex-1 overflow-hidden">
<ScreensTab screensUsingTable={screensUsingTable} loading={loading} />
</TabsContent>
<TabsContent value="references" className="mt-2 min-h-0 flex-1 overflow-hidden">
<ReferenceTab
tableName={tableName}
tableLabel={tableLabel}
referencedBy={referencedBy}
joinColumnRefs={joinColumnRefs}
loading={loading}
/>
</TabsContent>
</Tabs>
</div>
{/* 우측: 상세 설정 (60%) */}
<div className="flex w-[60%] min-h-0 flex-col rounded-lg border bg-muted/30 p-3">
{selectedColumn && mergedColumns.find((c) => c.columnName === selectedColumn) ? (
<ColumnDetailPanel
columnInfo={mergedColumns.find((c) => c.columnName === selectedColumn)!}
editedColumn={editedColumns[selectedColumn] || {}}
tableOptions={tableOptions}
inputTypeOptions={inputTypeOptions}
getRefColumnOptions={getRefColumnOptions}
loadingRefColumns={loadingRefColumns}
onColumnChange={(field, value) => handleColumnChange(selectedColumn, field, value)}
/>
) : (
<div className="flex h-full items-center justify-center text-sm text-muted-foreground">
<div className="text-center">
<Columns3 className="mx-auto h-12 w-12 text-muted-foreground/30" />
<p className="mt-2"> </p>
<p> .</p>
</div>
</div>
)}
</div>
</div>
</div>
{/* 테이블 타입 관리 모달 */}
<Dialog open={showTableManagementModal} onOpenChange={setShowTableManagementModal}>
<DialogContent className="flex h-[90vh] max-h-[1000px] w-[95vw] max-w-[1400px] flex-col p-0">
<div className="flex items-center justify-between border-b p-4">
<h2 className="text-lg font-semibold"> </h2>
<Button
variant="ghost"
size="sm"
onClick={() => {
setShowTableManagementModal(false);
loadTableData();
}}
>
<X className="h-4 w-4" />
</Button>
</div>
<div className="flex-1 overflow-hidden">
<TableManagementPage />
</div>
</DialogContent>
</Dialog>
</>
);
}
// 기존 모달 모드
return (
<>
<Dialog open={isOpen} onOpenChange={onClose}>
@@ -843,6 +1046,7 @@ function ColumnListTab({
<div className="space-y-1 px-3 pb-3">
{filteredColumns.map((col) => {
const edited = editedColumns[col.columnName] || {};
// editedColumns에서 inputType을 가져옴 (초기화 시 이미 보정됨)
const inputType = (edited.inputType || col.inputType || "text") as string;
const isSelected = selectedColumn === col.columnName;
@@ -873,23 +1077,17 @@ function ColumnListTab({
PK
</Badge>
)}
{col.isFK && (
<Badge variant="outline" className="bg-green-100 text-green-700 text-[10px] px-1.5">
<Link2 className="mr-0.5 h-2.5 w-2.5" />
FK
</Badge>
)}
{(edited.referenceTable || col.referenceTable) && (
{/* 엔티티 타입이거나 referenceTable이 설정되어 있으면 조인 배지 표시 (FK와 동일 의미) */}
{(inputType === "entity" || edited.referenceTable || col.referenceTable) && (
<Badge variant="outline" className="bg-blue-100 text-blue-700 text-[10px] px-1.5">
<Link2 className="mr-0.5 h-2.5 w-2.5" />
</Badge>
)}
</div>
</div>
<div className="mt-1 flex items-center gap-2 text-xs text-muted-foreground">
<div className="mt-1 text-xs text-muted-foreground">
<span className="font-mono">{col.columnName}</span>
<span></span>
<span>{col.dataType}</span>
</div>
</div>
);
@@ -925,10 +1123,11 @@ function ColumnDetailPanel({
onColumnChange,
}: ColumnDetailPanelProps) {
const currentLabel = editedColumn.displayName ?? columnInfo.displayName ?? "";
const currentInputType = (editedColumn.inputType ?? columnInfo.inputType ?? "text") as string;
const currentRefTable = editedColumn.referenceTable ?? columnInfo.referenceTable ?? "";
const currentRefColumn = editedColumn.referenceColumn ?? columnInfo.referenceColumn ?? "";
const currentDisplayColumn = editedColumn.displayColumn ?? columnInfo.displayColumn ?? "";
// editedColumn에서 inputType을 가져옴 (초기화 시 이미 보정됨)
const currentInputType = (editedColumn.inputType ?? columnInfo.inputType ?? "text") as string;
return (
<div className="flex h-full flex-col">
@@ -948,9 +1147,10 @@ function ColumnDetailPanel({
Primary Key
</Badge>
)}
{columnInfo.isFK && (
<Badge variant="outline" className="bg-green-100 text-green-700 text-[10px]">
Foreign Key
{/* 엔티티 타입이거나 referenceTable이 있으면 조인 배지 표시 */}
{(currentInputType === "entity" || currentRefTable) && (
<Badge variant="outline" className="bg-blue-100 text-blue-700 text-[10px]">
</Badge>
)}
</div>