feat: V2 레이아웃 동기화 및 컴포넌트 개선
- TableManagementService에서 V2 레이아웃 동기화 로직을 추가하여, 새로운 입력 타입에 따라 화면 레이아웃을 자동으로 업데이트하도록 개선하였습니다. - syncScreenLayoutsV2InputType 메서드를 통해 V2 레이아웃의 컴포넌트 source를 동기화하는 기능을 구현하였습니다. - EditModal에서 배열 데이터를 쉼표 구분 문자열로 변환하는 로직을 추가하여, 손상된 값을 필터링하고 데이터 저장 시 일관성을 높였습니다. - CategorySelectComponent에서 불필요한 스타일 및 높이 관련 props를 제거하여 코드 간결성을 개선하였습니다. - V2Select 및 관련 컴포넌트에서 height 스타일을 통일하여 사용자 경험을 향상시켰습니다.
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user