f8b21af4be
10_Wiki/Topics 대규모 정리: - 오류 캡처/미완성 stub 문서 227개 제거 - 교차폴더 중복 43클러스터 병합 (63파일 → redirect) - 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건 - 카테고리 MOC 6개 신규 생성 - Graph 섹션 미해결 related-keyword 링크 10,058건 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.8 KiB
5.8 KiB
id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
| id | title | category | status | canonical_id | aliases | duplicate_of | source_trust_level | confidence_score | verification_status | tags | raw_sources | last_reinforced | github_commit | tech_stack | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| wiki-2026-0508-re-renders-optimization | Re-renders Optimization | 10_Wiki/Topics | verified | self |
|
none | A | 0.9 | applied |
|
2026-05-10 | pending |
|
Re-renders Optimization
매 한 줄
"매 React 의 component re-render 의 최소화". 2026 React 19+ 매 React Compiler (auto-memo) 의 default 로 manual
useMemo/useCallback의 의미 약화. 매 measure first, optimize where needed.
매 핵심
매 Re-render Trigger
- State change (
useState,useReducer). - Parent re-render → child re-render (default).
- Context value change → 매 consumer 전체 re-render.
- Hook 의 internal state.
매 Tool (2026)
- React Compiler (default in React 19+): 매 auto-memoization, useMemo/useCallback 거의 불필요.
- React DevTools Profiler: 매 flame graph 로 re-render cost.
- why-did-you-render: 매 dev-time unnecessary re-render 감지.
- React Scan: 매 visual highlight (real-time).
매 핵심 기법
- Component 분할 (state collocation).
React.memo(props equality).useMemo/useCallback(compiler 가 대부분 자동).- Context split (frequently-changing 분리).
- Virtualization (TanStack Virtual, react-window).
매 응용
- Large list rendering: virtualization.
- Form with many fields: react-hook-form
register(uncontrolled). - Heavy chart: memo + Canvas/WebGL.
💻 패턴
React Compiler (default 2026)
// React 19 with Compiler — 매 manual memo 거의 불필요
function ExpensiveList({ items, filter }: Props) {
const filtered = items.filter(i => i.name.includes(filter)); // auto-memoized
return <ul>{filtered.map(i => <li key={i.id}>{i.name}</li>)}</ul>;
}
매 State Collocation
// Bad: state in App, but only Modal needs it
function App() {
const [modalOpen, setModalOpen] = useState(false);
return (
<>
<ExpensiveTree /> {/* re-renders on toggle */}
<Modal open={modalOpen} />
</>
);
}
// Good: state in wrapper
function App() {
return (
<>
<ExpensiveTree />
<ModalWrapper />
</>
);
}
function ModalWrapper() {
const [open, setOpen] = useState(false);
return <Modal open={open} />;
}
React.memo + useMemo 의 props
const Child = React.memo(({ data }: { data: Item[] }) => {
return <ul>{data.map(i => <li key={i.id}>{i.name}</li>)}</ul>;
});
function Parent({ items, query }: Props) {
// Without compiler: useMemo necessary
const filtered = useMemo(() => items.filter(i => i.name.includes(query)), [items, query]);
return <Child data={filtered} />;
}
Context Split
// Bad: single context, every change re-renders all
const AppContext = createContext({ user: null, theme: 'light', cart: [] });
// Good: split by change frequency
const UserContext = createContext<User | null>(null);
const ThemeContext = createContext<Theme>('light');
const CartContext = createContext<Cart>([]);
useSyncExternalStore (selector 의 fine-grained)
import { useSyncExternalStore } from 'react';
import { create } from 'zustand';
const useStore = create<State>(() => ({ count: 0, name: 'a' }));
function CountDisplay() {
// Only re-render on count change
const count = useStore((s) => s.count);
return <div>{count}</div>;
}
Virtualization (TanStack Virtual)
import { useVirtualizer } from '@tanstack/react-virtual';
function BigList({ items }: { items: Item[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 40,
});
return (
<div ref={parentRef} style={{ height: 400, overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
{virtualizer.getVirtualItems().map(v => (
<div key={v.key} style={{ position: 'absolute', top: v.start, height: v.size }}>
{items[v.index].name}
</div>
))}
</div>
</div>
);
}
React Scan (dev only)
import { scan } from 'react-scan';
if (process.env.NODE_ENV === 'development') {
scan({ enabled: true, log: true });
}
매 결정 기준
| 상황 | Approach |
|---|---|
| React 19+ project | React Compiler (자동) |
| Legacy React 18 | manual memo + Profiler |
| Large list (1000+) | Virtualization |
| Complex form | react-hook-form (uncontrolled) |
| Frequently-changing global state | Zustand/Jotai with selector |
| Cross-cutting state | Context (split by frequency) |
기본값: 매 React 19 Compiler enable → measure → optimize hot path only.
🔗 Graph
- 부모: React · Performance_Optimization
- 응용: Virtualization
- Adjacent: React_Compiler · Concurrent_Rendering · Suspense
🤖 LLM 활용
언제: Profiler 매 100ms+ render time 의 component, dropped frame, slow input. 언제 X: 매 micro-optimization without measurement — 매 premature.
❌ 안티패턴
- 모든 곳 useMemo: 매 dependency 비교 cost > recomputation cost (특히 primitive).
- Inline object/array as Context value: 매 every render new reference → consumer re-render.
- React.memo without checking props: 매 props 가 항상 다르면 memo 무효 + cost 추가.
- Optimization without Profiler: guessing → wasted effort.
🧪 검증 / 중복
- Verified (React 19 docs, React Compiler RC, TanStack Virtual 3).
- 신뢰도 A.
🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — React Compiler / React Scan modern stack |