9148c358d0
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
199 lines
5.8 KiB
Markdown
199 lines
5.8 KiB
Markdown
---
|
|
id: wiki-2026-0508-re-renders-optimization
|
|
title: Re-renders Optimization
|
|
category: 10_Wiki/Topics
|
|
status: verified
|
|
canonical_id: self
|
|
aliases: [React Re-render Optimization, Render Performance]
|
|
duplicate_of: none
|
|
source_trust_level: A
|
|
confidence_score: 0.9
|
|
verification_status: applied
|
|
tags: [frontend, react, performance, memoization, react-compiler]
|
|
raw_sources: []
|
|
last_reinforced: 2026-05-10
|
|
github_commit: pending
|
|
tech_stack:
|
|
language: TypeScript
|
|
framework: React 19
|
|
---
|
|
|
|
# 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).
|
|
|
|
### 매 응용
|
|
1. Large list rendering: virtualization.
|
|
2. Form with many fields: react-hook-form `register` (uncontrolled).
|
|
3. Heavy chart: memo + Canvas/WebGL.
|
|
|
|
## 💻 패턴
|
|
|
|
### React Compiler (default 2026)
|
|
```tsx
|
|
// 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
|
|
```tsx
|
|
// 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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
// 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)
|
|
```tsx
|
|
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)
|
|
```tsx
|
|
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)
|
|
```tsx
|
|
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 |
|