7.0 KiB
7.0 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| frontend-million-optimization | Million.js / React Compiler / Forget — auto optimize | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
Million.js / React Compiler
React 의 manual
useMemo/useCallback= boilerplate. React Compiler (Forget) 가 자동 memo. Million.js 가 다른 VDOM. Modern React 의 큰 변화.
📖 핵심 개념
- React Compiler: 자동 memoization (Babel plugin).
- Million.js: 다른 VDOM diff (block 식).
- Manual memo 불필요.
- React 19+ 의 React Compiler stable.
💻 코드 패턴
옛 방식 (manual memo)
const Component = ({ items, onSelect }) => {
const sorted = useMemo(() => items.sort(), [items]);
const handleClick = useCallback((id) => onSelect(id), [onSelect]);
return sorted.map(item => <Item key={item.id} onClick={handleClick} />);
};
→ Boilerplate. 잊으면 re-render.
React Compiler (자동)
// 옛 처럼 작성 — compiler 가 자동 memo.
const Component = ({ items, onSelect }) => {
const sorted = items.sort();
const handleClick = (id) => onSelect(id);
return sorted.map(item => <Item key={item.id} onClick={handleClick} />);
};
// → Compiler 가 useMemo / useCallback 자동 추가.
React Compiler 설정
// babel.config.js
{
plugins: ['babel-plugin-react-compiler'],
}
// Vite
import reactCompiler from 'babel-plugin-react-compiler';
export default {
plugins: [
react({
babel: { plugins: [['babel-plugin-react-compiler']] },
}),
],
};
Compiler 의 동작
Input:
const Item = ({ name }) => <div>{name}</div>;
Output (compiled):
const Item = (props) => {
const $ = _c(2);
let t0;
if ($[0] !== props.name) {
t0 = <div>{props.name}</div>;
$[0] = props.name;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
};
// → Cache 가 component 안.
→ "Memoize all the things" 자동.
Compiler 의 한계
Rules of React 위반 = compile X.
- Mutation 가 ref 외부.
- Closure 가 외부 변수 mutation.
- Component 안 effect 가 안 명시.
→ ESLint rule 가 compile-eligible 검증.
npx react-compiler-eslint
Million.js
yarn add million
// vite.config.ts
import million from 'million/compiler';
export default {
plugins: [million.vite({ auto: true }), react()],
};
// 자동 또는 explicit
import { block } from 'million/react';
const ItemBlock = block(({ name, count }) => (
<div>{name}: {count}</div>
));
// 사용
<ItemBlock name="x" count={1} />
→ Block 안 의 static 부분 = 1번 만. Variable 만 update.
Million.js 의 idea
React VDOM:
- 매 render = 매 element diff.
- 큰 list = 비싼.
Million block:
- Static structure (HTML) + dynamic prop.
- 매 render = prop 변경만 적용.
→ 70-80% 빠름 (specific case).
Auto vs manual block
// Auto (compiler 가 결정)
million.vite({ auto: true })
// Manual
const Heavy = block(MyHeavyComponent);
→ Auto = simple. Manual = 정밀.
When NOT block?
- 동적 children (변하는 structure).
- React-specific feature (Suspense, ErrorBoundary inside).
- 작은 component (gain 작음).
→ List item / row / 큰 visual = sweet spot.
React 19 + Compiler
// React 19 + compiler default
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// useMemo 없음
const expensive = computeExpensive(count);
// useCallback 없음
const handleClick = () => setCount(count + 1);
return <button onClick={handleClick}>{expensive}</button>;
}
→ 자동.
Performance gain
React 18 (manual memo): baseline.
React Compiler: ~10-30% faster (variable).
Million block: 70%+ on list-heavy.
→ Real-world = 10-20% 평균.
Combined
// Compiler + Million
const App = () => {
const items = useFetch('/items');
return <List items={items} />;
};
const List = block(({ items }) => (
items.map(item => <Item key={item.id} item={item} />)
));
Other optimizers
- Preact (작은 React 비슷)
- Solid (no VDOM, 가장 빠름)
- Qwik (resumability)
- Svelte 5 (signal-based)
→ React 의 alternative.
Server Component (RSC) 와 함께
RSC = server-render, no client JS.
Compiler = client component 의 memo 자동.
→ 둘 다 사용. RSC 가 큰 win.
Profiling 추천
import { Profiler } from 'react';
<Profiler id="List" onRender={(id, phase, dur) => console.log(id, phase, dur)}>
<List />
</Profiler>
→ Compiler 적용 전후 비교.
React Devtools
Profiler tab → flamegraph.
Compiled component = "memo" 표시.
useMemo / useCallback 가 obsolete?
React Compiler 후:
- useMemo: 거의 X (compiler 자동).
- useCallback: 거의 X.
- React.memo: 거의 X.
남은 use case:
- Manual control (specific dep)
- 레거시 (compiler 옵트아웃)
- Compiler 가 못 보는 case (rare)
Bundle size
Million.js: ~7 kB.
React Compiler: 0 (build-time).
React 19: 약간 작음 보다 18.
→ Compiler 는 free.
Migration
Existing app:
1. ESLint rule 활성 → violation fix.
2. React 19 / compiler 옵트인.
3. Manual memo 점차 삭제 (선택).
4. Million.js 가 큰 list 만.
→ Gradual.
Future
React Compiler 가 stable (React 19+).
Solid / Svelte 5 의 signal 식 = 다른 path.
Million.js 가 specific gain.
→ React 가 자동 optimize 의 길.
Anti-pattern: 미리 memoize
"혹시" useMemo 다 = compiler 가 잘못 보일 수.
→ Plain code → compiler 가 결정.
Production 의 진짜 답
1. React 19 + Compiler 옵트인.
2. ESLint react-compiler 활성.
3. Profile → bottleneck 식별.
4. Bottleneck 만 Million.js block.
→ 미리 optimize X.
🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| New React app | React 19 + Compiler |
| Existing | Gradual migrate |
| 큰 list | Million.js block + Compiler |
| Real-time UI | Solid / Signal (다른 framework) |
| Server-heavy | RSC + Compiler |
| Microfrontend | Compiler 가 자체 |
| Profiler 결과 | Specific block |
❌ 안티패턴
- Manual memo + Compiler 둘 다: redundant (compiler 가 무시).
- 모든 거 Million block: 작은 component 가치 X.
- Compiler 가 fix all 가정: profile.
- Rules of React 위반: compile X.
- Premature optimization: profile 후.
- 다른 framework 가 자동 magic: 모두 trade-off.
🤖 LLM 활용 힌트
- React Compiler 가 auto memo.
- Manual useMemo / useCallback 가 점차 obsolete.
- Million.js 가 list-heavy boost.
- Profile 후 specific optimize.