626 lines
16 KiB
TypeScript
626 lines
16 KiB
TypeScript
|
|
/**
|
||
|
|
* DDL 실행 서비스
|
||
|
|
* 실제 PostgreSQL 테이블 및 컬럼 생성을 담당
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { PrismaClient } from "@prisma/client";
|
||
|
|
import {
|
||
|
|
CreateColumnDefinition,
|
||
|
|
DDLExecutionResult,
|
||
|
|
WEB_TYPE_TO_POSTGRES_MAP,
|
||
|
|
WebType,
|
||
|
|
} from "../types/ddl";
|
||
|
|
import { DDLSafetyValidator } from "./ddlSafetyValidator";
|
||
|
|
import { DDLAuditLogger } from "./ddlAuditLogger";
|
||
|
|
import { logger } from "../utils/logger";
|
||
|
|
import { cache, CacheKeys } from "../utils/cache";
|
||
|
|
|
||
|
|
const prisma = new PrismaClient();
|
||
|
|
|
||
|
|
export class DDLExecutionService {
|
||
|
|
/**
|
||
|
|
* 새 테이블 생성
|
||
|
|
*/
|
||
|
|
async createTable(
|
||
|
|
tableName: string,
|
||
|
|
columns: CreateColumnDefinition[],
|
||
|
|
userCompanyCode: string,
|
||
|
|
userId: string,
|
||
|
|
description?: string
|
||
|
|
): Promise<DDLExecutionResult> {
|
||
|
|
// DDL 실행 시작 로그
|
||
|
|
await DDLAuditLogger.logDDLStart(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"CREATE_TABLE",
|
||
|
|
tableName,
|
||
|
|
{ columns, description }
|
||
|
|
);
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 1. 권한 검증
|
||
|
|
this.validateSuperAdminPermission(userCompanyCode);
|
||
|
|
|
||
|
|
// 2. 안전성 검증
|
||
|
|
const validation = DDLSafetyValidator.validateTableCreation(
|
||
|
|
tableName,
|
||
|
|
columns
|
||
|
|
);
|
||
|
|
if (!validation.isValid) {
|
||
|
|
const errorMessage = `테이블 생성 검증 실패: ${validation.errors.join(", ")}`;
|
||
|
|
|
||
|
|
await DDLAuditLogger.logDDLExecution(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"CREATE_TABLE",
|
||
|
|
tableName,
|
||
|
|
"VALIDATION_FAILED",
|
||
|
|
false,
|
||
|
|
errorMessage
|
||
|
|
);
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: errorMessage,
|
||
|
|
error: {
|
||
|
|
code: "VALIDATION_FAILED",
|
||
|
|
details: validation.errors.join(", "),
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. 테이블 존재 여부 확인
|
||
|
|
const tableExists = await this.checkTableExists(tableName);
|
||
|
|
if (tableExists) {
|
||
|
|
const errorMessage = `테이블 '${tableName}'이 이미 존재합니다.`;
|
||
|
|
|
||
|
|
await DDLAuditLogger.logDDLExecution(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"CREATE_TABLE",
|
||
|
|
tableName,
|
||
|
|
"TABLE_EXISTS",
|
||
|
|
false,
|
||
|
|
errorMessage
|
||
|
|
);
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: errorMessage,
|
||
|
|
error: {
|
||
|
|
code: "TABLE_EXISTS",
|
||
|
|
details: errorMessage,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. DDL 쿼리 생성
|
||
|
|
const ddlQuery = this.generateCreateTableQuery(tableName, columns);
|
||
|
|
|
||
|
|
// 5. 트랜잭션으로 안전하게 실행
|
||
|
|
await prisma.$transaction(async (tx) => {
|
||
|
|
// 5-1. 테이블 생성
|
||
|
|
await tx.$executeRawUnsafe(ddlQuery);
|
||
|
|
|
||
|
|
// 5-2. 테이블 메타데이터 저장
|
||
|
|
await this.saveTableMetadata(tx, tableName, description);
|
||
|
|
|
||
|
|
// 5-3. 컬럼 메타데이터 저장
|
||
|
|
await this.saveColumnMetadata(tx, tableName, columns);
|
||
|
|
});
|
||
|
|
|
||
|
|
// 6. 성공 로그 기록
|
||
|
|
await DDLAuditLogger.logDDLExecution(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"CREATE_TABLE",
|
||
|
|
tableName,
|
||
|
|
ddlQuery,
|
||
|
|
true
|
||
|
|
);
|
||
|
|
|
||
|
|
logger.info("테이블 생성 성공", {
|
||
|
|
tableName,
|
||
|
|
userId,
|
||
|
|
columnCount: columns.length,
|
||
|
|
});
|
||
|
|
|
||
|
|
// 테이블 생성 후 관련 캐시 무효화
|
||
|
|
this.invalidateTableCache(tableName);
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
message: `테이블 '${tableName}'이 성공적으로 생성되었습니다.`,
|
||
|
|
executedQuery: ddlQuery,
|
||
|
|
};
|
||
|
|
} catch (error) {
|
||
|
|
const errorMessage = `테이블 생성 실패: ${(error as Error).message}`;
|
||
|
|
|
||
|
|
// 실패 로그 기록
|
||
|
|
await DDLAuditLogger.logDDLExecution(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"CREATE_TABLE",
|
||
|
|
tableName,
|
||
|
|
`FAILED: ${(error as Error).message}`,
|
||
|
|
false,
|
||
|
|
errorMessage
|
||
|
|
);
|
||
|
|
|
||
|
|
logger.error("테이블 생성 실패:", {
|
||
|
|
tableName,
|
||
|
|
userId,
|
||
|
|
error: (error as Error).message,
|
||
|
|
stack: (error as Error).stack,
|
||
|
|
});
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: errorMessage,
|
||
|
|
error: {
|
||
|
|
code: "EXECUTION_FAILED",
|
||
|
|
details: (error as Error).message,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 기존 테이블에 컬럼 추가
|
||
|
|
*/
|
||
|
|
async addColumn(
|
||
|
|
tableName: string,
|
||
|
|
column: CreateColumnDefinition,
|
||
|
|
userCompanyCode: string,
|
||
|
|
userId: string
|
||
|
|
): Promise<DDLExecutionResult> {
|
||
|
|
// DDL 실행 시작 로그
|
||
|
|
await DDLAuditLogger.logDDLStart(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"ADD_COLUMN",
|
||
|
|
tableName,
|
||
|
|
{ column }
|
||
|
|
);
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 1. 권한 검증
|
||
|
|
this.validateSuperAdminPermission(userCompanyCode);
|
||
|
|
|
||
|
|
// 2. 안전성 검증
|
||
|
|
const validation = DDLSafetyValidator.validateColumnAddition(
|
||
|
|
tableName,
|
||
|
|
column
|
||
|
|
);
|
||
|
|
if (!validation.isValid) {
|
||
|
|
const errorMessage = `컬럼 추가 검증 실패: ${validation.errors.join(", ")}`;
|
||
|
|
|
||
|
|
await DDLAuditLogger.logDDLExecution(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"ADD_COLUMN",
|
||
|
|
tableName,
|
||
|
|
"VALIDATION_FAILED",
|
||
|
|
false,
|
||
|
|
errorMessage
|
||
|
|
);
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: errorMessage,
|
||
|
|
error: {
|
||
|
|
code: "VALIDATION_FAILED",
|
||
|
|
details: validation.errors.join(", "),
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. 테이블 존재 여부 확인
|
||
|
|
const tableExists = await this.checkTableExists(tableName);
|
||
|
|
if (!tableExists) {
|
||
|
|
const errorMessage = `테이블 '${tableName}'이 존재하지 않습니다.`;
|
||
|
|
|
||
|
|
await DDLAuditLogger.logDDLExecution(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"ADD_COLUMN",
|
||
|
|
tableName,
|
||
|
|
"TABLE_NOT_EXISTS",
|
||
|
|
false,
|
||
|
|
errorMessage
|
||
|
|
);
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: errorMessage,
|
||
|
|
error: {
|
||
|
|
code: "TABLE_NOT_EXISTS",
|
||
|
|
details: errorMessage,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. 컬럼 존재 여부 확인
|
||
|
|
const columnExists = await this.checkColumnExists(tableName, column.name);
|
||
|
|
if (columnExists) {
|
||
|
|
const errorMessage = `컬럼 '${column.name}'이 이미 존재합니다.`;
|
||
|
|
|
||
|
|
await DDLAuditLogger.logDDLExecution(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"ADD_COLUMN",
|
||
|
|
tableName,
|
||
|
|
"COLUMN_EXISTS",
|
||
|
|
false,
|
||
|
|
errorMessage
|
||
|
|
);
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: errorMessage,
|
||
|
|
error: {
|
||
|
|
code: "COLUMN_EXISTS",
|
||
|
|
details: errorMessage,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 5. DDL 쿼리 생성
|
||
|
|
const ddlQuery = this.generateAddColumnQuery(tableName, column);
|
||
|
|
|
||
|
|
// 6. 트랜잭션으로 안전하게 실행
|
||
|
|
await prisma.$transaction(async (tx) => {
|
||
|
|
// 6-1. 컬럼 추가
|
||
|
|
await tx.$executeRawUnsafe(ddlQuery);
|
||
|
|
|
||
|
|
// 6-2. 컬럼 메타데이터 저장
|
||
|
|
await this.saveColumnMetadata(tx, tableName, [column]);
|
||
|
|
});
|
||
|
|
|
||
|
|
// 7. 성공 로그 기록
|
||
|
|
await DDLAuditLogger.logDDLExecution(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"ADD_COLUMN",
|
||
|
|
tableName,
|
||
|
|
ddlQuery,
|
||
|
|
true
|
||
|
|
);
|
||
|
|
|
||
|
|
logger.info("컬럼 추가 성공", {
|
||
|
|
tableName,
|
||
|
|
columnName: column.name,
|
||
|
|
webType: column.webType,
|
||
|
|
userId,
|
||
|
|
});
|
||
|
|
|
||
|
|
// 컬럼 추가 후 관련 캐시 무효화
|
||
|
|
this.invalidateTableCache(tableName);
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
message: `컬럼 '${column.name}'이 성공적으로 추가되었습니다.`,
|
||
|
|
executedQuery: ddlQuery,
|
||
|
|
};
|
||
|
|
} catch (error) {
|
||
|
|
const errorMessage = `컬럼 추가 실패: ${(error as Error).message}`;
|
||
|
|
|
||
|
|
// 실패 로그 기록
|
||
|
|
await DDLAuditLogger.logDDLExecution(
|
||
|
|
userId,
|
||
|
|
userCompanyCode,
|
||
|
|
"ADD_COLUMN",
|
||
|
|
tableName,
|
||
|
|
`FAILED: ${(error as Error).message}`,
|
||
|
|
false,
|
||
|
|
errorMessage
|
||
|
|
);
|
||
|
|
|
||
|
|
logger.error("컬럼 추가 실패:", {
|
||
|
|
tableName,
|
||
|
|
columnName: column.name,
|
||
|
|
userId,
|
||
|
|
error: (error as Error).message,
|
||
|
|
stack: (error as Error).stack,
|
||
|
|
});
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: errorMessage,
|
||
|
|
error: {
|
||
|
|
code: "EXECUTION_FAILED",
|
||
|
|
details: (error as Error).message,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* CREATE TABLE DDL 쿼리 생성
|
||
|
|
*/
|
||
|
|
private generateCreateTableQuery(
|
||
|
|
tableName: string,
|
||
|
|
columns: CreateColumnDefinition[]
|
||
|
|
): string {
|
||
|
|
// 사용자 정의 컬럼들
|
||
|
|
const columnDefinitions = columns
|
||
|
|
.map((col) => {
|
||
|
|
const postgresType = this.mapWebTypeToPostgresType(
|
||
|
|
col.webType,
|
||
|
|
col.length
|
||
|
|
);
|
||
|
|
let definition = `"${col.name}" ${postgresType}`;
|
||
|
|
|
||
|
|
if (!col.nullable) {
|
||
|
|
definition += " NOT NULL";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (col.defaultValue) {
|
||
|
|
definition += ` DEFAULT '${col.defaultValue}'`;
|
||
|
|
}
|
||
|
|
|
||
|
|
return definition;
|
||
|
|
})
|
||
|
|
.join(",\n ");
|
||
|
|
|
||
|
|
// 기본 컬럼들 (시스템 필수 컬럼)
|
||
|
|
const baseColumns = `
|
||
|
|
"id" serial PRIMARY KEY,
|
||
|
|
"created_date" timestamp DEFAULT now(),
|
||
|
|
"updated_date" timestamp DEFAULT now(),
|
||
|
|
"writer" varchar(100),
|
||
|
|
"company_code" varchar(50) DEFAULT '*'`;
|
||
|
|
|
||
|
|
// 최종 CREATE TABLE 쿼리
|
||
|
|
return `
|
||
|
|
CREATE TABLE "${tableName}" (${baseColumns},
|
||
|
|
${columnDefinitions}
|
||
|
|
);`.trim();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ALTER TABLE ADD COLUMN DDL 쿼리 생성
|
||
|
|
*/
|
||
|
|
private generateAddColumnQuery(
|
||
|
|
tableName: string,
|
||
|
|
column: CreateColumnDefinition
|
||
|
|
): string {
|
||
|
|
const postgresType = this.mapWebTypeToPostgresType(
|
||
|
|
column.webType,
|
||
|
|
column.length
|
||
|
|
);
|
||
|
|
let definition = `"${column.name}" ${postgresType}`;
|
||
|
|
|
||
|
|
if (!column.nullable) {
|
||
|
|
definition += " NOT NULL";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (column.defaultValue) {
|
||
|
|
definition += ` DEFAULT '${column.defaultValue}'`;
|
||
|
|
}
|
||
|
|
|
||
|
|
return `ALTER TABLE "${tableName}" ADD COLUMN ${definition};`;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 웹타입을 PostgreSQL 타입으로 매핑
|
||
|
|
*/
|
||
|
|
private mapWebTypeToPostgresType(webType: WebType, length?: number): string {
|
||
|
|
const mapping = WEB_TYPE_TO_POSTGRES_MAP[webType];
|
||
|
|
|
||
|
|
if (!mapping) {
|
||
|
|
logger.warn(`알 수 없는 웹타입: ${webType}, text로 대체`);
|
||
|
|
return "text";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mapping.supportsLength && length && length > 0) {
|
||
|
|
if (mapping.postgresType === "varchar") {
|
||
|
|
return `varchar(${length})`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return mapping.postgresType;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 테이블 메타데이터 저장
|
||
|
|
*/
|
||
|
|
private async saveTableMetadata(
|
||
|
|
tx: any,
|
||
|
|
tableName: string,
|
||
|
|
description?: string
|
||
|
|
): Promise<void> {
|
||
|
|
await tx.table_labels.upsert({
|
||
|
|
where: { table_name: tableName },
|
||
|
|
update: {
|
||
|
|
table_label: tableName,
|
||
|
|
description: description || `사용자 생성 테이블: ${tableName}`,
|
||
|
|
updated_date: new Date(),
|
||
|
|
},
|
||
|
|
create: {
|
||
|
|
table_name: tableName,
|
||
|
|
table_label: tableName,
|
||
|
|
description: description || `사용자 생성 테이블: ${tableName}`,
|
||
|
|
created_date: new Date(),
|
||
|
|
updated_date: new Date(),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 컬럼 메타데이터 저장
|
||
|
|
*/
|
||
|
|
private async saveColumnMetadata(
|
||
|
|
tx: any,
|
||
|
|
tableName: string,
|
||
|
|
columns: CreateColumnDefinition[]
|
||
|
|
): Promise<void> {
|
||
|
|
// 먼저 table_labels에 테이블 정보가 있는지 확인하고 없으면 생성
|
||
|
|
await tx.table_labels.upsert({
|
||
|
|
where: {
|
||
|
|
table_name: tableName,
|
||
|
|
},
|
||
|
|
update: {
|
||
|
|
updated_date: new Date(),
|
||
|
|
},
|
||
|
|
create: {
|
||
|
|
table_name: tableName,
|
||
|
|
table_label: tableName,
|
||
|
|
description: `자동 생성된 테이블 메타데이터: ${tableName}`,
|
||
|
|
created_date: new Date(),
|
||
|
|
updated_date: new Date(),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
for (const column of columns) {
|
||
|
|
await tx.column_labels.upsert({
|
||
|
|
where: {
|
||
|
|
table_name_column_name: {
|
||
|
|
table_name: tableName,
|
||
|
|
column_name: column.name,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
update: {
|
||
|
|
column_label: column.label || column.name,
|
||
|
|
web_type: column.webType,
|
||
|
|
detail_settings: JSON.stringify(column.detailSettings || {}),
|
||
|
|
description: column.description,
|
||
|
|
display_order: column.order || 0,
|
||
|
|
is_visible: true,
|
||
|
|
updated_date: new Date(),
|
||
|
|
},
|
||
|
|
create: {
|
||
|
|
table_name: tableName,
|
||
|
|
column_name: column.name,
|
||
|
|
column_label: column.label || column.name,
|
||
|
|
web_type: column.webType,
|
||
|
|
detail_settings: JSON.stringify(column.detailSettings || {}),
|
||
|
|
description: column.description,
|
||
|
|
display_order: column.order || 0,
|
||
|
|
is_visible: true,
|
||
|
|
created_date: new Date(),
|
||
|
|
updated_date: new Date(),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 권한 검증 (슈퍼관리자 확인)
|
||
|
|
*/
|
||
|
|
private validateSuperAdminPermission(userCompanyCode: string): void {
|
||
|
|
if (userCompanyCode !== "*") {
|
||
|
|
throw new Error("최고 관리자 권한이 필요합니다.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 테이블 존재 여부 확인
|
||
|
|
*/
|
||
|
|
private async checkTableExists(tableName: string): Promise<boolean> {
|
||
|
|
try {
|
||
|
|
const result = await prisma.$queryRawUnsafe(
|
||
|
|
`
|
||
|
|
SELECT EXISTS (
|
||
|
|
SELECT FROM information_schema.tables
|
||
|
|
WHERE table_schema = 'public'
|
||
|
|
AND table_name = $1
|
||
|
|
);
|
||
|
|
`,
|
||
|
|
tableName
|
||
|
|
);
|
||
|
|
|
||
|
|
return (result as any)[0]?.exists || false;
|
||
|
|
} catch (error) {
|
||
|
|
logger.error("테이블 존재 확인 오류:", error);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 컬럼 존재 여부 확인
|
||
|
|
*/
|
||
|
|
private async checkColumnExists(
|
||
|
|
tableName: string,
|
||
|
|
columnName: string
|
||
|
|
): Promise<boolean> {
|
||
|
|
try {
|
||
|
|
const result = await prisma.$queryRawUnsafe(
|
||
|
|
`
|
||
|
|
SELECT EXISTS (
|
||
|
|
SELECT FROM information_schema.columns
|
||
|
|
WHERE table_schema = 'public'
|
||
|
|
AND table_name = $1
|
||
|
|
AND column_name = $2
|
||
|
|
);
|
||
|
|
`,
|
||
|
|
tableName,
|
||
|
|
columnName
|
||
|
|
);
|
||
|
|
|
||
|
|
return (result as any)[0]?.exists || false;
|
||
|
|
} catch (error) {
|
||
|
|
logger.error("컬럼 존재 확인 오류:", error);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 생성된 테이블 정보 조회
|
||
|
|
*/
|
||
|
|
async getCreatedTableInfo(tableName: string): Promise<{
|
||
|
|
tableInfo: any;
|
||
|
|
columns: any[];
|
||
|
|
} | null> {
|
||
|
|
try {
|
||
|
|
// 테이블 정보 조회
|
||
|
|
const tableInfo = await prisma.table_labels.findUnique({
|
||
|
|
where: { table_name: tableName },
|
||
|
|
});
|
||
|
|
|
||
|
|
// 컬럼 정보 조회
|
||
|
|
const columns = await prisma.column_labels.findMany({
|
||
|
|
where: { table_name: tableName },
|
||
|
|
orderBy: { display_order: "asc" },
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!tableInfo) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
tableInfo,
|
||
|
|
columns,
|
||
|
|
};
|
||
|
|
} catch (error) {
|
||
|
|
logger.error("생성된 테이블 정보 조회 실패:", error);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 테이블 관련 캐시 무효화
|
||
|
|
* DDL 작업 후 호출하여 캐시된 데이터를 클리어
|
||
|
|
*/
|
||
|
|
private invalidateTableCache(tableName: string): void {
|
||
|
|
try {
|
||
|
|
// 테이블 컬럼 관련 캐시 무효화
|
||
|
|
const columnCacheDeleted = cache.deleteByPattern(
|
||
|
|
`table_columns:${tableName}`
|
||
|
|
);
|
||
|
|
const countCacheDeleted = cache.deleteByPattern(
|
||
|
|
`table_column_count:${tableName}`
|
||
|
|
);
|
||
|
|
cache.delete("table_list");
|
||
|
|
|
||
|
|
const totalDeleted = columnCacheDeleted + countCacheDeleted + 1;
|
||
|
|
|
||
|
|
logger.info(
|
||
|
|
`테이블 캐시 무효화 완료: ${tableName}, 삭제된 키: ${totalDeleted}개`
|
||
|
|
);
|
||
|
|
} catch (error) {
|
||
|
|
logger.warn(`테이블 캐시 무효화 실패: ${tableName}`, error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|