Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-05-19 - React Flow 데이터 변이와 성능
**Learning:** useMemo 내부에서 노드 데이터를 변이하여 파생된 속성(예: isHighlighted/isDimmed)을 계산하면, 검색 키 입력 시마다 데이터 객체의 식별자가 변경되어 React Flow가 모든 노드를 다시 렌더링하게 만듭니다. 이는 모든 노드 컴포넌트에서 React.memo를 깨뜨리고 큰 그래프에서 검색 시 심각한 프레임 저하를 일으킵니다.
**Action:** 안정적인 node.data 객체를 키로 사용하는 WeakMap을 사용하여 장식된 데이터를 캐시합니다. 이렇게 하면 노드의 검색 일치 상태가 변경되지 않은 경우 렌더링 전반에 걸쳐 정확히 동일한 데이터 객체 참조를 유지하도록 보장하여 React.memo 성능을 유지하고 키 입력당 O(N) 객체 할당을 피할 수 있습니다.
20 changes: 16 additions & 4 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ function strengthLabel(strength: CardinalityStrength): string {
return "보류";
}

// ⚡ Bolt: 객체 참조를 보존하여 불필요한 렌더링을 방지하기 위해 WeakMap을 사용해 검색 상태를 캐싱합니다.
// 안정적인 `node.data` 참조를 키로 사용하여 검색 강조 상태가 변경되지 않은 경우
// 매 검색 입력마다 새로운 `data` 객체를 할당하는 것을 방지합니다.
// 이를 통해 React Flow가 수천 개의 노드를 불필요하게 다시 렌더링하는 것을 막습니다.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const searchDataCache = new WeakMap<object, any>();
Comment on lines +123 to +124

export default function App() {
const [activeView, setActiveView] = useState<WorkspaceView>("dashboard");
const [me, setMe] = useState<CurrentUser | null>(null);
Expand Down Expand Up @@ -201,13 +208,18 @@ export default function App() {
if (!normalizedNodeSearch) return nodes;
return nodes.map((node) => {
const isHighlighted = searchMatchedNodeIds.has(node.id);
return {
...node,
data: {
let nextData = searchDataCache.get(node.data);
if (!nextData || nextData.isHighlighted !== isHighlighted) {
nextData = {
...node.data,
isDimmed: !isHighlighted,
isHighlighted,
},
};
searchDataCache.set(node.data, nextData);
}
return {
...node,
data: nextData,
};
});
}, [nodes, normalizedNodeSearch, searchMatchedNodeIds]);
Expand Down
Loading