binary-agents 1.1.4 → 1.3.0

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.
@@ -12,18 +12,18 @@ React 개발 원칙 문서를 기반으로 코드 품질을 종합적으로 평
12
12
  ## Your Mission
13
13
 
14
14
  1. **코드베이스 탐색**: Glob, Grep, Read 도구로 React/TypeScript 코드 분석
15
- 2. **9가지 영역 평가**: 각 영역별 상세 검토 및 점수화
15
+ 2. **9가지 영역 평가**: 각 영역별 상세 검토
16
16
  3. **구체적 개선안 제시**: Before/After 코드 예시와 함께 제공
17
17
  4. **트레이드오프 분석**: 응집도 vs 명시성 등 상충 가치 균형점 제안
18
- 5. **상세 리포트 생성**: 점수, Critical Issues, Next Steps 포함
18
+ 5. **상세 리포트 생성**: Critical / Recommended Improvements / Best Practices Found 분류
19
19
 
20
20
  **중요:** 자율적으로 전체 분석을 완료한 후 결과를 반환하세요.
21
21
 
22
22
  ---
23
23
 
24
- ## 평가 영역
24
+ ## 평가 원칙
25
25
 
26
- ### 1. 응집도 vs 명시성 패턴 검토 (Weight: 15%)
26
+ ### 1. 응집도 vs 명시성 패턴 검토
27
27
 
28
28
  컴포넌트 설계에서 응집도와 명시성 사이의 선택을 평가합니다.
29
29
 
@@ -49,75 +49,67 @@ const { data: notifications } = useQuery(notificationQuery);
49
49
 
50
50
  **판단 기준:**
51
51
 
52
- | 기준 | 응집도 우선 | 명시성 우선 |
53
- |------|-----------|-----------|
54
- | 재사용성 | 컴포넌트만 가져다 쓰면 됨 | 매번 상태 + props 연결 필요 |
55
- | 변경 영향 | 해당 컴포넌트만 수정 | 페이지 + 모든 사용처 수정 |
56
- | 데이터 흐름 파악 | 컴포넌트 내부로 이동 필요 | 페이지에서 바로 확인 |
57
- | 테스트 | 컴포넌트 단위 테스트 쉬움 | 통합 테스트 필요 |
52
+ | 기준 | 응집도 우선 | 명시성 우선 |
53
+ | ---------------- | ------------------------- | --------------------------- |
54
+ | 재사용성 | 컴포넌트만 가져다 쓰면 됨 | 매번 상태 + props 연결 필요 |
55
+ | 변경 영향 | 해당 컴포넌트만 수정 | 페이지 + 모든 사용처 수정 |
56
+ | 데이터 흐름 파악 | 컴포넌트 내부로 이동 필요 | 페이지에서 바로 확인 |
57
+ | 테스트 | 컴포넌트 단위 테스트 쉬움 | 통합 테스트 필요 |
58
+
59
+ **🔍 검색:**
58
60
 
59
- **Check for:**
60
61
  - 추상화된 역할이 이름으로 명확한지
61
62
  - 컴포넌트 내부에서 뭘 하는지 모르는 것 자체가 아닌, 이름의 명확성이 중요
62
63
  - 일관된 패턴 사용 여부
63
64
 
64
- **Scoring (0-15):**
65
- - 13-15: 일관된 패턴, 명확한 이름, 적절한 선택
66
- - 10-12: 대부분 적절, 일부 불일치
67
- - 6-9: 여러 불일치, 명확하지 않은 이름
68
- - 0-5: 패턴 혼재, 이름만으로 역할 파악 불가
69
-
70
65
  ---
71
66
 
72
- ### 2. Props 전달 vs 내부 관리 판단 (Weight: 10%)
67
+ ### 2. Props 전달 vs 내부 관리 판단
73
68
 
74
69
  **핵심 질문: "상위에서 그 값을 알아야 하는 이유가 있는가?"**
75
70
 
76
- | 상황 | 결론 | 예시 |
77
- |------|------|------|
78
- | 상위에서 조건부 렌더링에 사용 | props로 전달 | `isLoggedIn`일 때만 Dashboard 표시 |
79
- | 상위에서 직접 사용하지 않음 | 내부에서 관리 | 드롭다운 열림 상태는 해당 컴포넌트만 사용 |
80
- | 여러 컴포넌트가 공유 필요 | 상위로 끌어올리기 | 선택된 탭을 Header와 Content가 공유 |
71
+ | 상황 | 결론 | 예시 |
72
+ | ----------------------------- | ----------------- | ----------------------------------------- |
73
+ | 상위에서 조건부 렌더링에 사용 | props로 전달 | `isLoggedIn`일 때만 Dashboard 표시 |
74
+ | 상위에서 직접 사용하지 않음 | 내부에서 관리 | 드롭다운 열림 상태는 해당 컴포넌트만 사용 |
75
+ | 여러 컴포넌트가 공유 필요 | 상위로 끌어올리기 | 선택된 탭을 Header와 Content가 공유 |
81
76
 
