diff --git a/.jules/bolt.md b/.jules/bolt.md index f1a8c146..81ddfaa6 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -77,3 +77,7 @@ Optimized metric route processing to O(N) by creating a mapping of routes direct ## 2024-07-13 - [Optimize Export Dictionary FK lookups] **Learning:** Found O(N * C * E) performance bottleneck in ERD export dictionaries due to repeated array searching with `edges.some()` inside a nested loop over nodes and columns. **Action:** Replace repeated linear array scans for edges by precomputing O(1) Set lookups of foreign key column handles per node before looping. + +## 2024-07-20 - Avoid Data Object Re-creation in React Flow Nodes during Dragging +**Learning:** For high-frequency updates like React Flow drag events (60fps), generating new object references for `node.data` on derived state like `visibleNodes` breaks `React.memo` fast paths. This forces costly full re-renders of all nodes and heightens garbage collection pressure. +**Action:** Use a `WeakMap` keyed by the stable `node.data` reference to cache decorated state modifications (such as search highlights). This preserves object identity across animation frames for fast pointer comparison. diff --git a/CHANGELOG.md b/CHANGELOG.md index 519bee6c..f09979dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,3 +7,6 @@ - [FE] `autoInfer.ts`에 대한 단위 테스트 및 UI 컴포넌트 단위 테스트를 추가하여 100% 테스트 커버리지를 유지합니다. - [FE] ⬇️ **DBML Export**: ERD 다이어그램을 DBML (Database Markup Language) 형식으로 내보낼 수 있는 기능을 추가했습니다. 상단의 DBML 버튼을 클릭하여 다운로드할 수 있습니다. - [FE] 📚 **Data Dictionary Export**: ERD 테이블/컬럼 메타데이터를 CSV 및 Markdown으로 내보내며, CSV formula injection과 Markdown 렌더링 escape를 적용했습니다. + +### 변경 사항 +- `App.tsx` 파일에서 검색 결과 하이라이트를 위해 재생성되던 파생 상태인 `visibleNodes` 객체 생성을 `WeakMap`을 사용하여 캐싱 처리. React Flow의 노드 드래깅 이벤트 중 렌더링 성능 최적화. diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4d0dc258..4e4458f8 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -116,6 +116,12 @@ function strengthLabel(strength: CardinalityStrength): string { return "보류"; } + +const searchDataCache = new WeakMap< + TableNodeData, + { isHighlighted: boolean; data: TableNodeData } +>(); + export default function App() { const [activeView, setActiveView] = useState("dashboard"); const [me, setMe] = useState(null); @@ -201,13 +207,21 @@ export default function App() { if (!normalizedNodeSearch) return nodes; return nodes.map((node) => { const isHighlighted = searchMatchedNodeIds.has(node.id); + let cached = searchDataCache.get(node.data); + if (!cached || cached.isHighlighted !== isHighlighted) { + cached = { + isHighlighted, + data: { + ...node.data, + isDimmed: !isHighlighted, + isHighlighted, + }, + }; + searchDataCache.set(node.data, cached); + } return { ...node, - data: { - ...node.data, - isDimmed: !isHighlighted, - isHighlighted, - }, + data: cached.data, }; }); }, [nodes, normalizedNodeSearch, searchMatchedNodeIds]);