diff --git a/frontend/components/numbering-rule/NumberingRuleDesigner.tsx b/frontend/components/numbering-rule/NumberingRuleDesigner.tsx index 2ab42e75..9320f00e 100644 --- a/frontend/components/numbering-rule/NumberingRuleDesigner.tsx +++ b/frontend/components/numbering-rule/NumberingRuleDesigner.tsx @@ -260,22 +260,24 @@ export const NumberingRuleDesigner: React.FC = ({ toast.success(`규칙 ${newPart.order}가 추가되었습니다`); }, [currentRule, maxRules]); - const handleUpdatePart = useCallback((partId: string, updates: Partial) => { + // partOrder 기반으로 파트 업데이트 (id가 null일 수 있으므로 order 사용) + const handleUpdatePart = useCallback((partOrder: number, updates: Partial) => { setCurrentRule((prev) => { if (!prev) return null; return { ...prev, - parts: prev.parts.map((part) => (part.id === partId ? { ...part, ...updates } : part)), + parts: prev.parts.map((part) => (part.order === partOrder ? { ...part, ...updates } : part)), }; }); }, []); - const handleDeletePart = useCallback((partId: string) => { + // partOrder 기반으로 파트 삭제 (id가 null일 수 있으므로 order 사용) + const handleDeletePart = useCallback((partOrder: number) => { setCurrentRule((prev) => { if (!prev) return null; return { ...prev, - parts: prev.parts.filter((part) => part.id !== partId).map((part, index) => ({ ...part, order: index + 1 })), + parts: prev.parts.filter((part) => part.order !== partOrder).map((part, index) => ({ ...part, order: index + 1 })), }; }); @@ -295,8 +297,6 @@ export const NumberingRuleDesigner: React.FC = ({ setLoading(true); try { - const existing = savedRules.find((r) => r.ruleId === currentRule.ruleId); - // 파트별 기본 autoConfig 정의 const defaultAutoConfigs: Record = { sequence: { sequenceLength: 3, startFrom: 1 }, @@ -345,15 +345,30 @@ export const NumberingRuleDesigner: React.FC = ({ const response = await saveNumberingRuleToTest(ruleToSave); if (response.success && response.data) { + // 깊은 복사하여 savedRules와 currentRule이 다른 객체를 참조하도록 함 + const currentData = JSON.parse(JSON.stringify(response.data)) as NumberingRuleConfig; + + // setSavedRules 내부에서 prev를 사용해서 existing 확인 (클로저 문제 방지) setSavedRules((prev) => { - if (existing) { - return prev.map((r) => (r.ruleId === ruleToSave.ruleId ? response.data! : r)); + const savedData = JSON.parse(JSON.stringify(response.data)) as NumberingRuleConfig; + const existsInPrev = prev.some((r) => r.ruleId === ruleToSave.ruleId); + + console.log("🔍 [handleSave] setSavedRules:", { + ruleId: ruleToSave.ruleId, + existsInPrev, + prevCount: prev.length, + }); + + if (existsInPrev) { + // 기존 규칙 업데이트 + return prev.map((r) => (r.ruleId === ruleToSave.ruleId ? savedData : r)); } else { - return [...prev, response.data!]; + // 새 규칙 추가 + return [...prev, savedData]; } }); - setCurrentRule(response.data); + setCurrentRule(currentData); setSelectedRuleId(response.data.ruleId); await onSave?.(response.data); @@ -366,11 +381,27 @@ export const NumberingRuleDesigner: React.FC = ({ } finally { setLoading(false); } - }, [currentRule, savedRules, onSave, currentTableName]); + }, [currentRule, onSave, currentTableName, menuObjid]); const handleSelectRule = useCallback((rule: NumberingRuleConfig) => { + console.log("🔍 [handleSelectRule] 규칙 선택:", { + ruleId: rule.ruleId, + ruleName: rule.ruleName, + partsCount: rule.parts?.length || 0, + parts: rule.parts?.map(p => ({ id: p.id, order: p.order, partType: p.partType })), + }); + setSelectedRuleId(rule.ruleId); - setCurrentRule(rule); + // 깊은 복사하여 객체 참조 분리 (좌측 목록과 편집 영역의 객체가 공유되지 않도록) + const ruleCopy = JSON.parse(JSON.stringify(rule)) as NumberingRuleConfig; + + console.log("🔍 [handleSelectRule] 깊은 복사 후:", { + ruleId: ruleCopy.ruleId, + partsCount: ruleCopy.parts?.length || 0, + parts: ruleCopy.parts?.map(p => ({ id: p.id, order: p.order, partType: p.partType })), + }); + + setCurrentRule(ruleCopy); toast.info(`"${rule.ruleName}" 규칙을 불러왔습니다`); }, []); @@ -595,12 +626,12 @@ export const NumberingRuleDesigner: React.FC = ({ ) : (
- {currentRule.parts.map((part) => ( + {currentRule.parts.map((part, index) => ( handleUpdatePart(part.id, updates)} - onDelete={() => handleDeletePart(part.id)} + onUpdate={(updates) => handleUpdatePart(part.order, updates)} + onDelete={() => handleDeletePart(part.order)} isPreview={isPreview} /> ))}