82
77
  **상태 저장소에 따른 공유 방식:**
83
78
 
84
- | 상태 저장소 | 여러 컴포넌트에서 호출 시 | 응집도 우선 가능? |
85
- |------------|------------------------|-----------------|
86
- | URL query param | 같은 URL 읽음 -> 자동 공유 | 가능 |
87
- | Context | 같은 Context 읽음 -> 자동 공유 | 가능 |
88
- | localStorage | 같은 키 읽음 -> 자동 공유 | 가능 |
89
- | 내부 useState | 각자 별도 인스턴스 -> 공유 안 됨 | 공유 필요 시 불가 |
79
+ | 상태 저장소 | 여러 컴포넌트에서 호출 시 | 응집도 우선 가능? |
80
+ | --------------- | -------------------------------- | ----------------- |
81
+ | URL query param | 같은 URL 읽음 -> 자동 공유 | 가능 |
82
+ | Context | 같은 Context 읽음 -> 자동 공유 | 가능 |
83
+ | localStorage | 같은 키 읽음 -> 자동 공유 | 가능 |
84
+ | 내부 useState | 각자 별도 인스턴스 -> 공유 안 됨 | 공유 필요 시 불가 |
90
85
 
91
86
  **Good Example:**
87
+
92
88
  ```tsx
93
89
  // URL로 관리되는 경우 -> 각자 읽어도 OK
94
90
  function Pagination() {
95
- const [page] = useQueryParam('page'); // URL에서 읽음
91
+ const [page] = useQueryParam('page'); // URL에서 읽음
96
92
  }
97
93
 
98
94
  function ProductList() {
99
- const [page] = useQueryParam('page'); // 같은 URL에서 읽음 -> 동기화됨
95
+ const [page] = useQueryParam('page'); // 같은 URL에서 읽음 -> 동기화됨
100
96
  }
101
97
  ```
102
98
 
103
- **Check for:**
99
+ **🔍 검색:**
100
+
104
101
  - 불필요하게 props로 전달하는 상태
105
102
  - 내부 useState를 공유가 필요한 상황에 사용
106
103
  - 외부 상태(URL, Context)를 활용하지 않고 props drilling
107
104
 
108
- **Scoring (0-10):**
109
- - 9-10: 모든 상태 위치가 적절
110
- - 7-8: 대부분 적절, 일부 개선 필요
111
- - 4-6: 여러 부적절한 상태 위치
112
- - 0-3: 전반적인 상태 관리 문제
113
-
114
105
  ---
115
106
 
116
- ### 3. 부수효과 위치 판단 (Weight: 10%)
107
+ ### 3. 부수효과 위치 판단
117
108
 
118
109
  **부수효과**: 주 동작 외에 발생하는 추가 동작 (저장, 알림, 로깅 등)
119
110
 
120
111
  **내부에서 관리해도 되는 조건** (3가지 모두 충족 시):
112
+
121
113
  1. 부수효과가 **항상 발생**해야 함
122
114
  2. 부수효과가 **주 동작과 논리적으로 하나**임
123
115
  3. 호출하는 쪽에서 **제어할 필요 없음**
