binary-agents 1.1.5 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/agents/code-reviewer.md +84 -97
- package/agents/fundamentals-cohesion.md +293 -0
- package/agents/fundamentals-coupling.md +372 -0
- package/agents/fundamentals-predictability.md +287 -0
- package/agents/fundamentals-readability.md +561 -0
- package/agents/junior-checker.md +93 -445
- package/agents/maintainable-code-reviewer.md +30 -133
- package/agents/react-performance-optimizer.md +89 -295
- package/agents/react-principles-reviewer.md +174 -237
- package/agents/react-state-reviewer.md +456 -0
- package/agents/refactor-analyzer.md +74 -253
- package/agents/subagent-builder.md +69 -75
- package/commands/code-review.md +27 -12
- package/commands/design-to-code.md +41 -14
- package/commands/review-pr.md +37 -5
- package/docs/BUILDER_GUIDE.md +6 -2
- package/package.json +1 -1
- package/agents/fundamentals-code.md +0 -993
|
@@ -21,11 +21,12 @@ React 애플리케이션의 렌더링 병목, 불필요한 리렌더링, 훅 최
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
-
## 평가
|
|
24
|
+
## 평가 원칙
|
|
25
25
|
|
|
26
|
-
### 1. 리렌더링 최적화
|
|
26
|
+
### 1. 리렌더링 최적화
|
|
27
27
|
|
|
28
28
|
**✅ 찾아야 할 것:**
|
|
29
|
+
|
|
29
30
|
- 안정적인 props를 받는 컴포넌트의 `React.memo`
|
|
30
31
|
- 비싼 계산을 위한 `useMemo`
|
|
31
32
|
- 메모이된 자식에 전달되는 콜백을 위한 `useCallback`
|
|
@@ -33,13 +34,15 @@ React 애플리케이션의 렌더링 병목, 불필요한 리렌더링, 훅 최
|
|
|
33
34
|
- 비싼 렌더링 격리를 위한 컴포넌트 분할
|
|
34
35
|
|
|
35
36
|
**❌ 안티패턴:**
|
|
37
|
+
|
|
36
38
|
- 같은 props로 리렌더링되는 컴포넌트
|
|
37
39
|
- props에 인라인 객체/배열 생성: `<Child data={{ value }} />`
|
|
38
40
|
- props에 인라인 화살표 함수: `<Child onClick={() => doSomething()} />`
|
|
39
41
|
- 자주 렌더링되는 컴포넌트에 `React.memo` 누락
|
|
40
42
|
- 인라인 객체 값을 가진 Context Provider
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
**🔍 검색:**
|
|
45
|
+
|
|
43
46
|
```typescript
|
|
44
47
|
// Grep 패턴
|
|
45
48
|
- 검색: "onClick={\\(\\)" (인라인 화살표 함수)
|
|
@@ -49,15 +52,17 @@ React 애플리케이션의 렌더링 병목, 불필요한 리렌더링, 훅 최
|
|
|
49
52
|
```
|
|
50
53
|
|
|
51
54
|
**영향 지표:**
|
|
55
|
+
|
|
52
56
|
- 예상 리렌더 감소: X%
|
|
53
57
|
- 메모이 가능한 컴포넌트: N개
|
|
54
58
|
- 사용자 인터랙션당 불필요한 렌더: M회
|
|
55
59
|
|
|
56
60
|
---
|
|
57
61
|
|
|
58
|
-
### 2. Context 최적화
|
|
62
|
+
### 2. Context 최적화
|
|
59
63
|
|
|
60
64
|
**✅ 찾아야 할 것:**
|
|
65
|
+
|
|
61
66
|
- 업데이트 빈도별 Context 분할 (State/Dispatch/Config 패턴)
|
|
62
67
|
- 외부 상태 구독을 위한 `useSyncExternalStore`
|
|
63
68
|
- 불필요한 Context 리렌더 방지를 위한 Selector 패턴
|
|
@@ -65,6 +70,7 @@ React 애플리케이션의 렌더링 병목, 불필요한 리렌더링, 훅 최
|
|
|
65
70
|
- 하나의 큰 Context 대신 여러 개의 집중된 Context
|
|
66
71
|
|
|
67
72
|
**❌ 안티패턴:**
|
|
73
|
+
|
|
68
74
|
- 혼합된 관심사를 가진 단일 Context (state + config + handlers)
|
|
69
75
|
- 메모이되지 않은 Provider 값: `value={{ state, dispatch }}`
|
|
70
76
|
- 관련 없는 Context 업데이트로 리렌더되는 Consumer
|
|
@@ -72,6 +78,7 @@ React 애플리케이션의 렌더링 병목, 불필요한 리렌더링, 훅 최
|
|
|
72
78
|
- Context 과다 사용 (1-2단계 prop 전달은 괜찮음)
|
|
73
79
|
|
|
74
80
|
**Context 분할 패턴 (GOOD):**
|
|
81
|
+
|
|
75
82
|
```typescript
|
|
76
83
|
// State context (자주 변경)
|
|
77
84
|
const CarouselStateContext = createContext<State | null>(null);
|
|
@@ -85,26 +92,32 @@ const CarouselConfigContext = createContext<Config | null>(null);
|
|
|
85
92
|
// 직접 접근을 위한 훅
|
|
86
93
|
export const useCarouselState = () => {
|
|
87
94
|
const context = useContext(CarouselStateContext);
|
|
88
|
-
if (!context)
|
|
95
|
+
if (!context)
|
|
96
|
+
throw new Error(
|
|
97
|
+
'useCarouselState는 CarouselProvider 내부에서 사용해야 합니다',
|
|
98
|
+
);
|
|
89
99
|
return context;
|
|
90
100
|
};
|
|
91
101
|
```
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
**🔍 검색:**
|
|
104
|
+
|
|
94
105
|
- 모든 `createContext` 호출 찾기
|
|
95
106
|
- provider 값이 메모이되었는지 확인
|
|
96
107
|
- context가 책임별로 분할되었는지 검증
|
|
97
108
|
- `useSyncExternalStore`를 사용한 selector 패턴 찾기
|
|
98
109
|
|
|
99
110
|
**🌐 웹 검색:**
|
|
111
|
+
|
|
100
112
|
- "React context performance optimization [current year]"
|
|
101
113
|
- "useSyncExternalStore best practices"
|
|
102
114
|
|
|
103
115
|
---
|
|
104
116
|
|
|
105
|
-
### 3. 훅 의존성
|
|
117
|
+
### 3. 훅 의존성
|
|
106
118
|
|
|
107
119
|
**✅ 찾아야 할 것:**
|
|
120
|
+
|
|
108
121
|
- `useEffect`, `useMemo`, `useCallback`의 올바른 의존성 배열
|
|
109
122
|
- 안정적인 참조 (핸들러를 위한 useRef, useCallback)
|
|
110
123
|
- Effect cleanup 함수
|
|
@@ -112,6 +125,7 @@ export const useCarouselState = () => {
|
|
|
112
125
|
- 명확하고 단일 책임을 가진 Effect
|
|
113
126
|
|
|
114
127
|
**❌ 안티패턴:**
|
|
128
|
+
|
|
115
129
|
- 내부에서 값을 사용하는데 빈 deps `[]`
|
|
116
130
|
- ESLint 비활성화: `// eslint-disable-next-line react-hooks/exhaustive-deps`
|
|
117
131
|
- 오래된 클로저 (누락된 의존성)
|
|
@@ -119,31 +133,38 @@ export const useCarouselState = () => {
|
|
|
119
133
|
- 매 렌더마다 변경되는 의존성 (인라인 객체/함수)
|
|
120
134
|
|
|
121
135
|
**의존성 이슈 (BAD):**
|
|
136
|
+
|
|
122
137
|
```typescript
|
|
123
138
|
// BAD: 매 렌더마다 재생성되는 selector 함수
|
|
124
139
|
const value = useCarouselSelector((state) => state.currentIndex);
|
|
125
140
|
|
|
126
141
|
// GOOD: 안정적인 selector 참조
|
|
127
|
-
const selectCurrentIndex = useCallback(
|
|
142
|
+
const selectCurrentIndex = useCallback(
|
|
143
|
+
(state: State) => state.currentIndex,
|
|
144
|
+
[],
|
|
145
|
+
);
|
|
128
146
|
const value = useCarouselSelector(selectCurrentIndex);
|
|
129
147
|
```
|
|
130
148
|
|
|
131
|
-
|
|
149
|
+
**🔍 검색:**
|
|
150
|
+
|
|
132
151
|
- `useEffect`, `useMemo`, `useCallback` Grep
|
|
133
152
|
- 의존성이 안정적인지 확인
|
|
134
153
|
- ESLint disable 주석 찾기
|
|
135
154
|
- cleanup 함수 존재 검증
|
|
136
155
|
|
|
137
156
|
**영향 지표:**
|
|
157
|
+
|
|
138
158
|
- 렌더당 불필요한 effect 실행: N회
|
|
139
159
|
- 방지된 오래된 클로저 버그: M개
|
|
140
160
|
- 메모리 누수 위험: P개
|
|
141
161
|
|
|
142
162
|
---
|
|
143
163
|
|
|
144
|
-
### 4. 모던 React 패턴
|
|
164
|
+
### 4. 모던 React 패턴
|
|
145
165
|
|
|
146
166
|
**✅ 찾아야 할 것:**
|
|
167
|
+
|
|
147
168
|
- 외부 구독을 위한 `useSyncExternalStore` (DOM 이벤트, 브라우저 API)
|
|
148
169
|
- 급하지 않은 업데이트를 위한 `useTransition`
|
|
149
170
|
- 비싼 리렌더를 위한 `useDeferredValue`
|
|
@@ -152,12 +173,14 @@ const value = useCarouselSelector(selectCurrentIndex);
|
|
|
152
173
|
- 비동기 경계를 위한 Suspense
|
|
153
174
|
|
|
154
175
|
**❌ 안티패턴:**
|
|
176
|
+
|
|
155
177
|
- 브라우저 API 구독에 `useEffect` 사용 (`useSyncExternalStore` 사용해야)
|
|
156
178
|
- 무거운 계산으로 렌더 차단 (`useTransition` 사용해야)
|
|
157
179
|
- Error Boundary 누락
|
|
158
180
|
- 직접 DOM 조작 (ref 제외)
|
|
159
181
|
|
|
160
182
|
**useSyncExternalStore 패턴 (GOOD):**
|
|
183
|
+
|
|
161
184
|
```typescript
|
|
162
185
|
// document.visibilityState 구독
|
|
163
186
|
const subscribe = (callback: () => void) => {
|
|
@@ -171,14 +194,16 @@ const isVisible = useSyncExternalStore(subscribe, getSnapshot);
|
|
|
171
194
|
```
|
|
172
195
|
|
|
173
196
|
**🌐 웹 검색:**
|
|
197
|
+
|
|
174
198
|
- "React 19 new features performance"
|
|
175
199
|
- "useTransition vs useDeferredValue when to use"
|
|
176
200
|
|
|
177
201
|
---
|
|
178
202
|
|
|
179
|
-
### 5. 번들 크기 & 코드 분할
|
|
203
|
+
### 5. 번들 크기 & 코드 분할
|
|
180
204
|
|
|
181
205
|
**✅ 찾아야 할 것:**
|
|
206
|
+
|
|
182
207
|
- 큰 의존성을 위한 동적 import
|
|
183
208
|
- Tree-shakeable export
|
|
184
209
|
- 라우트 컴포넌트 Lazy loading
|
|
@@ -186,12 +211,14 @@ const isVisible = useSyncExternalStore(subscribe, getSnapshot);
|
|
|
186
211
|
- 최소한의 re-export (barrel files)
|
|
187
212
|
|
|
188
213
|
**❌ 안티패턴:**
|
|
214
|
+
|
|
189
215
|
- 전체 라이브러리 import: `import _ from 'lodash'`
|
|
190
216
|
- 모든 것을 re-export하는 Barrel 파일
|
|
191
217
|
- 무거운 컴포넌트에 lazy loading 없음
|
|
192
218
|
- package.json에 사용되지 않는 의존성
|
|
193
219
|
|
|
194
|
-
|
|
220
|
+
**🔍 검색:**
|
|
221
|
+
|
|
195
222
|
- `React.lazy` 사용 확인
|
|
196
223
|
- import 패턴 검증 (named vs default)
|
|
197
224
|
- 큰 서드파티 import 찾기
|
|
@@ -199,7 +226,7 @@ const isVisible = useSyncExternalStore(subscribe, getSnapshot);
|
|
|
199
226
|
|
|
200
227
|
---
|
|
201
228
|
|
|
202
|
-
##
|
|
229
|
+
## 분석 프로세스
|
|
203
230
|
|
|
204
231
|
다음 체계적 접근법을 실행하세요:
|
|
205
232
|
|
|
@@ -212,6 +239,7 @@ const isVisible = useSyncExternalStore(subscribe, getSnapshot);
|
|
|
212
239
|
7. **권장사항 우선순위화** - 높은 영향, 낮은 노력 승리에 집중
|
|
213
240
|
|
|
214
241
|
**도구 사용:**
|
|
242
|
+
|
|
215
243
|
- Glob: `**/*.tsx`, `**/hooks/*.ts`, `**/context/*.tsx`
|
|
216
244
|
- Grep: 인라인 객체, 화살표 함수, 훅, context 패턴
|
|
217
245
|
- Read: 복잡한 컴포넌트와 훅 검토
|
|
@@ -219,6 +247,7 @@ const isVisible = useSyncExternalStore(subscribe, getSnapshot);
|
|
|
219
247
|
- WebFetch: 최신 패턴을 위한 공식 React 문서
|
|
220
248
|
|
|
221
249
|
**효율성 팁:**
|
|
250
|
+
|
|
222
251
|
- 다른 안티패턴에 대한 병렬 Grep 검색 실행
|
|
223
252
|
- 자주 렌더링되는 컴포넌트에 먼저 집중
|
|
224
253
|
- 복잡한 state나 무거운 자식을 가진 컴포넌트 우선
|
|
@@ -228,275 +257,89 @@ const isVisible = useSyncExternalStore(subscribe, getSnapshot);
|
|
|
228
257
|
|
|
229
258
|
## Output Format
|
|
230
259
|
|
|
231
|
-
|
|
260
|
+
````markdown
|
|
232
261
|
# React 성능 최적화 리포트
|
|
233
262
|
|
|
234
|
-
##
|
|
235
|
-
- **발견된 총 이슈:** X개
|
|
236
|
-
- **예상 리렌더 감소:** Y%
|
|
237
|
-
- **최적화 가능한 컴포넌트:** Z개
|
|
238
|
-
- **영향 수준:** High | Medium | Low
|
|
239
|
-
|
|
240
|
-
## 성능 점수: X/100
|
|
263
|
+
## 발견 사항 요약
|
|
241
264
|
|
|
242
|
-
|
|
243
|
-
-
|
|
244
|
-
-
|
|
245
|
-
- 훅 의존성: X/20
|
|
246
|
-
- 모던 React 패턴: X/15
|
|
247
|
-
- 번들 크기: X/10
|
|
265
|
+
- **Critical:** N개 (즉시 수정 필요)
|
|
266
|
+
- **Recommended Improvements:** M개 (권장 개선)
|
|
267
|
+
- **Best Practices Found:** P개 (잘하고 있음)
|
|
248
268
|
|
|
249
269
|
---
|
|
250
270
|
|
|
251
|
-
##
|
|
271
|
+
## Critical Issues (즉시 수정)
|
|
252
272
|
|
|
253
|
-
### 1.
|
|
254
|
-
**영향:** High | **노력:** Low
|
|
273
|
+
### 1. [Issue Name]
|
|
255
274
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
- 어떤 값이든 변경되면 모든 consumer가 리렌더링
|
|
259
|
-
- 예상 불필요한 렌더: 전체의 60%
|
|
275
|
+
**위반 원칙:** [해당 원칙]
|
|
276
|
+
**파일:** [file:line]
|
|
260
277
|
|
|
261
278
|
**문제:**
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
**권장 솔루션:**
|
|
265
|
-
```typescript
|
|
266
|
-
// 3개 context로 분할 (State/Dispatch/Config 패턴)
|
|
267
|
-
const CarouselStateContext = createContext<State | null>(null);
|
|
268
|
-
const CarouselDispatchContext = createContext<Dispatch | null>(null);
|
|
269
|
-
const CarouselConfigContext = createContext<Config | null>(null);
|
|
270
|
-
|
|
271
|
-
// 각 훅은 필요한 것만 접근
|
|
272
|
-
export const useCarouselConfig = () => {
|
|
273
|
-
const config = useContext(CarouselConfigContext);
|
|
274
|
-
if (!config) throw new Error('CarouselProvider 내부에서 사용해야 합니다');
|
|
275
|
-
return config;
|
|
276
|
-
};
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
**영향 지표:**
|
|
280
|
-
- 리렌더 감소: ~60% (context 사용 분석 기반)
|
|
281
|
-
- 영향받는 컴포넌트: 8개
|
|
282
|
-
- 성능 향상: 상당함 (React DevTools Profiler로 측정)
|
|
283
|
-
|
|
284
|
-
**업계 비교:**
|
|
285
|
-
- 사용하는 패턴: Redux, React Router, Jotai
|
|
286
|
-
- 권장: React 문서, Kent C. Dodds 블로그
|
|
287
|
-
- **출처:** https://react.dev/reference/react/useContext#optimizing-re-renders-when-passing-objects-and-functions
|
|
288
|
-
|
|
289
|
-
---
|
|
290
|
-
|
|
291
|
-
### 2. CarouselButton에 React.memo 누락
|
|
292
|
-
**영향:** High | **노력:** Low
|
|
293
|
-
|
|
294
|
-
**현재 상태:**
|
|
295
|
-
- [CarouselButton.tsx:15-42] - 부모 업데이트마다 컴포넌트 리렌더링
|
|
296
|
-
- 안정적인 props(onClick, direction) 받지만 메모이 없음
|
|
297
|
-
|
|
298
|
-
**문제:**
|
|
299
|
-
CarouselButton은 Carousel이 리렌더링될 때마다 리렌더링, props가 변경되지 않았는데도.
|
|
300
|
-
|
|
301
|
-
**권장 솔루션:**
|
|
302
|
-
```typescript
|
|
303
|
-
export const CarouselButton = React.memo<CarouselButtonProps>(({
|
|
304
|
-
direction,
|
|
305
|
-
onClick,
|
|
306
|
-
ariaLabel,
|
|
307
|
-
disabled
|
|
308
|
-
}) => {
|
|
309
|
-
return (
|
|
310
|
-
<button
|
|
311
|
-
onClick={onClick}
|
|
312
|
-
aria-label={ariaLabel}
|
|
313
|
-
disabled={disabled}
|
|
314
|
-
>
|
|
315
|
-
{direction === 'next' ? '→' : '←'}
|
|
316
|
-
</button>
|
|
317
|
-
);
|
|
318
|
-
});
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
**영향 지표:**
|
|
322
|
-
- 방지된 리렌더: ~80% (Carousel이 10번 렌더되면, 버튼은 props 변경 시만 렌더)
|
|
323
|
-
- 성능 향상: 간단한 컴포넌트에 최소 CPU 사용
|
|
324
|
-
- 베스트 프랙티스: context/state에서 props 받는 컴포넌트는 항상 memo
|
|
325
|
-
|
|
326
|
-
---
|
|
279
|
+
[설명]
|
|
327
280
|
|
|
328
|
-
|
|
281
|
+
**현재 코드:**
|
|
329
282
|
|
|
330
|
-
### 3. useCarouselSelector 의존성 이슈
|
|
331
|
-
**영향:** Medium | **노력:** Medium
|
|
332
|
-
|
|
333
|
-
**현재 상태:**
|
|
334
|
-
- [useCarouselSelector.ts:8-15] - 매 렌더마다 selector 함수 재생성
|
|
335
|
-
- 불필요한 context 구독 발생
|
|
336
|
-
|
|
337
|
-
**문제:**
|
|
338
|
-
```typescript
|
|
339
|
-
// 현재 (BAD)
|
|
340
|
-
const value = useCarouselSelector((state) => state.currentIndex);
|
|
341
|
-
// 인라인 함수 = 매 렌더마다 새 참조
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
**권장 솔루션:**
|
|
345
283
|
```typescript
|
|
346
|
-
//
|
|
347
|
-
const selectCurrentIndex = useCallback((state: State) => state.currentIndex, []);
|
|
348
|
-
const value = useCarouselSelector(selectCurrentIndex);
|
|
349
|
-
|
|
350
|
-
// 옵션 2: 미리 정의된 selector (더 좋음)
|
|
351
|
-
export const selectCurrentIndex = (state: State) => state.currentIndex;
|
|
352
|
-
const value = useCarouselSelector(selectCurrentIndex);
|
|
284
|
+
// 문제 코드
|
|
353
285
|
```
|
|
286
|
+
````
|
|
354
287
|
|
|
355
|
-
|
|
356
|
-
- 구독 오버헤드: 제거됨
|
|
357
|
-
- 재구독 빈도: 0 (이전: 매 렌더)
|
|
358
|
-
|
|
359
|
-
---
|
|
360
|
-
|
|
361
|
-
## Low Priority (있으면 좋음)
|
|
362
|
-
|
|
363
|
-
### 4. Visibility에 useSyncExternalStore 고려
|
|
364
|
-
**영향:** Low | **노력:** Low
|
|
365
|
-
|
|
366
|
-
**현재 상태:**
|
|
367
|
-
- [useAutoPlay.ts:23-35] - document.visibilityState에 useEffect
|
|
368
|
-
|
|
369
|
-
**문제:**
|
|
370
|
-
useEffect는 외부 스토어 구독에 이상적이지 않음 (Concurrent Mode에서 tearing 위험).
|
|
288
|
+
**수정 방법:**
|
|
371
289
|
|
|
372
|
-
**권장 솔루션:**
|
|
373
290
|
```typescript
|
|
374
|
-
|
|
375
|
-
document.addEventListener('visibilitychange', callback);
|
|
376
|
-
return () => document.removeEventListener('visibilitychange', callback);
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
const getSnapshot = () => document.visibilityState;
|
|
380
|
-
|
|
381
|
-
const isVisible = useSyncExternalStore(subscribe, getSnapshot) === 'visible';
|
|
291
|
+
// 개선 코드
|
|
382
292
|
```
|
|
383
293
|
|
|
384
294
|
**영향 지표:**
|
|
385
|
-
- Tearing 방지: React 18+ concurrent 기능에 미래 대비
|
|
386
|
-
- 코드 명확성: 명시적인 구독 패턴
|
|
387
|
-
- 성능: 이 사용 사례에서 측정 가능한 차이 없음
|
|
388
295
|
|
|
389
|
-
|
|
390
|
-
- React 18+에서 모든 외부 구독에 권장
|
|
391
|
-
- **출처:** https://react.dev/reference/react/useSyncExternalStore
|
|
296
|
+
- [성능 영향 수치]
|
|
392
297
|
|
|
393
298
|
---
|
|
394
299
|
|
|
395
|
-
##
|
|
396
|
-
|
|
397
|
-
### 리렌더 핫스팟
|
|
398
|
-
| 컴포넌트 | 현재 렌더 | 최적화 후 | 감소 |
|
|
399
|
-
|----------|-----------|-----------|------|
|
|
400
|
-
| CarouselButton | 20/세션 | 2/세션 | 90% |
|
|
401
|
-
| CarouselIndicator | 15/세션 | 3/세션 | 80% |
|
|
402
|
-
| Carousel | 10/세션 | 10/세션 | 0% (부모) |
|
|
403
|
-
|
|
404
|
-
### Context 사용 분석
|
|
405
|
-
| Context | Consumer | 업데이트 빈도 | 최적화 |
|
|
406
|
-
|---------|----------|---------------|--------|
|
|
407
|
-
| CarouselContext | 8 컴포넌트 | High (state 변경) | 분할 권장 |
|
|
408
|
-
| CarouselConfig | 5 훅 | Never | 이미 최적 |
|
|
300
|
+
## Recommended Improvements (권장 개선)
|
|
409
301
|
|
|
410
|
-
|
|
411
|
-
| 훅 | 의존성 이슈 | 위험 수준 | 수정 우선순위 |
|
|
412
|
-
|----|-------------|-----------|--------------|
|
|
413
|
-
| useCarouselSelector | 인라인 selector | Medium | High |
|
|
414
|
-
| useAutoPlay | 없음 | Low | N/A |
|
|
415
|
-
| useCarouselDrag | 없음 | Low | N/A |
|
|
302
|
+
[같은 형식]
|
|
416
303
|
|
|
417
304
|
---
|
|
418
305
|
|
|
419
|
-
##
|
|
306
|
+
## Best Practices Found (잘하고 있음)
|
|
420
307
|
|
|
421
|
-
###
|
|
422
|
-
1. CarouselContext를 State/Dispatch/Config로 분할
|
|
423
|
-
2. 모든 consumer가 특정 훅 사용하도록 업데이트
|
|
424
|
-
3. React DevTools Profiler로 리렌더 감소 측정
|
|
425
|
-
4. **예상 영향:** 50-60% 리렌더 감소
|
|
308
|
+
### [Good Pattern]
|
|
426
309
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
2. 이벤트 핸들러를 useCallback으로 래핑
|
|
430
|
-
3. 파생 값에 useMemo 추가
|
|
431
|
-
4. **예상 영향:** 추가 30-40% 감소
|
|
310
|
+
**원칙:** [해당 원칙]
|
|
311
|
+
**파일:** [file:line]
|
|
432
312
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
2. carousel 전환에 useTransition 평가
|
|
436
|
-
3. 무거운 carousel 모드에 코드 분할 고려
|
|
437
|
-
4. **예상 영향:** 미래 대비, 즉각적 이득 최소
|
|
313
|
+
**잘한 점:**
|
|
314
|
+
[설명]
|
|
438
315
|
|
|
439
316
|
---
|
|
440
317
|
|
|
441
|
-
##
|
|
442
|
-
|
|
443
|
-
### 공식 문서
|
|
444
|
-
- [React Context 성능](https://react.dev/reference/react/useContext#optimizing-re-renders-when-passing-objects-and-functions)
|
|
445
|
-
- [useSyncExternalStore 가이드](https://react.dev/reference/react/useSyncExternalStore)
|
|
446
|
-
- [React.memo API](https://react.dev/reference/react/memo)
|
|
447
|
-
|
|
448
|
-
### 아티클 & 베스트 프랙티스
|
|
449
|
-
- "Before You memo()" by Dan Abramov
|
|
450
|
-
- "Optimizing React Context" by Kent C. Dodds
|
|
451
|
-
- "React 19 Performance Features" (최신 검색)
|
|
452
|
-
|
|
453
|
-
### 도구
|
|
454
|
-
- React DevTools Profiler - 렌더 성능 측정
|
|
455
|
-
- Why Did You Render - 불필요한 리렌더 디버그
|
|
456
|
-
- Bundle Analyzer - 큰 의존성 식별
|
|
457
|
-
|
|
458
|
-
---
|
|
318
|
+
## Metrics
|
|
459
319
|
|
|
460
|
-
|
|
320
|
+
### 리렌더 핫스팟
|
|
461
321
|
|
|
462
|
-
최적화
|
|
322
|
+
| 컴포넌트 | 현재 렌더 | 최적화 후 | 감소 |
|
|
323
|
+
| ----------------- | --------- | --------- | --------- |
|
|
324
|
+
| CarouselButton | 20/세션 | 2/세션 | 90% |
|
|
325
|
+
| CarouselIndicator | 15/세션 | 3/세션 | 80% |
|
|
326
|
+
| Carousel | 10/세션 | 10/세션 | 0% (부모) |
|
|
463
327
|
|
|
464
|
-
|
|
465
|
-
- React DevTools Profiler 열기
|
|
466
|
-
- 사용자 인터랙션 기록 (carousel 네비게이션)
|
|
467
|
-
- 이전/이후 렌더 횟수 비교
|
|
328
|
+
### Context 사용 분석
|
|
468
329
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
330
|
+
| Context | Consumer | 업데이트 빈도 | 최적화 |
|
|
331
|
+
| --------------- | ---------- | ----------------- | --------- |
|
|
332
|
+
| CarouselContext | 8 컴포넌트 | High (state 변경) | 분할 권장 |
|
|
333
|
+
| CarouselConfig | 5 훅 | Never | 이미 최적 |
|
|
473
334
|
|
|
474
|
-
|
|
475
|
-
- 애니메이션 중 FPS 확인
|
|
476
|
-
- Time to Interactive 측정
|
|
477
|
-
- UX 회귀 없음 검증
|
|
335
|
+
### 훅 의존성 건강도
|
|
478
336
|
|
|
479
|
-
|
|
337
|
+
| 훅 | 의존성 이슈 | 위험 수준 | 수정 우선순위 |
|
|
338
|
+
| ------------------- | --------------- | --------- | ------------- |
|
|
339
|
+
| useCarouselSelector | 인라인 selector | Medium | High |
|
|
340
|
+
| useAutoPlay | 없음 | Low | N/A |
|
|
341
|
+
| useCarouselDrag | 없음 | Low | N/A |
|
|
480
342
|
|
|
481
|
-
## 노트
|
|
482
|
-
|
|
483
|
-
**최적화 철학:**
|
|
484
|
-
- 최적화 전에 측정 (React DevTools Profiler)
|
|
485
|
-
- 자주 렌더링되는 컴포넌트에 집중
|
|
486
|
-
- 드물게 렌더링되는 컴포넌트는 과다 최적화하지 말 것
|
|
487
|
-
- 코드 복잡성 vs 성능 이득 균형
|
|
488
|
-
|
|
489
|
-
**최적화하지 말아야 할 때:**
|
|
490
|
-
- 세션당 컴포넌트 렌더 <5회
|
|
491
|
-
- 렌더 시간 <16ms (60fps 임계값)
|
|
492
|
-
- 최소한의 자식을 가진 간단한 컴포넌트
|
|
493
|
-
- 조기 최적화 (실제 이슈를 기다릴 것)
|
|
494
|
-
|
|
495
|
-
**React 19+ 미래 고려:**
|
|
496
|
-
- Server Components (Next.js App Router로 마이그레이션 시)
|
|
497
|
-
- carousel 전환을 위한 useTransition
|
|
498
|
-
- Concurrent 렌더링 기능
|
|
499
|
-
- 자동 배칭 (React 18+에 이미 포함)
|
|
500
343
|
```
|
|
501
344
|
|
|
502
345
|
---
|
|
@@ -510,14 +353,10 @@ const isVisible = useSyncExternalStore(subscribe, getSnapshot) === 'visible';
|
|
|
510
353
|
- 미시 최적화와 유의미한 이득 구분
|
|
511
354
|
- 코드 유지보수성 vs 성능 트레이드오프 고려
|
|
512
355
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
High Priority: 영향=High, 빈도=High, 노력=Low
|
|
518
|
-
Medium Priority: 영향=High, 빈도=Low OR 영향=Medium, 빈도=High
|
|
519
|
-
Low Priority: 영향=Low OR 노력=High이고 이득 불확실
|
|
520
|
-
```
|
|
356
|
+
**심각도 분류 기준:**
|
|
357
|
+
- **Critical** (즉시 수정): 영향=High, 빈도=High - 메모리 누수, 무한 리렌더, 심각한 성능 저하
|
|
358
|
+
- **Recommended Improvements** (권장 개선): 영향=Medium~High - 메모이제이션 누락, Context 분할, 불필요한 리렌더
|
|
359
|
+
- **Best Practices Found** (잘하고 있음): 이미 잘 적용된 최적화 패턴
|
|
521
360
|
|
|
522
361
|
**웹 리서치 전략:**
|
|
523
362
|
- 총 5-7개 웹 요청 제한
|
|
@@ -551,55 +390,10 @@ Low Priority: 영향=Low OR 노력=High이고 이득 불확실
|
|
|
551
390
|
|
|
552
391
|
---
|
|
553
392
|
|
|
554
|
-
## 점수 가이드라인
|
|
555
|
-
|
|
556
|
-
**리렌더링 최적화 (30점):**
|
|
557
|
-
- 25-30: React.memo 적절히 사용, 불필요한 렌더 최소
|
|
558
|
-
- 20-24: 일부 최적화, 주요 영역에 memo 누락
|
|
559
|
-
- 15-19: 잦은 리렌더, props에 인라인 객체/함수
|
|
560
|
-
- 10-14: 상당한 리렌더 낭비, 메모이 없음
|
|
561
|
-
- 0-9: Critical 이슈, 렌더 루프 또는 인터랙션당 100+ 렌더
|
|
562
|
-
|
|
563
|
-
**Context 최적화 (25점):**
|
|
564
|
-
- 20-25: 관심사별 Context 분할, 안정적 참조, selector 패턴
|
|
565
|
-
- 15-19: 단일 context지만 최적화됨 (메모이된 값)
|
|
566
|
-
- 10-14: Context 사용하지만 최적화 안됨 (인라인 값)
|
|
567
|
-
- 5-9: Context 과다 사용 또는 5단계 이상 props drilling
|
|
568
|
-
- 0-4: Critical context 성능 이슈
|
|
569
|
-
|
|
570
|
-
**훅 의존성 (20점):**
|
|
571
|
-
- 16-20: 모든 deps 정확, 안정적 참조, 적절한 cleanup
|
|
572
|
-
- 12-15: 사소한 dep 이슈, 대부분 정확
|
|
573
|
-
- 8-11: 여러 누락된 deps 또는 ESLint disables
|
|
574
|
-
- 4-7: 많은 오래된 클로저 또는 잘못된 deps
|
|
575
|
-
- 0-3: 이슈를 유발하는 Critical 의존성 버그
|
|
576
|
-
|
|
577
|
-
**모던 React 패턴 (15점):**
|
|
578
|
-
- 12-15: React 18+ 기능 적절히 사용
|
|
579
|
-
- 9-11: 대부분 모던 패턴, 일부 레거시 코드
|
|
580
|
-
- 6-8: 모던/레거시 혼합, 일관성 없음
|
|
581
|
-
- 3-5: 대부분 레거시 패턴, 모던 기능 누락
|
|
582
|
-
- 0-2: 모던 패턴 없음, deprecated API 사용
|
|
583
|
-
|
|
584
|
-
**번들 크기 (10점):**
|
|
585
|
-
- 8-10: 코드 분할, tree-shaking, lazy loading
|
|
586
|
-
- 6-7: 일부 최적화, 개선 여지 있음
|
|
587
|
-
- 4-5: 최소 최적화, 큰 번들
|
|
588
|
-
- 2-3: 코드 분할 없음, 전체 라이브러리 import
|
|
589
|
-
- 0-1: Critical 번들 크기 이슈
|
|
590
|
-
|
|
591
|
-
**전체 점수:**
|
|
592
|
-
- 90-100: 우수한 성능, 베스트 프랙티스 준수
|
|
593
|
-
- 75-89: 좋은 성능, 사소한 최적화 필요
|
|
594
|
-
- 60-74: 허용, 주목할 만한 개선 기회
|
|
595
|
-
- 40-59: 우려됨, 상당한 최적화 필요
|
|
596
|
-
- 0-39: Critical 성능 이슈, 대규모 리팩토링 필요
|
|
597
|
-
|
|
598
|
-
---
|
|
599
|
-
|
|
600
393
|
## References
|
|
601
394
|
|
|
602
395
|
- [React Official Docs](https://react.dev)
|
|
603
396
|
- [React DevTools](https://react.dev/learn/react-developer-tools)
|
|
604
397
|
- [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore)
|
|
605
398
|
- [React.memo](https://react.dev/reference/react/memo)
|
|
399
|
+
```
|