테이블리스트 조인기능 구현
This commit is contained in:
326
backend-node/src/controllers/entityJoinController.ts
Normal file
326
backend-node/src/controllers/entityJoinController.ts
Normal file
@@ -0,0 +1,326 @@
|
||||
import { Request, Response } from "express";
|
||||
import { logger } from "../utils/logger";
|
||||
import { TableManagementService } from "../services/tableManagementService";
|
||||
import { entityJoinService } from "../services/entityJoinService";
|
||||
import { referenceCacheService } from "../services/referenceCacheService";
|
||||
|
||||
const tableManagementService = new TableManagementService();
|
||||
|
||||
/**
|
||||
* Entity 조인 기능 컨트롤러
|
||||
* ID값을 의미있는 데이터로 자동 변환하는 API 제공
|
||||
*/
|
||||
export class EntityJoinController {
|
||||
/**
|
||||
* Entity 조인이 포함된 테이블 데이터 조회
|
||||
* GET /api/table-management/tables/:tableName/data-with-joins
|
||||
*/
|
||||
async getTableDataWithJoins(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tableName } = req.params;
|
||||
const {
|
||||
page = 1,
|
||||
size = 20,
|
||||
search,
|
||||
sortBy,
|
||||
sortOrder = "asc",
|
||||
enableEntityJoin = true,
|
||||
userLang, // userLang은 별도로 분리하여 search에 포함되지 않도록 함
|
||||
...otherParams
|
||||
} = req.query;
|
||||
|
||||
logger.info(`Entity 조인 데이터 요청: ${tableName}`, {
|
||||
page,
|
||||
size,
|
||||
enableEntityJoin,
|
||||
search,
|
||||
});
|
||||
|
||||
// 검색 조건 처리
|
||||
let searchConditions: Record<string, any> = {};
|
||||
if (search) {
|
||||
try {
|
||||
// search가 문자열인 경우 JSON 파싱
|
||||
searchConditions =
|
||||
typeof search === "string" ? JSON.parse(search) : search;
|
||||
} catch (error) {
|
||||
logger.warn("검색 조건 파싱 오류:", error);
|
||||
searchConditions = {};
|
||||
}
|
||||
}
|
||||
|
||||
const result = await tableManagementService.getTableDataWithEntityJoins(
|
||||
tableName,
|
||||
{
|
||||
page: Number(page),
|
||||
size: Number(size),
|
||||
search:
|
||||
Object.keys(searchConditions).length > 0
|
||||
? searchConditions
|
||||
: undefined,
|
||||
sortBy: sortBy as string,
|
||||
sortOrder: sortOrder as string,
|
||||
enableEntityJoin:
|
||||
enableEntityJoin === "true" || enableEntityJoin === true,
|
||||
}
|
||||
);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "Entity 조인 데이터 조회 성공",
|
||||
data: result,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Entity 조인 데이터 조회 실패", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Entity 조인 데이터 조회 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블의 Entity 조인 설정 조회
|
||||
* GET /api/table-management/tables/:tableName/entity-joins
|
||||
*/
|
||||
async getEntityJoinConfigs(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tableName } = req.params;
|
||||
|
||||
logger.info(`Entity 조인 설정 조회: ${tableName}`);
|
||||
|
||||
const joinConfigs = await entityJoinService.detectEntityJoins(tableName);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "Entity 조인 설정 조회 성공",
|
||||
data: {
|
||||
tableName,
|
||||
joinConfigs,
|
||||
count: joinConfigs.length,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Entity 조인 설정 조회 실패", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Entity 조인 설정 조회 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 참조 테이블의 표시 가능한 컬럼 목록 조회
|
||||
* GET /api/table-management/reference-tables/:tableName/columns
|
||||
*/
|
||||
async getReferenceTableColumns(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tableName } = req.params;
|
||||
|
||||
logger.info(`참조 테이블 컬럼 조회: ${tableName}`);
|
||||
|
||||
const columns =
|
||||
await tableManagementService.getReferenceTableColumns(tableName);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "참조 테이블 컬럼 조회 성공",
|
||||
data: {
|
||||
tableName,
|
||||
columns,
|
||||
count: columns.length,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("참조 테이블 컬럼 조회 실패", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "참조 테이블 컬럼 조회 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 컬럼 Entity 설정 업데이트 (display_column 포함)
|
||||
* PUT /api/table-management/tables/:tableName/columns/:columnName/entity-settings
|
||||
*/
|
||||
async updateEntitySettings(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tableName, columnName } = req.params;
|
||||
const {
|
||||
webType,
|
||||
referenceTable,
|
||||
referenceColumn,
|
||||
displayColumn,
|
||||
columnLabel,
|
||||
description,
|
||||
} = req.body;
|
||||
|
||||
logger.info(`Entity 설정 업데이트: ${tableName}.${columnName}`, req.body);
|
||||
|
||||
// Entity 타입인 경우 필수 필드 검증
|
||||
if (webType === "entity") {
|
||||
if (!referenceTable || !referenceColumn) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message:
|
||||
"Entity 타입의 경우 referenceTable과 referenceColumn이 필수입니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await tableManagementService.updateColumnLabel(tableName, columnName, {
|
||||
webType,
|
||||
referenceTable,
|
||||
referenceColumn,
|
||||
displayColumn,
|
||||
columnLabel,
|
||||
description,
|
||||
});
|
||||
|
||||
// Entity 설정 변경 시 관련 캐시 무효화
|
||||
if (webType === "entity" && referenceTable) {
|
||||
referenceCacheService.invalidateCache(
|
||||
referenceTable,
|
||||
referenceColumn,
|
||||
displayColumn
|
||||
);
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "Entity 설정 업데이트 성공",
|
||||
data: {
|
||||
tableName,
|
||||
columnName,
|
||||
settings: {
|
||||
webType,
|
||||
referenceTable,
|
||||
referenceColumn,
|
||||
displayColumn,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Entity 설정 업데이트 실패", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Entity 설정 업데이트 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 캐시 상태 조회
|
||||
* GET /api/table-management/cache/status
|
||||
*/
|
||||
async getCacheStatus(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
logger.info("캐시 상태 조회");
|
||||
|
||||
const cacheInfo = referenceCacheService.getCacheInfo();
|
||||
const overallHitRate = referenceCacheService.getOverallCacheHitRate();
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "캐시 상태 조회 성공",
|
||||
data: {
|
||||
overallHitRate,
|
||||
caches: cacheInfo,
|
||||
summary: {
|
||||
totalCaches: cacheInfo.length,
|
||||
totalSize: cacheInfo.reduce((sum, cache) => sum + cache.size, 0),
|
||||
averageHitRate:
|
||||
cacheInfo.length > 0
|
||||
? cacheInfo.reduce((sum, cache) => sum + cache.hitRate, 0) /
|
||||
cacheInfo.length
|
||||
: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("캐시 상태 조회 실패", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "캐시 상태 조회 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 캐시 무효화
|
||||
* DELETE /api/table-management/cache
|
||||
*/
|
||||
async invalidateCache(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { table, keyColumn, displayColumn } = req.query;
|
||||
|
||||
logger.info("캐시 무효화 요청", { table, keyColumn, displayColumn });
|
||||
|
||||
if (table && keyColumn && displayColumn) {
|
||||
// 특정 캐시만 무효화
|
||||
referenceCacheService.invalidateCache(
|
||||
table as string,
|
||||
keyColumn as string,
|
||||
displayColumn as string
|
||||
);
|
||||
} else {
|
||||
// 전체 캐시 무효화
|
||||
referenceCacheService.invalidateCache();
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "캐시 무효화 완료",
|
||||
data: {
|
||||
target: table ? `${table}.${keyColumn}.${displayColumn}` : "전체",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("캐시 무효화 실패", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "캐시 무효화 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 공통 참조 테이블 자동 캐싱
|
||||
* POST /api/table-management/cache/preload
|
||||
*/
|
||||
async preloadCommonCaches(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
logger.info("공통 참조 테이블 자동 캐싱 시작");
|
||||
|
||||
await referenceCacheService.autoPreloadCommonTables();
|
||||
|
||||
const cacheInfo = referenceCacheService.getCacheInfo();
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "공통 참조 테이블 캐싱 완료",
|
||||
data: {
|
||||
preloadedCaches: cacheInfo.length,
|
||||
caches: cacheInfo,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("공통 참조 테이블 캐싱 실패", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "공통 참조 테이블 캐싱 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const entityJoinController = new EntityJoinController();
|
||||
Reference in New Issue
Block a user