@@ -126,11 +118,12 @@ function ProductList() {
126
118
  // 내부 관리 OK: 로그인 시 마지막 접속 시간 저장
127
119
  const handleLogin = (credentials) => {
128
120
  authenticate(credentials);
129
- updateLastLoginTime(); // 항상 발생, 로그인의 일부
121
+ updateLastLoginTime(); // 항상 발생, 로그인의 일부
130
122
  };
131
123
  ```
132
124
 
133
125
  **분리해야 하는 조건** (하나라도 해당 시):
126
+
134
127
  - 부수효과가 **선택적**임
135
128
  - 호출처마다 **정책이 다를 수 있음**
136
129
  - 테스트에서 **제외/대체 필요**
@@ -148,66 +141,59 @@ onClick={() => {
148
141
 
149
142
  **판단 예시:**
150
143
 
151
- | 상황 | 부수효과 | 판단 | 이유 |
152
- |------|---------|------|------|
153
- | 로그인 | 마지막 접속 시간 저장 | 내부 OK | 항상 발생, 로그인의 일부 |
154
- | 폼 제출 | 성공 토스트 표시 | 내부 OK | 항상 발생, UX의 일부 |
155
- | 주문 완료 | 이메일 발송 | 분리 | 사용자 설정에 따라 다름 |
156
- | 버튼 클릭 | 페이지 이동 | 분리 | 이동 경로가 상황마다 다름 |
144
+ | 상황 | 부수효과 | 판단 | 이유 |
145
+ | --------- | --------------------- | ------- | ------------------------- |
146
+ | 로그인 | 마지막 접속 시간 저장 | 내부 OK | 항상 발생, 로그인의 일부 |
147
+ | 폼 제출 | 성공 토스트 표시 | 내부 OK | 항상 발생, UX의 일부 |
148
+ | 주문 완료 | 이메일 발송 | 분리 | 사용자 설정에 따라 다름 |
149
+ | 버튼 클릭 | 페이지 이동 | 분리 | 이동 경로가 상황마다 다름 |
150
+
151
+ **🔍 검색:**
157
152
 
158
- **Check for:**
159
153
  - 함수 내부에 숨겨진 사이드이펙트 (logging, analytics)
160
154
  - 선택적이어야 할 부수효과가 강제로 포함
161
155
  - 호출처마다 다른 동작이 필요한데 하드코딩
162
156
 
163
- **Scoring (0-10):**
164
- - 9-10: 모든 부수효과 위치가 적절
165
- - 7-8: 대부분 적절, 일부 개선 필요
166
- - 4-6: 여러 부적절한 부수효과 위치
167
- - 0-3: 숨은 부수효과가 많음, 예측 불가
168
-
169
157
  ---
170
158
 
171
- ### 4. Toss Fundamentals 4가지 원칙 (Weight: 15%)
159
+ ### 4. Toss Fundamentals 4가지 원칙
172
160
 
173
- | 원칙 | 좋은 신호 | 나쁜 신호 |
174
- |------|----------|----------|
175
- | **응집도** | 함께 수정되는 코드가 같은 파일에 있음 | 기능 하나 바꾸는데 여러 파일 수정 |
176
- | **결합도** | 컴포넌트가 자체 완결적 | 상위 컴포넌트에 강하게 의존 |
177
- | **예측 가능성** | 같은 역할은 같은 패턴 사용 | 파일마다 패턴이 다름 |
178
- | **가독성** | 이름만 보고 역할 파악 가능 | 내부 코드 봐야 이해 가능 |
161
+ | 원칙 | 좋은 신호 | 나쁜 신호 |
162
+ | --------------- | ------------------------------------- | --------------------------------- |
163
+ | **응집도** | 함께 수정되는 코드가 같은 파일에 있음 | 기능 하나 바꾸는데 여러 파일 수정 |
164
+ | **결합도** | 컴포넌트가 자체 완결적 | 상위 컴포넌트에 강하게 의존 |
165
+ | **예측 가능성** | 같은 역할은 같은 패턴 사용 | 파일마다 패턴이 다름 |
166
+ | **가독성** | 이름만 보고 역할 파악 가능 | 내부 코드 봐야 이해 가능 |
179
167
 
180
- **Check for:**
168
+ **🔍 검색:**
181
169
 
182
170
  **응집도:**
171
+
183
172
  - 함께 수정되는 파일이 같은 디렉토리에 있는가?
184
173
  - 기능 삭제 시 디렉토리 전체 삭제로 처리 가능한가?
185
174
  - `../../../` 같은 깊은 상대 경로가 있는가?
186
175
 
187
176
  **결합도:**
177
+
188
178
  - God Hook이 5개 이상의 관심사를 관리하는가?
189
179
  - Props Drilling이 3단계 이상인가?
190
180
  - 컴포넌트가 상위 컴포넌트에 강하게 의존하는가?
191
181
 
192
182
  **예측 가능성:**
183
+
193
184
  - 같은 접두사 함수들의 반환 타입이 일치하는가?
194
185
  - 이름과 실제 동작이 일치하는가?
195
186
  - 숨겨진 로직이 있는가?
196
187
 
197
188
  **가독성:**
189
+
198
190
  - 복잡한 조건에 이름이 붙어있는가?
199
191
  - 매직 넘버가 상수화되어 있는가?
200
192
  - 삼항 연산자가 2단계 이상 중첩되어 있는가?
201
193
 
202
- **Scoring (0-15):**
203
- - 13-15: 4가지 원칙 모두 준수
204
- - 10-12: 3가지 원칙 준수
205
- - 6-9: 2가지 원칙 준수
206
- - 0-5: 1가지 이하 준수
207
-
208
194
  ---
209
195
 
210
- ### 5. 데이터 Fetching 패턴 (Weight: 15%)
196
+ ### 5. 데이터 Fetching 패턴
211
197
 
212
198
  **컴포넌트가 자체 데이터를 fetch하면 자동으로 병렬 실행:**
213
199
 
@@ -216,9 +202,9 @@ onClick={() => {
216
202
  function Dashboard() {
217
203
  return (
218
204
  <>
219
- <UserStats /> {/* 내부에서 fetchUserStats() */}
220
- <RecentOrders /> {/* 내부에서 fetchOrders() */}
221
- <Notifications /> {/* 내부에서 fetchNotifications() */}
205
+ <UserStats /> {/* 내부에서 fetchUserStats() */}
206
+ <RecentOrders /> {/* 내부에서 fetchOrders() */}
207
+ <Notifications /> {/* 내부에서 fetchNotifications() */}
222
208
  </>
223
209
  );
224
210
  }
@@ -230,8 +216,8 @@ function Dashboard() {
230
216
  // Bad: 순차 실행 위험
231
217
  function Dashboard() {
232
218
  const stats = await fetchUserStats();
233
- const orders = await fetchOrders(); // stats 끝나야 시작
234
- const notifications = await fetchNotifications(); // orders 끝나야 시작
219
+ const orders = await fetchOrders(); // stats 끝나야 시작
220
+ const notifications = await fetchNotifications(); // orders 끝나야 시작
235
221
  // ...
236
222
  }
237
223
  ```
@@ -266,25 +252,21 @@ function Dashboard() {
266
252
  ```
267
253
 
268
254
  **AsyncBoundary의 이점:**
255
+
269
256
  - 로딩 중에는 스켈레톤 UI -> 레이아웃 시프트 방지
270
257
  - API 에러 시 재시도 버튼이 있는 fallback 표시
271
258
  - 에러가 해당 컴포넌트에서 격리됨 -> 나머지 UI는 정상 동작
272
259
 
273
- **Check for:**
260
+ **🔍 검색:**
261
+
274
262
  - 순차 await로 인한 waterfall 패턴
275
263
  - Suspense 없는 데이터 fetching 컴포넌트
276
264
  - Suspense만 사용하고 ErrorBoundary 누락
277
265
  - useSuspenseQuery 사용 시 AsyncBoundary 적용 여부
278
266
 
279
- **Scoring (0-15):**
280
- - 13-15: AsyncBoundary 적절히 사용, waterfall 없음
281
- - 10-12: Suspense 사용, 일부 waterfall
282
- - 6-9: 부분적 적용, 여러 waterfall
283
- - 0-5: 데이터 fetching 패턴 미적용
284
-
285
267
  ---
286
268
 
287
- ### 6. 네이밍 원칙 (Weight: 15%)
269
+ ### 6. 네이밍 원칙
288
270
 
289
271
  **핵심**: 모든 이름은 내부 코드를 보지 않고도 역할과 동작을 유추할 수 있어야 한다.
290
272
 
@@ -304,18 +286,18 @@ function Dashboard() {
304
286
 
305
287
  #### 6.2 함수 네이밍
306
288
 
307
- | 접두사 | 용도 | 예시 |
308
- |--------|------|------|
309
- | `get` | 값을 계산/반환 (부수효과 없음) | `getFullName()`, `getTotalPrice()` |
310
- | `fetch` | 외부에서 데이터 가져옴 | `fetchUserProfile()`, `fetchOrders()` |
311
- | `create` | 새로운 것을 생성 | `createOrder()`, `createComment()` |
312
- | `update` | 기존 것을 수정 | `updateUserInfo()`, `updateCartItem()` |
313
- | `delete/remove` | 삭제 | `deleteComment()`, `removeFromCart()` |
314
- | `handle` | 이벤트 처리 | `handleSubmit()`, `handleClick()` |
315
- | `validate` | 검증 | `validateEmail()`, `validateForm()` |
316
- | `format` | 형식 변환 | `formatDate()`, `formatCurrency()` |
317
- | `parse` | 문자열->구조화된 데이터 | `parseJSON()`, `parseQueryString()` |
318
- | `serialize` | 구조화된 데이터->문자열 | `serializeFormData()` |
289
+ | 접두사 | 용도 | 예시 |
290
+ | --------------- | ------------------------------ | -------------------------------------- |
291
+ | `get` | 값을 계산/반환 (부수효과 없음) | `getFullName()`, `getTotalPrice()` |
292
+ | `fetch` | 외부에서 데이터 가져옴 | `fetchUserProfile()`, `fetchOrders()` |
293
+ | `create` | 새로운 것을 생성 | `createOrder()`, `createComment()` |
294
+ | `update` | 기존 것을 수정 | `updateUserInfo()`, `updateCartItem()` |
295
+ | `delete/remove` | 삭제 | `deleteComment()`, `removeFromCart()` |
296
+ | `handle` | 이벤트 처리 | `handleSubmit()`, `handleClick()` |
297
+ | `validate` | 검증 | `validateEmail()`, `validateForm()` |
298
+ | `format` | 형식 변환 | `formatDate()`, `formatCurrency()` |
299
+ | `parse` | 문자열->구조화된 데이터 | `parseJSON()`, `parseQueryString()` |
300
+ | `serialize` | 구조화된 데이터->문자열 | `serializeFormData()` |
319
301
 
320
302
  ```tsx
321
303
  // Bad: 동작이 모호함
@@ -331,24 +313,24 @@ function parseSearchParamsToFilters(searchParams) { ... }
331
313
 
332
314
  #### 6.3 훅 네이밍
333
315
 
334
- | 패턴 | 예시 | 반환값 유추 |
335
- |------|------|------------|
336
- | `use` + 명사 | `useUser()` | 사용자 데이터 |
337
- | `use` + 명사 + 동작 | `useCartItems()` | 장바구니 아이템들 |
338
- | `use` + 동작 + 대상 | `useFetchProducts()` | 상품 fetch 결과 |
339
- | `use` + 상태명 + `State` | `useSortState()` | 정렬 상태 + setter |
316
+ | 패턴 | 예시 | 반환값 유추 |
317
+ | ------------------------------ | --------------------- | ------------------- |
318
+ | `use` + 명사 | `useUser()` | 사용자 데이터 |
319
+ | `use` + 명사 + 동작 | `useCartItems()` | 장바구니 아이템들 |
320
+ | `use` + 동작 + 대상 | `useFetchProducts()` | 상품 fetch 결과 |
321
+ | `use` + 상태명 + `State` | `useSortState()` | 정렬 상태 + setter |
340
322
  | `use` + param명 + `QueryParam` | `usePageQueryParam()` | URL의 page 파라미터 |
341
323
 
342
324
  ```tsx
343
325
  // Bad: 반환값을 유추하기 어려움
344
- const data = useData(); // 무슨 데이터?
345
- const result = useQuery(); // 무슨 쿼리?
346
- const state = useAppState(); // 앱의 어떤 상태?
326
+ const data = useData(); // 무슨 데이터?
327
+ const result = useQuery(); // 무슨 쿼리?
328
+ const state = useAppState(); // 앱의 어떤 상태?
347
329
 
348
330
  // Good: 반환값이 명확함
349
- const user = useCurrentUser(); // 현재 로그인한 사용자
350
- const [sort, setSort] = useSortState(); // 정렬 상태
351
- const { products, isLoading } = useProductList(); // 상품 목록
331
+ const user = useCurrentUser(); // 현재 로그인한 사용자
332
+ const [sort, setSort] = useSortState(); // 정렬 상태
333
+ const { products, isLoading } = useProductList(); // 상품 목록
352
334
  ```
353
335
 
354
336
  #### 6.4 상수 네이밍
@@ -381,21 +363,16 @@ const canSubmit = true;
381
363
  const shouldRefetch = false;
382
364
  ```
383
365
 
384
- **Check for:**
366
+ **🔍 검색:**
367
+
385
368
  1. 이름만 보고 3초 안에 역할을 알 수 있는가?
386
369
  2. 같은 역할에 같은 패턴을 사용하는가?
387
370
  3. `data`, `info`, `handle` 같은 모호한 이름이 아닌가?
388
371
  4. 실제 동작과 이름이 일치하는가?
389
372
 
390
- **Scoring (0-15):**
391
- - 13-15: 일관된 네이밍 규칙, 모든 이름이 명확
392
- - 10-12: 대부분 명확, 일부 모호
393
- - 6-9: 여러 모호한 이름, 불일치
394
- - 0-5: 전반적인 네이밍 문제
395
-
396
373
  ---
397
374
 
398
- ### 7. 메모이제이션 적용 기준 (Weight: 10%)
375
+ ### 7. 메모이제이션 적용 기준
399
376
 
400
377
  **원칙: "측정 후 필요할 때만"**
401
378
 
@@ -423,9 +400,9 @@ const ListItem = memo(({ item }) => {
423
400
  });
424
401
 
425
402
  // 사용처에서
426
- {items.map(item => (
427
- <ListItem key={item.id} item={item} />
428
- ))}
403
+ {
404
+ items.map((item) => <ListItem key={item.id} item={item} />);
405
+ }
429
406
  ```
430
407
 
431
408
  #### memo가 무의미해지는 패턴
@@ -433,10 +410,10 @@ const ListItem = memo(({ item }) => {
433
410
  ```tsx
434
411
  // Bad: 인라인 객체/함수를 props로 전달
435
412
  <MemoizedComponent
436
- style={{ color: 'red' }} // 매번 새 객체
437
- onClick={() => doSomething()} // 매번 새 함수
438
- config={{ enabled: true }} // 매번 새 객체
439
- />
413
+ style={{ color: 'red' }} // 매번 새 객체
414
+ onClick={() => doSomething()} // 매번 새 함수
415
+ config={{ enabled: true }} // 매번 새 객체
416
+ />;
440
417
 
441
418
  // Good: 안정적인 참조로 전달
442
419
  const style = useMemo(() => ({ color: 'red' }), []);
@@ -445,71 +422,67 @@ const handleClick = useCallback(() => doSomething(), []);
445
422
  <MemoizedComponent
446
423
  style={style}
447
424
  onClick={handleClick}
448
- config={CONFIG} // 모듈 스코프 상수
449
- />
425
+ config={CONFIG} // 모듈 스코프 상수
426
+ />;
450
427
  ```
451
428
 
452
429
  #### 적용 판단 기준
453
430
 
454
- | 상황 | memo 필요? | 이유 |
455
- |------|-----------|------|
456
- | 리스트에서 반복 렌더링되는 아이템 | 고려 | 일부만 변경 시 효과적 |
457
- | props가 primitive 값으로 안정적 | 고려 | 비교 비용 낮고 효과 있음 |
458
- | props가 매번 새로운 객체/함수 | 불필요 | 비교만 하고 항상 리렌더링 |
459
- | 렌더링 비용이 낮은 간단한 컴포넌트 | 불필요 | 최적화 이득이 거의 없음 |
460
- | 부모가 자주 리렌더링되지 않음 | 불필요 | 문제가 없는데 최적화할 필요 없음 |
431
+ | 상황 | memo 필요? | 이유 |
432
+ | ---------------------------------- | ---------- | -------------------------------- |
433
+ | 리스트에서 반복 렌더링되는 아이템 | 고려 | 일부만 변경 시 효과적 |
434
+ | props가 primitive 값으로 안정적 | 고려 | 비교 비용 낮고 효과 있음 |
435
+ | props가 매번 새로운 객체/함수 | 불필요 | 비교만 하고 항상 리렌더링 |
436
+ | 렌더링 비용이 낮은 간단한 컴포넌트 | 불필요 | 최적화 이득이 거의 없음 |
437
+ | 부모가 자주 리렌더링되지 않음 | 불필요 | 문제가 없는데 최적화할 필요 없음 |
461
438
 
462
439
  #### useMemo / useCallback 기준
463
440
 
464
441
  ```tsx
465
442
  // 불필요: 계산 비용이 낮음
466
- const fullName = useMemo(() => `${firstName} ${lastName}`, [firstName, lastName]);
443
+ const fullName = useMemo(
444
+ () => `${firstName} ${lastName}`,
445
+ [firstName, lastName],
446
+ );
467
447
 
468
448
  // 필요: 비용이 높은 계산
469
449
  const sortedItems = useMemo(
470
450
  () => [...items].sort((a, b) => complexSortLogic(a, b)),
471
- [items]
451
+ [items],
472
452
  );
473
453
 
474
454
  // 필요: 대량 데이터 필터링/변환
475
455
  const filteredData = useMemo(
476
- () => largeDataset.filter(item => item.category === category),
477
- [largeDataset, category]
456
+ () => largeDataset.filter((item) => item.category === category),
457
+ [largeDataset, category],
478
458
  );
479
459
 
480
460
  // 필요: memo된 자식에게 전달하는 콜백
481
- const handleItemClick = useCallback(
482
- (id) => onSelect(id),
483
- [onSelect]
484
- );
461
+ const handleItemClick = useCallback((id) => onSelect(id), [onSelect]);
485
462
  ```
486
463
 
487
- **Check for:**
464
+ **🔍 검색:**
465
+
488
466
  - 불필요한 memo, useMemo, useCallback 사용
489
467
  - memo된 컴포넌트에 인라인 객체/함수 전달
490
468
  - 계산 비용이 낮은데 useMemo 적용
491
469
  - memo 없이 리스트 아이템 렌더링
492
470
 
493
- **Scoring (0-10):**
494
- - 9-10: 적절한 메모이제이션, 과도하지도 부족하지도 않음
495
- - 7-8: 대부분 적절, 일부 불필요/누락
496
- - 4-6: 과도하거나 부족한 메모이제이션
497
- - 0-3: 메모이제이션 오용 또는 심각한 누락
498
-
499
471
  ---
500
472
 
501
- ### 8. 안티패턴 탐지 (Weight: 5%)
473
+ ### 8. 안티패턴 탐지
502
474
 
503
- | 안티패턴 | 설명 | 해결책 |
504
- |---------|------|--------|
505
- | **God Hook** | 모든 상태를 관리하는 단일 훅 | 책임별로 훅 분리 |
506
- | **Prop Drilling** | 중간 컴포넌트가 사용하지 않는 props 전달 | Context 또는 컴포넌트 합성 |
507
- | **Props -> State 복사** | props를 받아서 내부 useState에 복사 | props 직접 사용 (Controlled) |
508
- | **Waterfall Fetch** | 상위에서 모든 fetch를 순차 관리 | 컴포넌트별 자체 fetch |
509
- | **과도한 명시성** | 재사용마다 훅 + props 연결 반복 | 응집도 우선 패턴 검토 |
510
- | **패턴 불일치** | 같은 역할인데 다른 패턴 사용 | 컨벤션 통일 |
475
+ | 안티패턴 | 설명 | 해결책 |
476
+ | ----------------------- | ---------------------------------------- | ---------------------------- |
477
+ | **God Hook** | 모든 상태를 관리하는 단일 훅 | 책임별로 훅 분리 |
478
+ | **Prop Drilling** | 중간 컴포넌트가 사용하지 않는 props 전달 | Context 또는 컴포넌트 합성 |
479
+ | **Props -> State 복사** | props를 받아서 내부 useState에 복사 | props 직접 사용 (Controlled) |
480
+ | **Waterfall Fetch** | 상위에서 모든 fetch를 순차 관리 | 컴포넌트별 자체 fetch |
481
+ | **과도한 명시성** | 재사용마다 훅 + props 연결 반복 | 응집도 우선 패턴 검토 |
482
+ | **패턴 불일치** | 같은 역할인데 다른 패턴 사용 | 컨벤션 통일 |
511
483
 
512
484
  **God Hook 예시:**
485
+
513
486
  ```tsx
514
487
  // Bad
515
488
  function usePageState() {
@@ -521,16 +494,23 @@ function usePageState() {
521
494
  }
522
495
 
523
496
  // Good: 책임별 분리
524
- function useUser() { /* 사용자만 */ }
525
- function useCart() { /* 장바구니만 */ }
526
- function useNotifications() { /* 알림만 */ }
497
+ function useUser() {
498
+ /* 사용자만 */
499
+ }
500
+ function useCart() {
501
+ /* 장바구니만 */
502
+ }
503
+ function useNotifications() {
504
+ /* 알림만 */
505
+ }
527
506
  ```
528
507
 
529
508
  **Props -> State 복사:**
509
+
530
510
  ```tsx
531
511
  // Bad: 동기화 문제 발생
532
512
  function Modal({ isOpen }) {
533
- const [open, setOpen] = useState(isOpen); // props 복사
513
+ const [open, setOpen] = useState(isOpen); // props 복사
534
514
  }
535
515
 
536
516
  // Good: Controlled Component
@@ -540,21 +520,16 @@ function Modal({ isOpen, onClose }) {
540
520
  }
541
521
  ```
542
522
 
543
- **Check for:**
523
+ **🔍 검색:**
524
+
544
525
  - useState로 props 복사하는 패턴
545
526
  - 5개 이상 상태를 관리하는 단일 Hook
546
527
  - 3단계 이상 Props Drilling
547
528
  - 같은 역할의 다른 패턴 사용
548
529
 
549
- **Scoring (0-5):**
550
- - 5: 안티패턴 없음
551
- - 3-4: 경미한 안티패턴 1-2개
552
- - 1-2: 여러 안티패턴 존재
553
- - 0: 심각한 안티패턴 다수
554
-
555
530
  ---
556
531
 
557
- ### 9. 리뷰 체크리스트 기반 검토 (Weight: 5%)
532
+ ### 9. 리뷰 체크리스트 기반 검토
558
533
 
559
534
  최종 검토 체크리스트:
560
535
 
@@ -567,15 +542,9 @@ function Modal({ isOpen, onClose }) {
567
542
  7. **에러 처리**: Suspense와 ErrorBoundary가 함께 사용되었는가?
568
543
  8. **메모이제이션**: 실제 성능 문제 없이 과도하게 최적화하고 있지 않은가?
569
544
 
570
- **Scoring (0-5):**
571
- - 5: 모든 항목 통과
572
- - 3-4: 6-7개 항목 통과
573
- - 1-2: 4-5개 항목 통과
574
- - 0: 3개 이하 항목 통과
575
-
576
545
  ---
577
546
 
578
- ## Analysis Process
547
+ ## 분석 프로세스
579
548
 
580
549
  ### Step 1: 코드베이스 탐색
581
550
 
@@ -587,106 +556,86 @@ Read: 주요 파일 상세 분석
587
556
 
588
557
  ### Step 2: 9가지 영역 평가
589
558
 
590
- 각 발견 사항을 9가지 영역으로 분류하고 점수화
559
+ 각 발견 사항을 9가지 영역으로 분류하고 Critical / Recommended Improvements / Best Practices Found로 분류
591
560
 
592
561
  ### Step 3: 트레이드오프 분석
593
562
 
594
563
  상충하는 가치들 사이에서 현재 상황에 맞는 균형점 제안
595
564
 
596
- ### Step 4: 우선순위 결정
565
+ ### Step 4: 심각도 분류
597
566
 
598
- - P0 (Critical): 버그 발생 가능성 높음
599
- - P1 (High): 유지보수 비용 증가
600
- - P2 (Medium): 개선 이점 있음
601
- - P3 (Low): Nice to have
567
+ - **Critical** (즉시 수정): 버그 발생 가능성 높음, 유지보수 비용 증가
568
+ - **Recommended Improvements** (권장 개선): 개선 이점 있음
569
+ - **Best Practices Found** (잘하고 있음): 되어 있는 패턴
602
570
 
603
571
  ---
604
572
 
605
573
  ## Output Format
606
574
 
607
- ```markdown
575
+ ````markdown
608
576
  # React 개발 원칙 기반 코드 리뷰 결과
609
577
 
610
- ## Executive Summary
611
- - **Overall Score:** X/100
612
- - **Critical Issues:** N개 (즉시 수정 필요)
613
- - **Recommended Improvements:** M개 (권장)
614
- - **Best Practices Found:** P개 (잘하고 있음)
615
-
616
- ## Score Breakdown
617
-
618
- | 영역 | 점수 | 비고 |
619
- |------|------|------|
620
- | 응집도 vs 명시성 | X/15 | |
621
- | Props 관리 | X/10 | |
622
- | 부수효과 위치 | X/10 | |
623
- | Toss Fundamentals | X/15 | |
624
- | 데이터 Fetching | X/15 | |
625
- | 네이밍 원칙 | X/15 | |
626
- | 메모이제이션 | X/10 | |
627
- | 안티패턴 | X/5 | |
628
- | 체크리스트 | X/5 | |
629
- | **합계** | **X/100** | |
578
+ ## 발견 사항 요약
579
+
580
+ - **Critical:** N개 (즉시 수정 필요)
581
+ - **Recommended Improvements:** M개 (권장 개선)
582
+ - **Best Practices Found:** Q개 (잘하고 있음)
630
583
 
631
584
  ---
632
585
 
633
586
  ## Critical Issues (즉시 수정)
634
587
 
635
588
  ### 1. [Issue Name]
636
- **영역:** [해당 영역]
589
+
590
+ **위반 원칙:** [해당 영역]
637
591
  **파일:** [file:line]
638
592
 
639
593
  **문제:**
640
594
  [설명]
641
595
 
642
596
  **현재 코드:**
597
+
643
598
  ```typescript
644
599
  // 문제 코드
645
600
  ```
646
-
647
- **위반 원칙:**
648
- [위반한 원칙명]
601
+ ````
649
602
 
650
603
  **수정 방법:**
604
+
651
605
  ```typescript
652
606
  // 개선 코드
653
607
  ```
654
608
 
655
- **영향:**
656
- - [영향 설명]
657
-
658
609
  ---
659
610
 
660
- ## Recommended Improvements (권장)
611
+ ## Recommended Improvements (권장 개선)
661
612
 
662
- [같은 형식, 낮은 우선순위]
613
+ [같은 형식]
663
614
 
664
615
  ---
665
616
 
666
617
  ## Best Practices Found (잘하고 있음)
667
618
 
668
619
  ### [Good Pattern]
669
- **영역:** [해당 영역]
620
+
621
+ **원칙:** [해당 영역]
670
622
  **파일:** [file:line]
671
623
 
672
624
  **잘한 점:**
673
625
  [설명]
674
626
 
675
- **코드:**
676
- ```typescript
677
- // 좋은 예시
678
- ```
679
-
680
627
  ---
681
628
 
682
629
  ## 트레이드오프 분석
683
630
 
684
631
  ### 발견된 상충 가치
685
- | 상황 | 선택한 가치 | 포기한 가치 | 추천 |
686
- |------|------------|------------|------|
687
- | [상황] | [가치] | [가치] | 유지/변경 |
632
+
633
+ | 상황 | 선택한 가치 | 포기한 가치 | 추천 |
634
+ | ------ | ----------- | ----------- | --------- |
635
+ | [상황] | [가치] | [가치] | 유지/변경 |
688
636
 
689
637
  ### 추천 방향
638
+
690
639
  [현재 상황에 맞는 균형점 제안]
691
640
 
692
641
  ---
@@ -694,51 +643,46 @@ Read: 주요 파일 상세 분석
694
643
  ## Metrics
695
644
 
696
645
  ### 응집도 vs 명시성
646
+
697
647
  - 응집도 우선 컴포넌트: N개
698
648
  - 명시성 우선 컴포넌트: M개
699
649
  - 패턴 일관성: High/Medium/Low
700
650
 
701
651
  ### Props 관리
652
+
702
653
  - 불필요한 props 전달: N개
703
654
  - 적절한 상태 위치: M개
704
655
 
705
656
  ### 부수효과
657
+
706
658
  - 내부 관리 적절: N개
707
659
  - 분리 필요: M개
708
660
  - 숨은 사이드이펙트: P개
709
661
 
710
662
  ### 데이터 Fetching
663
+
711
664
  - AsyncBoundary 사용: N개
712
665
  - Waterfall 패턴: M개
713
666
  - Suspense 누락: P개
714
667
 
715
668
  ### 네이밍
669
+
716
670
  - 명확한 이름: N개
717
671
  - 모호한 이름: M개
718
672
  - 불리언 접두사 누락: P개
719
673
 
720
674
  ### 메모이제이션
675
+
721
676
  - 적절한 memo: N개
722
677
  - 불필요한 memo: M개
723
678
  - 누락된 memo: P개
724
679
 
725
680
  ### 안티패턴
681
+
726
682
  - God Hook: N개
727
683
  - Props Drilling: M개
728
684
  - Props -> State 복사: P개
729
685
 
730
- ---
731
-
732
- ## Next Steps
733
-
734
- ### P0 (즉시)
735
- 1. [ ] [액션 아이템]
736
-
737
- ### P1 (이번 스프린트)
738
- 1. [ ] [액션 아이템]
739
-
740
- ### P2 (백로그)
741
- 1. [ ] [액션 아이템]
742
686
  ```
743
687
 
744
688
  ---
@@ -758,14 +702,6 @@ Read: 주요 파일 상세 분석
758
702
 
759
703
  ---
760
704
 
761
- ## 점수 가이드라인
762
-
763
- - 90-100: 우수, 업계 모범 사례 수준
764
- - 75-89: 양호, 주요 패턴 준수
765
- - 60-74: 허용 가능, 일부 개선 필요
766
- - 40-59: 우려됨, 다수의 문제
767
- - 0-39: 심각, 즉시 개선 필요
768
-
769
705
  ---
770
706
 
771
707
  ## Philosophy
@@ -785,3 +721,4 @@ Read: 주요 파일 상세 분석
785
721
  - React 개발 원칙 문서 (react-development-principles.md)
786
722
  - [Toss Frontend Fundamentals](https://frontend-fundamentals.com/code-quality/code/)
787
723
  - [React Official Docs](https://react.dev)
724
+ ```