데이터 수정이 안되는 문제 해결

This commit is contained in:
kjs
2025-12-01 15:21:03 +09:00
parent 93b92960e7
commit da6ac92391
14 changed files with 621 additions and 409 deletions

View File

@@ -124,7 +124,7 @@ export class DynamicFormApi {
* @returns 업데이트 결과
*/
static async updateFormDataPartial(
id: number,
id: string | number, // 🔧 UUID 문자열도 지원
originalData: Record<string, any>,
newData: Record<string, any>,
tableName: string,

View File

@@ -337,6 +337,11 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
// onChange 핸들러 - 컴포넌트 타입에 따라 다르게 처리
const handleChange = (value: any) => {
// autocomplete-search-input, entity-search-input은 자체적으로 onFormDataChange를 호출하므로 중복 저장 방지
if (componentType === "autocomplete-search-input" || componentType === "entity-search-input") {
return;
}
// React 이벤트 객체인 경우 값 추출
let actualValue = value;
if (value && typeof value === "object" && value.nativeEvent && value.target) {

View File

@@ -57,20 +57,42 @@ export function AutocompleteSearchInputComponent({
filterCondition,
});
// 선택된 데이터를 ref로도 유지 (리렌더링 시 초기화 방지)
const selectedDataRef = useRef<EntitySearchResult | null>(null);
const inputValueRef = useRef<string>("");
// formData에서 현재 값 가져오기 (isInteractive 모드)
const currentValue = isInteractive && formData && component?.columnName
? formData[component.columnName]
: value;
// value가 변경되면 표시값 업데이트
// selectedData 변경 시 ref도 업데이트
useEffect(() => {
if (currentValue && selectedData) {
setInputValue(selectedData[displayField] || "");
} else if (!currentValue) {
setInputValue("");
setSelectedData(null);
if (selectedData) {
selectedDataRef.current = selectedData;
inputValueRef.current = inputValue;
}
}, [currentValue, displayField, selectedData]);
}, [selectedData, inputValue]);
// 리렌더링 시 ref에서 값 복원
useEffect(() => {
if (!selectedData && selectedDataRef.current) {
setSelectedData(selectedDataRef.current);
setInputValue(inputValueRef.current);
}
}, []);
// value가 변경되면 표시값 업데이트 - 단, selectedData가 있으면 유지
useEffect(() => {
// selectedData가 있으면 표시값 유지 (사용자가 방금 선택한 경우)
if (selectedData || selectedDataRef.current) {
return;
}
if (!currentValue) {
setInputValue("");
}
}, [currentValue, selectedData]);
// 외부 클릭 감지
useEffect(() => {

View File

@@ -1,6 +1,6 @@
"use client";
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useRef } from "react";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
@@ -21,7 +21,9 @@ export function AutocompleteSearchInputConfigPanel({
config,
onConfigChange,
}: AutocompleteSearchInputConfigPanelProps) {
const [localConfig, setLocalConfig] = useState(config);
// 초기화 여부 추적 (첫 마운트 시에만 config로 초기화)
const isInitialized = useRef(false);
const [localConfig, setLocalConfig] = useState<AutocompleteSearchInputConfig>(config);
const [allTables, setAllTables] = useState<any[]>([]);
const [sourceTableColumns, setSourceTableColumns] = useState<any[]>([]);
const [targetTableColumns, setTargetTableColumns] = useState<any[]>([]);
@@ -32,12 +34,21 @@ export function AutocompleteSearchInputConfigPanel({
const [openTargetTableCombo, setOpenTargetTableCombo] = useState(false);
const [openDisplayFieldCombo, setOpenDisplayFieldCombo] = useState(false);
// 첫 마운트 시에만 config로 초기화 (이후에는 localConfig 유지)
useEffect(() => {
setLocalConfig(config);
if (!isInitialized.current && config) {
setLocalConfig(config);
isInitialized.current = true;
}
}, [config]);
const updateConfig = (updates: Partial<AutocompleteSearchInputConfig>) => {
const newConfig = { ...localConfig, ...updates };
console.log("🔧 [AutocompleteConfigPanel] updateConfig:", {
updates,
localConfig,
newConfig,
});
setLocalConfig(newConfig);
onConfigChange(newConfig);
};
@@ -325,10 +336,11 @@ export function AutocompleteSearchInputConfigPanel({
<div className="space-y-1.5">
<Label className="text-xs"> *</Label>
<Select
value={mapping.sourceField}
onValueChange={(value) =>
updateFieldMapping(index, { sourceField: value })
}
value={mapping.sourceField || undefined}
onValueChange={(value) => {
console.log("🔧 [Select] sourceField 변경:", value);
updateFieldMapping(index, { sourceField: value });
}}
disabled={!localConfig.tableName || isLoadingSourceColumns}
>
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">
@@ -347,10 +359,11 @@ export function AutocompleteSearchInputConfigPanel({
<div className="space-y-1.5">
<Label className="text-xs"> *</Label>
<Select
value={mapping.targetField}
onValueChange={(value) =>
updateFieldMapping(index, { targetField: value })
}
value={mapping.targetField || undefined}
onValueChange={(value) => {
console.log("🔧 [Select] targetField 변경:", value);
updateFieldMapping(index, { targetField: value });
}}
disabled={!localConfig.targetTable || isLoadingTargetColumns}
>
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">

View File

@@ -694,7 +694,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
const context: ButtonActionContext = {
formData: formData || {},
originalData: originalData || {}, // 부분 업데이트용 원본 데이터 추가
originalData: originalData, // 🔧 빈 객체 대신 undefined 유지 (UPDATE 판단에 사용)
screenId: effectiveScreenId, // 🆕 ScreenContext에서 가져온 값 사용
tableName: effectiveTableName, // 🆕 ScreenContext에서 가져온 값 사용
userId, // 🆕 사용자 ID

View File

@@ -414,9 +414,20 @@ export class ButtonActionExecutor {
const primaryKeys = primaryKeyResult.data || [];
const primaryKeyValue = this.extractPrimaryKeyValueFromDB(formData, primaryKeys);
// 단순히 기본키 값 존재 여부로 판단 (임시)
// TODO: 실제 테이블에서 기본키로 레코드 존재 여부 확인하는 API 필요
const isUpdate = false; // 현재는 항상 INSERT로 처리
// 🔧 수정: originalData가 있고 실제 데이터가 있으면 UPDATE 모드로 처리
// originalData는 수정 버튼 클릭 시 editData로 전달되어 context.originalData로 설정됨
// 빈 객체 {}도 truthy이므로 Object.keys로 실제 데이터 유무 확인
const hasRealOriginalData = originalData && Object.keys(originalData).length > 0;
const isUpdate = hasRealOriginalData && !!primaryKeyValue;
console.log("🔍 [handleSave] INSERT/UPDATE 판단:", {
hasOriginalData: !!originalData,
hasRealOriginalData,
originalDataKeys: originalData ? Object.keys(originalData) : [],
primaryKeyValue,
isUpdate,
primaryKeys,
});
let saveResult;