feat: V2 레이아웃 동기화 및 컴포넌트 개선

- TableManagementService에서 V2 레이아웃 동기화 로직을 추가하여, 새로운 입력 타입에 따라 화면 레이아웃을 자동으로 업데이트하도록 개선하였습니다.
- syncScreenLayoutsV2InputType 메서드를 통해 V2 레이아웃의 컴포넌트 source를 동기화하는 기능을 구현하였습니다.
- EditModal에서 배열 데이터를 쉼표 구분 문자열로 변환하는 로직을 추가하여, 손상된 값을 필터링하고 데이터 저장 시 일관성을 높였습니다.
- CategorySelectComponent에서 불필요한 스타일 및 높이 관련 props를 제거하여 코드 간결성을 개선하였습니다.
- V2Select 및 관련 컴포넌트에서 height 스타일을 통일하여 사용자 경험을 향상시켰습니다.
This commit is contained in:
DDD1542
2026-02-06 09:15:50 +09:00
parent 9f3437d499
commit a424b3b775
12 changed files with 833 additions and 105 deletions

View File

@@ -102,6 +102,80 @@ export interface NodeExecutionSummary {
error?: string;
}
// ===== 헬퍼 함수 =====
/**
* 🔧 유효한 값인지 체크 (중괄호, 따옴표, 백슬래시 없어야 함)
* 숫자도 유효한 값으로 처리
*/
function isValidDBValue(v: any): boolean {
// 숫자면 유효 (나중에 문자열로 변환됨)
if (typeof v === "number" && !isNaN(v)) return true;
// 문자열이 아니면 무효
if (typeof v !== "string") return false;
if (!v || v.trim() === "") return false;
if (v.includes("{") || v.includes("}") || v.includes('"') || v.includes("\\")) return false;
return true;
}
/**
* 🔧 값을 DB 저장용으로 정규화 (PostgreSQL 배열 형식 저장 방지)
* - JavaScript 배열 → 쉼표 구분 문자열 (유효한 값만)
* - PostgreSQL 배열 형식 문자열 → 쉼표 구분 문자열 (유효한 값만)
* - 중첩된 잘못된 형식 → null
*/
function normalizeValueForDB(value: any): any {
// 1. 배열이면 유효한 값만 필터링 후 쉼표 구분 문자열로 변환
if (Array.isArray(value)) {
// 숫자를 문자열로 변환하고 유효한 값만 필터링
const validValues = value
.map(v => typeof v === "number" ? String(v) : v)
.filter(isValidDBValue)
.map(v => typeof v === "number" ? String(v) : v); // 최종 문자열 변환
if (validValues.length === 0) {
console.warn(`⚠️ [normalizeValueForDB] 배열에 유효한 값 없음:`, value);
return null;
}
const normalized = validValues.join(",");
console.log(`🔧 [normalizeValueForDB] 배열→문자열:`, { original: value.length, valid: validValues.length, normalized });
return normalized;
}
// 2. 문자열인데 잘못된 형식이면 정리
if (typeof value === "string" && value) {
// 잘못된 형식 감지
if (value.includes("{") || value.includes("}") || value.includes('\\"') || value.includes("\\\\")) {
console.warn(`⚠️ [normalizeValueForDB] 잘못된 문자열 형식:`, value.substring(0, 80));
// 정규표현식으로 유효한 코드만 추출
const codePattern = /\b(CAT_[A-Z0-9_]+|[A-Z]{2,}_[A-Z0-9_]+)\b/g;
const matches = value.match(codePattern);
if (matches && matches.length > 0) {
const uniqueValues = [...new Set(matches)];
const normalized = uniqueValues.join(",");
console.log(`🔧 [normalizeValueForDB] 코드 추출:`, { count: uniqueValues.length, normalized });
return normalized;
}
console.warn(`⚠️ [normalizeValueForDB] 유효한 코드 없음, null 반환`);
return null;
}
// 쉼표 구분 문자열이면 각 값 검증
if (value.includes(",")) {
const parts = value.split(",").map(v => v.trim()).filter(isValidDBValue);
if (parts.length === 0) {
return null;
}
return parts.join(",");
}
}
return value;
}
// ===== 메인 실행 서비스 =====
export class NodeFlowExecutionService {
@@ -1019,10 +1093,12 @@ export class NodeFlowExecutionService {
);
}
values.push(value);
// 🔧 배열을 쉼표 구분 문자열로 변환
const normalizedValue = normalizeValueForDB(value);
values.push(normalizedValue);
// 🔥 삽입된 값을 데이터에 반영
insertedData[mapping.targetField] = value;
insertedData[mapping.targetField] = normalizedValue;
}
// 🆕 writer와 company_code 자동 추가 (필드 매핑에 없는 경우)
@@ -1155,9 +1231,11 @@ export class NodeFlowExecutionService {
mapping.staticValue !== undefined
? mapping.staticValue
: data[mapping.sourceField];
values.push(value);
// 🔧 배열을 쉼표 구분 문자열로 변환
const normalizedValue = normalizeValueForDB(value);
values.push(normalizedValue);
// 🔥 삽입된 데이터 객체에 매핑된 값 적용
insertedData[mapping.targetField] = value;
insertedData[mapping.targetField] = normalizedValue;
});
// 외부 DB별 SQL 문법 차이 처리
@@ -1493,7 +1571,8 @@ export class NodeFlowExecutionService {
if (mapping.targetField) {
setClauses.push(`${mapping.targetField} = $${paramIndex}`);
values.push(value);
// 🔧 배열을 쉼표 구분 문자열로 변환
values.push(normalizeValueForDB(value));
paramIndex++;
}
});
@@ -1556,11 +1635,13 @@ export class NodeFlowExecutionService {
// targetField가 비어있지 않은 경우만 추가
if (mapping.targetField) {
setClauses.push(`${mapping.targetField} = $${paramIndex}`);
values.push(value);
// 🔧 배열을 쉼표 구분 문자열로 변환
const normalizedValue = normalizeValueForDB(value);
values.push(normalizedValue);
paramIndex++;
// 🔥 업데이트된 값을 데이터에 반영
updatedData[mapping.targetField] = value;
updatedData[mapping.targetField] = normalizedValue;
} else {
console.log(
`⚠️ targetField가 비어있어 스킵: ${mapping.sourceField}`
@@ -1685,10 +1766,12 @@ export class NodeFlowExecutionService {
setClauses.push(`${mapping.targetField} = $${paramIndex}`);
}
values.push(value);
// 🔧 배열을 쉼표 구분 문자열로 변환
const normalizedValue = normalizeValueForDB(value);
values.push(normalizedValue);
paramIndex++;
// 🔥 업데이트된 데이터 객체에 매핑된 값 적용
updatedData[mapping.targetField] = value;
updatedData[mapping.targetField] = normalizedValue;
});
// WHERE 조건 생성
@@ -2317,7 +2400,8 @@ export class NodeFlowExecutionService {
? mapping.staticValue
: data[mapping.sourceField];
setClauses.push(`${mapping.targetField} = $${paramIndex}`);
updateValues.push(value);
// 🔧 배열을 쉼표 구분 문자열로 변환
updateValues.push(normalizeValueForDB(value));
paramIndex++;
}
});
@@ -2368,7 +2452,8 @@ export class NodeFlowExecutionService {
? mapping.staticValue
: data[mapping.sourceField];
columns.push(mapping.targetField);
values.push(value);
// 🔧 배열을 쉼표 구분 문자열로 변환
values.push(normalizeValueForDB(value));
});
// 🆕 writer와 company_code 자동 추가 (필드 매핑에 없는 경우)
@@ -2549,7 +2634,8 @@ export class NodeFlowExecutionService {
setClauses.push(`${mapping.targetField} = $${paramIndex}`);
}
updateValues.push(value);
// 🔧 배열을 쉼표 구분 문자열로 변환
updateValues.push(normalizeValueForDB(value));
paramIndex++;
}
});
@@ -2587,7 +2673,8 @@ export class NodeFlowExecutionService {
? mapping.staticValue
: data[mapping.sourceField];
columns.push(mapping.targetField);
values.push(value);
// 🔧 배열을 쉼표 구분 문자열로 변환
values.push(normalizeValueForDB(value));
});
let insertSql: string;