feat: UnifiedSelect 및 DynamicComponentRenderer에서 DISTINCT 값 자동 로드 기능 추가

- UnifiedSelect 컴포넌트에서 columnName이 유효하지 않은 경우 옵션 로드를 건너뛰도록 개선하였습니다.
- DynamicComponentRenderer에서 unified-select의 기본 source를 'distinct'로 설정하여 항상 테이블 컬럼에서 DISTINCT 값을 자동으로 로드하도록 변경하였습니다.
- layoutV2Converter에서 상위 레벨 속성을 추출하고, componentConfig와 병합하여 레거시 구조와의 호환성을 유지하였습니다.
- 관련된 경고 메시지를 추가하여 유효하지 않은 columnName에 대한 정보를 로그로 남기도록 하였습니다.
This commit is contained in:
kjs
2026-01-28 16:40:37 +09:00
parent 4fe512aeda
commit 2fac9371c8
3 changed files with 36 additions and 5 deletions

View File

@@ -621,7 +621,9 @@ export const UnifiedSelect = forwardRef<HTMLDivElement, UnifiedSelectProps>(
} else if (source === "select" || source === "distinct") {
// 해당 테이블의 해당 컬럼에서 DISTINCT 값 조회
// tableName, columnName은 props에서 가져옴
if (tableName && columnName) {
// 🆕 columnName이 컴포넌트 ID 형식(comp_xxx)이면 유효하지 않으므로 건너뜀
const isValidColumnName = columnName && !columnName.startsWith("comp_");
if (tableName && isValidColumnName) {
const response = await apiClient.get(`/entity/${tableName}/distinct/${columnName}`);
const data = response.data;
if (data.success && data.data) {
@@ -630,6 +632,9 @@ export const UnifiedSelect = forwardRef<HTMLDivElement, UnifiedSelectProps>(
label: String(item.label),
}));
}
} else if (!isValidColumnName) {
// columnName이 없거나 유효하지 않으면 빈 옵션
console.warn("UnifiedSelect: 유효한 columnName이 없어 옵션을 로드하지 않습니다.", { tableName, columnName });
}
}

View File

@@ -279,14 +279,15 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
);
case "unified-select":
// 🆕 unified-select는 항상 테이블 컬럼에서 distinct 값을 자동 로드
// 정적 옵션(static)은 사용하지 않음
return (
<UnifiedSelect
unifiedType="UnifiedSelect"
{...commonProps}
config={{
mode: config.mode || "dropdown",
source: config.source || "static",
options: config.options || [],
source: config.source || "distinct", // 기본값: 테이블 컬럼에서 distinct 조회
multiple: config.multiple,
searchable: config.searchable,
codeGroup: config.codeGroup,

View File

@@ -45,6 +45,9 @@ export function convertV2ToLegacy(v2Layout: LayoutV2 | null): LegacyLayoutData |
const componentType = getComponentTypeFromUrl(comp.url);
const defaults = getDefaultsByUrl(comp.url);
const mergedConfig = mergeComponentConfig(defaults, comp.overrides);
// 🆕 overrides에서 상위 레벨 속성들 추출
const overrides = comp.overrides || {};
return {
id: comp.id,
@@ -54,8 +57,16 @@ export function convertV2ToLegacy(v2Layout: LayoutV2 | null): LegacyLayoutData |
position: comp.position,
size: comp.size,
componentConfig: mergedConfig,
// 🆕 상위 레벨 속성 복원 (테이블/컬럼 연결 정보)
tableName: overrides.tableName,
columnName: overrides.columnName,
label: overrides.label || mergedConfig.label || "", // 라벨이 없으면 빈 문자열
required: overrides.required,
readonly: overrides.readonly,
codeCategory: overrides.codeCategory,
inputType: overrides.inputType,
webType: overrides.webType,
// 기존 구조 호환을 위한 추가 필드
label: mergedConfig.label || componentType,
style: {},
parentId: null,
gridColumns: 12,
@@ -94,9 +105,23 @@ export function convertLegacyToV2(legacyLayout: LegacyLayoutData): LayoutV2 {
// 기본값 가져오기
const defaults = getDefaultsByUrl(url);
// 🆕 컴포넌트 상위 레벨 속성들도 포함 (tableName, columnName 등)
const topLevelProps: Record<string, any> = {};
if (comp.tableName) topLevelProps.tableName = comp.tableName;
if (comp.columnName) topLevelProps.columnName = comp.columnName;
if (comp.label) topLevelProps.label = comp.label;
if (comp.required !== undefined) topLevelProps.required = comp.required;
if (comp.readonly !== undefined) topLevelProps.readonly = comp.readonly;
if (comp.codeCategory) topLevelProps.codeCategory = comp.codeCategory;
if (comp.inputType) topLevelProps.inputType = comp.inputType;
if (comp.webType) topLevelProps.webType = comp.webType;
// 현재 설정에서 차이값만 추출
const fullConfig = comp.componentConfig || {};
const overrides = extractCustomConfig(fullConfig, defaults);
const configOverrides = extractCustomConfig(fullConfig, defaults);
// 상위 레벨 속성과 componentConfig 병합
const overrides = { ...topLevelProps, ...configOverrides };
return {
id: comp.id,