From ab8b5a2c9105c1538c11bc9b110755db0b6183e7 Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Thu, 11 Dec 2025 19:14:55 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20UniversalFormModal=20=EC=B1=84=EB=B2=88?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=A4=91=EB=B3=B5=20=ED=98=B8=EC=B6=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - generateNumberingValues 중복 호출 방지 (ref 플래그 추가) - generateOnOpen 시 allocateCode 직접 호출로 변경 - config 변경 시 initializeForm 재호출 비활성화 - cleanup 함수에서 플래그 초기화 추가 - 저장 시점 채번 로직 간소화 (generateOnSave만 처리) --- .../UniversalFormModalComponent.tsx | 89 ++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx index 1cb8439a..a78d2e95 100644 --- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx +++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx @@ -216,14 +216,24 @@ export function UniversalFormModalComponent({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // 빈 의존성 배열 - 마운트 시 한 번만 실행 - // config 변경 시에만 재초기화 (initialData 변경은 무시) + // config 변경 시에만 재초기화 (initialData 변경은 무시) - 채번규칙 제외 useEffect(() => { if (!hasInitialized.current) return; // 최초 초기화 전이면 스킵 - initializeForm(); + console.log('[useEffect config 변경] 재초기화 스킵 (채번 중복 방지)'); + // initializeForm(); // 주석 처리 - config 변경 시 재초기화 안 함 (채번 중복 방지) // eslint-disable-next-line react-hooks/exhaustive-deps }, [config]); + // 컴포넌트 unmount 시 채번 플래그 초기화 + useEffect(() => { + return () => { + console.log('[채번] 컴포넌트 unmount - 플래그 초기화'); + numberingGeneratedRef.current = false; + isGeneratingRef.current = false; + }; + }, []); + // 🆕 beforeFormSave 이벤트 리스너 - ButtonPrimary 저장 시 formData를 전달 // 설정된 필드(columnName)만 병합하여 의도치 않은 덮어쓰기 방지 useEffect(() => { @@ -301,6 +311,8 @@ export function UniversalFormModalComponent({ // 폼 초기화 const initializeForm = useCallback(async () => { + console.log('[initializeForm] 시작'); + // 캡처된 initialData 사용 (props로 전달된 initialData가 아닌) const effectiveInitialData = capturedInitialData.current || initialData; @@ -351,7 +363,9 @@ export function UniversalFormModalComponent({ setOriginalData(effectiveInitialData || {}); // 채번규칙 자동 생성 + console.log('[initializeForm] generateNumberingValues 호출'); await generateNumberingValues(newFormData); + console.log('[initializeForm] 완료'); // eslint-disable-next-line react-hooks/exhaustive-deps }, [config]); // initialData는 의존성에서 제거 (capturedInitialData.current 사용) @@ -369,9 +383,26 @@ export function UniversalFormModalComponent({ return item; }; - // 채번규칙 자동 생성 + // 채번규칙 자동 생성 (중복 호출 방지) + const numberingGeneratedRef = useRef(false); + const isGeneratingRef = useRef(false); // 진행 중 플래그 추가 + const generateNumberingValues = useCallback( async (currentFormData: FormDataState) => { + // 이미 생성되었거나 진행 중이면 스킵 + if (numberingGeneratedRef.current) { + console.log('[채번] 이미 생성됨 - 스킵'); + return; + } + + if (isGeneratingRef.current) { + console.log('[채번] 생성 진행 중 - 스킵'); + return; + } + + isGeneratingRef.current = true; // 진행 중 표시 + console.log('[채번] 생성 시작'); + const updatedData = { ...currentFormData }; let hasChanges = false; @@ -386,14 +417,14 @@ export function UniversalFormModalComponent({ !updatedData[field.columnName] ) { try { - // generateOnOpen: 미리보기만 표시 (실제 순번 할당은 저장 시) - const response = await generateNumberingCode(field.numberingRule.ruleId); + console.log(`[채번 API 호출] ${field.columnName}, ruleId: ${field.numberingRule.ruleId}`); + // generateOnOpen: 모달 열 때 실제 순번 할당 (DB 시퀀스 즉시 증가) + const response = await allocateNumberingCode(field.numberingRule.ruleId); if (response.success && response.data?.generatedCode) { - // 임시 플래그 추가하여 저장 시 실제 순번으로 교체할 것을 표시 updatedData[field.columnName] = response.data.generatedCode; - updatedData[`_${field.columnName}_needsAllocation`] = true; // 저장 시 재할당 필요 hasChanges = true; - console.log(`[채번 미리보기] ${field.columnName} = ${response.data.generatedCode} (저장 시 실제 순번으로 교체)`); + numberingGeneratedRef.current = true; // 생성 완료 표시 + console.log(`[채번 완료] ${field.columnName} = ${response.data.generatedCode}`); } } catch (error) { console.error(`채번규칙 생성 실패 (${field.columnName}):`, error); @@ -402,6 +433,8 @@ export function UniversalFormModalComponent({ } } + isGeneratingRef.current = false; // 진행 완료 + if (hasChanges) { setFormData(updatedData); } @@ -633,40 +666,16 @@ export function UniversalFormModalComponent({ } }); - // 저장 시점 채번규칙 처리 (allocateNumberingCode로 실제 순번 증가) + // 저장 시점 채번규칙 처리 (generateOnSave만 처리) for (const section of config.sections) { for (const field of section.fields) { - if (field.numberingRule?.enabled && field.numberingRule?.ruleId) { - console.log(`[채번 체크] ${field.columnName}:`, { - enabled: field.numberingRule.enabled, - ruleId: field.numberingRule.ruleId, - generateOnSave: field.numberingRule.generateOnSave, - generateOnOpen: field.numberingRule.generateOnOpen, - currentValue: dataToSave[field.columnName], - needsAllocation: dataToSave[`_${field.columnName}_needsAllocation`] - }); - - // generateOnSave: 항상 새로 생성 - // generateOnOpen: 미리보기 값이 있으면 실제 순번으로 교체 - const shouldAllocate = - field.numberingRule.generateOnSave || - (field.numberingRule.generateOnOpen && dataToSave[`_${field.columnName}_needsAllocation`]); - - console.log(`[채번] shouldAllocate = ${shouldAllocate}`); - - if (shouldAllocate) { - console.log(`[채번] allocateNumberingCode 호출 시작: ${field.columnName}, ruleId: ${field.numberingRule.ruleId}`); - const response = await allocateNumberingCode(field.numberingRule.ruleId); - if (response.success && response.data?.generatedCode) { - const oldValue = dataToSave[field.columnName]; - dataToSave[field.columnName] = response.data.generatedCode; - console.log(`[채번 성공] ${field.columnName}: ${oldValue} → ${response.data.generatedCode}`); - - // 임시 플래그 제거 - delete dataToSave[`_${field.columnName}_needsAllocation`]; - } else { - console.error(`[채번 실패] ${field.columnName}:`, response.error); - } + if (field.numberingRule?.enabled && field.numberingRule?.generateOnSave && field.numberingRule?.ruleId) { + const response = await allocateNumberingCode(field.numberingRule.ruleId); + if (response.success && response.data?.generatedCode) { + dataToSave[field.columnName] = response.data.generatedCode; + console.log(`[채번 할당] ${field.columnName} = ${response.data.generatedCode}`); + } else { + console.error(`[채번 실패] ${field.columnName}:`, response.error); } } }