입력 타입 변경시 바로 적용 가능하게 수정

This commit is contained in:
kjs
2025-12-03 10:24:07 +09:00
parent 37705e4a24
commit 8317af92cd
2 changed files with 265 additions and 1 deletions

View File

@@ -797,6 +797,9 @@ export class TableManagementService {
]
);
// 🔥 해당 컬럼을 사용하는 화면 레이아웃의 widgetType도 업데이트
await this.syncScreenLayoutsInputType(tableName, columnName, inputType, companyCode);
// 🔥 캐시 무효화: 해당 테이블의 컬럼 캐시 삭제
const cacheKeyPattern = `${CacheKeys.TABLE_COLUMNS(tableName, 1, 1000)}_${companyCode}`;
cache.delete(cacheKeyPattern);
@@ -816,6 +819,135 @@ export class TableManagementService {
}
}
/**
* 입력 타입에 해당하는 컴포넌트 ID 반환
* (프론트엔드 webTypeMapping.ts와 동일한 매핑)
*/
private getComponentIdFromInputType(inputType: string): string {
const mapping: Record<string, string> = {
// 텍스트 입력
text: "text-input",
email: "text-input",
password: "text-input",
tel: "text-input",
// 숫자 입력
number: "number-input",
decimal: "number-input",
// 날짜/시간
date: "date-input",
datetime: "date-input",
time: "date-input",
// 텍스트 영역
textarea: "textarea-basic",
// 선택
select: "select-basic",
dropdown: "select-basic",
// 체크박스/라디오
checkbox: "checkbox-basic",
radio: "radio-basic",
boolean: "toggle-switch",
// 파일
file: "file-upload",
// 이미지
image: "image-widget",
img: "image-widget",
picture: "image-widget",
photo: "image-widget",
// 버튼
button: "button-primary",
// 기타
label: "text-display",
code: "select-basic",
entity: "select-basic",
category: "select-basic",
};
return mapping[inputType] || "text-input";
}
/**
* 컬럼 입력 타입 변경 시 해당 컬럼을 사용하는 화면 레이아웃의 widgetType 및 componentType 동기화
* @param tableName - 테이블명
* @param columnName - 컬럼명
* @param inputType - 새로운 입력 타입
* @param companyCode - 회사 코드
*/
private async syncScreenLayoutsInputType(
tableName: string,
columnName: string,
inputType: string,
companyCode: string
): Promise<void> {
try {
// 해당 컬럼을 사용하는 화면 레이아웃 조회
const affectedLayouts = await query<{
layout_id: number;
screen_id: number;
component_id: string;
component_type: string;
properties: any;
}>(
`SELECT sl.layout_id, sl.screen_id, sl.component_id, sl.component_type, sl.properties
FROM screen_layouts sl
JOIN screen_definitions sd ON sl.screen_id = sd.screen_id
WHERE sl.properties->>'tableName' = $1
AND sl.properties->>'columnName' = $2
AND (sd.company_code = $3 OR $3 = '*')`,
[tableName, columnName, companyCode]
);
if (affectedLayouts.length === 0) {
logger.info(
`화면 레이아웃 동기화: ${tableName}.${columnName}을 사용하는 화면 없음`
);
return;
}
logger.info(
`화면 레이아웃 동기화 시작: ${affectedLayouts.length}개 컴포넌트 발견`
);
// 새로운 componentType 계산
const newComponentType = this.getComponentIdFromInputType(inputType);
// 각 레이아웃의 widgetType, componentType 업데이트
for (const layout of affectedLayouts) {
const updatedProperties = {
...layout.properties,
widgetType: inputType,
inputType: inputType,
// componentConfig 내부의 type도 업데이트
componentConfig: {
...layout.properties?.componentConfig,
type: newComponentType,
inputType: inputType,
},
};
await query(
`UPDATE screen_layouts
SET properties = $1, component_type = $2
WHERE layout_id = $3`,
[JSON.stringify(updatedProperties), newComponentType, layout.layout_id]
);
logger.info(
`화면 레이아웃 업데이트: screen_id=${layout.screen_id}, component_id=${layout.component_id}, widgetType=${inputType}, componentType=${newComponentType}`
);
}
logger.info(
`화면 레이아웃 동기화 완료: ${affectedLayouts.length}개 컴포넌트 업데이트됨`
);
} catch (error) {
// 화면 레이아웃 동기화 실패는 치명적이지 않으므로 로그만 남기고 계속 진행
logger.warn(
`화면 레이아웃 동기화 실패 (무시됨): ${tableName}.${columnName}`,
error
);
}
}
/**
* 입력 타입별 기본 상세 설정 생성
*/