fix: UniversalFormModal 채번규칙 중복 호출 문제 해결

- generateNumberingValues 중복 호출 방지 (ref 플래그 추가)
- generateOnOpen 시 allocateCode 직접 호출로 변경
- config 변경 시 initializeForm 재호출 비활성화
- cleanup 함수에서 플래그 초기화 추가
- 저장 시점 채번 로직 간소화 (generateOnSave만 처리)
This commit is contained in:
SeongHyun Kim 2025-12-11 19:14:55 +09:00
parent 038c5a0973
commit ab8b5a2c91
1 changed files with 49 additions and 40 deletions

View File

@ -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);
}
}
}