✨ 새로운 기능 - 테이블 타입 관리에 테이블 복제 기능 추가 - 기존 테이블의 설정과 컬럼 정보를 복사하여 새 테이블 생성 - 최고 관리자만 사용 가능 (company_code = '*' AND userType = 'SUPER_ADMIN') - 테이블 1개 선택 시에만 복제 버튼 활성화 🎨 UI 개선 - 테이블 목록에 '테이블 복제' 버튼 추가 (Copy 아이콘) - CreateTableModal을 복제 모드로 재사용 - 복제 모드 시 제목/설명/버튼 텍스트 동적 변경 - 원본 테이블 정보 자동 로드 🔧 기술적 개선 - CreateTableModal에 mode/sourceTableName props 추가 - 복제 모드 감지 및 데이터 자동 로드 로직 구현 - API 타입 정의 수정 (ColumnListData 인터페이스 추가) - 백엔드 응답 구조와 프론트엔드 타입 일치화 🐛 버그 수정 - API 응답 구조 불일치 문제 해결 - ColumnListResponse 타입 수정 (배열 → 객체) - 데이터 파싱 로직 수정 (data.columns 접근) - 디버그 로그 추가로 문제 추적 개선 📝 변경된 파일 - frontend/app/(main)/admin/tableMng/page.tsx - frontend/components/admin/CreateTableModal.tsx - frontend/lib/api/tableManagement.ts - frontend/types/ddl.ts - 테이블_복제_기능_구현_계획서.md (신규) ✅ 테스트 완료 - 최고 관리자 권한 체크 - 테이블 정보 로드 - 컬럼 정보 복제 - 새 테이블명 입력 및 검증 - 테이블 생성 및 목록 갱신
341 lines
10 KiB
TypeScript
341 lines
10 KiB
TypeScript
/**
|
|
* 테이블 관리 API
|
|
* 테이블 컬럼 정보 조회 및 관리 기능
|
|
*/
|
|
|
|
import { apiClient, ApiResponse } from "./client";
|
|
|
|
// 컬럼 정보 타입 (백엔드와 일치)
|
|
export interface ColumnTypeInfo {
|
|
tableName?: string;
|
|
columnName: string;
|
|
displayName: string;
|
|
dataType: string;
|
|
dbType: string;
|
|
webType: string;
|
|
inputType?: "direct" | "auto";
|
|
detailSettings: string;
|
|
description?: string;
|
|
isNullable: string;
|
|
isPrimaryKey: boolean;
|
|
defaultValue?: string;
|
|
maxLength?: number;
|
|
numericPrecision?: number;
|
|
numericScale?: number;
|
|
codeCategory?: string;
|
|
codeValue?: string;
|
|
referenceTable?: string;
|
|
referenceColumn?: string;
|
|
displayColumn?: string;
|
|
displayOrder?: number;
|
|
isVisible?: boolean;
|
|
}
|
|
|
|
// 테이블 정보 타입
|
|
export interface TableInfo {
|
|
tableName: string;
|
|
displayName: string;
|
|
description: string;
|
|
columnCount: number;
|
|
}
|
|
|
|
// 컬럼 설정 타입
|
|
export interface ColumnSettings {
|
|
columnName?: string;
|
|
columnLabel: string;
|
|
webType: string;
|
|
detailSettings: string;
|
|
codeCategory: string;
|
|
codeValue: string;
|
|
referenceTable: string;
|
|
referenceColumn: string;
|
|
displayColumn?: string;
|
|
displayOrder?: number;
|
|
isVisible?: boolean;
|
|
}
|
|
|
|
// 컬럼 리스트 페이지네이션 응답
|
|
export interface ColumnListData {
|
|
columns: ColumnTypeInfo[];
|
|
total: number;
|
|
page: number;
|
|
size: number;
|
|
totalPages: number;
|
|
}
|
|
|
|
// API 응답 타입들
|
|
export interface TableListResponse extends ApiResponse<TableInfo[]> {}
|
|
export interface ColumnListResponse extends ApiResponse<ColumnListData> {}
|
|
export interface ColumnSettingsResponse extends ApiResponse<void> {}
|
|
|
|
/**
|
|
* 테이블 관리 API 클래스
|
|
*/
|
|
class TableManagementApi {
|
|
private readonly basePath = "/table-management";
|
|
|
|
/**
|
|
* 테이블 목록 조회
|
|
*/
|
|
async getTableList(): Promise<TableListResponse> {
|
|
try {
|
|
const response = await apiClient.get(`${this.basePath}/tables`);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error("❌ 테이블 목록 조회 실패:", error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "테이블 목록을 조회할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 특정 테이블의 컬럼 목록 조회
|
|
*/
|
|
async getColumnList(tableName: string, size: number = 1000): Promise<ColumnListResponse> {
|
|
try {
|
|
const response = await apiClient.get(`${this.basePath}/tables/${tableName}/columns?size=${size}`);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 테이블 '${tableName}' 컬럼 목록 조회 실패:`, error);
|
|
return {
|
|
success: false,
|
|
message:
|
|
error.response?.data?.message || error.message || `테이블 '${tableName}'의 컬럼 정보를 조회할 수 없습니다.`,
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 컬럼 타입 설정 저장
|
|
*/
|
|
async updateColumnSettings(
|
|
tableName: string,
|
|
columnName: string,
|
|
settings: ColumnSettings,
|
|
): Promise<ColumnSettingsResponse> {
|
|
try {
|
|
const response = await apiClient.put(`${this.basePath}/tables/${tableName}/columns/${columnName}`, settings);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 컬럼 '${tableName}.${columnName}' 설정 저장 실패:`, error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "컬럼 설정을 저장할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 여러 컬럼 설정 일괄 저장
|
|
*/
|
|
async updateMultipleColumnSettings(
|
|
tableName: string,
|
|
settingsArray: Array<{ columnName: string; settings: ColumnSettings }>,
|
|
): Promise<ColumnSettingsResponse> {
|
|
try {
|
|
const response = await apiClient.put(`${this.basePath}/tables/${tableName}/columns/batch`, {
|
|
settings: settingsArray,
|
|
});
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 테이블 '${tableName}' 컬럼 설정 일괄 저장 실패:`, error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "컬럼 설정을 일괄 저장할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 테이블 스키마 정보 조회 (컬럼 존재 여부 검증용)
|
|
*/
|
|
async getTableSchema(tableName: string): Promise<ApiResponse<ColumnTypeInfo[]>> {
|
|
try {
|
|
const response = await apiClient.get(`${this.basePath}/tables/${tableName}/schema`);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 테이블 '${tableName}' 스키마 조회 실패:`, error);
|
|
return {
|
|
success: false,
|
|
message:
|
|
error.response?.data?.message || error.message || `테이블 '${tableName}'의 스키마 정보를 조회할 수 없습니다.`,
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 테이블 존재 여부 확인
|
|
*/
|
|
async checkTableExists(tableName: string): Promise<ApiResponse<{ exists: boolean }>> {
|
|
try {
|
|
const response = await apiClient.get(`${this.basePath}/tables/${tableName}/exists`);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 테이블 '${tableName}' 존재 여부 확인 실패:`, error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "테이블 존재 여부를 확인할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 컬럼 웹타입 정보 조회 (화면관리 연동용)
|
|
*/
|
|
async getColumnWebTypes(tableName: string): Promise<ApiResponse<ColumnTypeInfo[]>> {
|
|
try {
|
|
const response = await apiClient.get(`${this.basePath}/tables/${tableName}/web-types`);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 테이블 '${tableName}' 웹타입 정보 조회 실패:`, error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "웹타입 정보를 조회할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 데이터베이스 연결 상태 확인
|
|
*/
|
|
async checkDatabaseConnection(): Promise<ApiResponse<{ connected: boolean; message: string }>> {
|
|
try {
|
|
const response = await apiClient.get(`${this.basePath}/health`);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error("❌ 데이터베이스 연결 상태 확인 실패:", error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "데이터베이스 연결 상태를 확인할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// 테이블 로그 시스템 API
|
|
// ========================================
|
|
|
|
/**
|
|
* 로그 테이블 생성
|
|
*/
|
|
async createLogTable(
|
|
tableName: string,
|
|
pkColumn: { columnName: string; dataType: string },
|
|
): Promise<ApiResponse<void>> {
|
|
try {
|
|
const response = await apiClient.post(`${this.basePath}/tables/${tableName}/log`, { pkColumn });
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 로그 테이블 생성 실패: ${tableName}`, error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "로그 테이블을 생성할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 로그 설정 조회
|
|
*/
|
|
async getLogConfig(tableName: string): Promise<
|
|
ApiResponse<{
|
|
originalTableName: string;
|
|
logTableName: string;
|
|
triggerName: string;
|
|
triggerFunctionName: string;
|
|
isActive: string;
|
|
createdAt: Date;
|
|
createdBy: string;
|
|
} | null>
|
|
> {
|
|
try {
|
|
const response = await apiClient.get(`${this.basePath}/tables/${tableName}/log/config`);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 로그 설정 조회 실패: ${tableName}`, error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "로그 설정을 조회할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 로그 데이터 조회
|
|
*/
|
|
async getLogData(
|
|
tableName: string,
|
|
options: {
|
|
page?: number;
|
|
size?: number;
|
|
operationType?: string;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
changedBy?: string;
|
|
originalId?: string;
|
|
} = {},
|
|
): Promise<
|
|
ApiResponse<{
|
|
data: any[];
|
|
total: number;
|
|
page: number;
|
|
size: number;
|
|
totalPages: number;
|
|
}>
|
|
> {
|
|
try {
|
|
const response = await apiClient.get(`${this.basePath}/tables/${tableName}/log`, {
|
|
params: options,
|
|
});
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 로그 데이터 조회 실패: ${tableName}`, error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "로그 데이터를 조회할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 로그 테이블 활성화/비활성화
|
|
*/
|
|
async toggleLogTable(tableName: string, isActive: boolean): Promise<ApiResponse<void>> {
|
|
try {
|
|
const response = await apiClient.post(`${this.basePath}/tables/${tableName}/log/toggle`, {
|
|
isActive,
|
|
});
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error(`❌ 로그 테이블 토글 실패: ${tableName}`, error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "로그 테이블 설정을 변경할 수 없습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
// 싱글톤 인스턴스 생성
|
|
export const tableManagementApi = new TableManagementApi();
|
|
|
|
// 편의 함수들
|
|
export const getTableColumns = (tableName: string) => tableManagementApi.getColumnList(tableName);
|
|
export const updateColumnType = (tableName: string, columnName: string, settings: ColumnSettings) =>
|
|
tableManagementApi.updateColumnSettings(tableName, columnName, settings);
|
|
export const checkTableExists = (tableName: string) => tableManagementApi.checkTableExists(tableName);
|