@su-record/vibe 2.3.0 → 2.3.2

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.
Files changed (98) hide show
  1. package/.claude/settings.json +35 -35
  2. package/.claude/settings.local.json +24 -25
  3. package/.claude/vibe/constitution.md +184 -184
  4. package/.claude/vibe/rules/core/communication-guide.md +104 -104
  5. package/.claude/vibe/rules/core/development-philosophy.md +52 -52
  6. package/.claude/vibe/rules/core/quick-start.md +120 -120
  7. package/.claude/vibe/rules/languages/dart-flutter.md +509 -509
  8. package/.claude/vibe/rules/languages/go.md +396 -396
  9. package/.claude/vibe/rules/languages/java-spring.md +586 -586
  10. package/.claude/vibe/rules/languages/kotlin-android.md +491 -491
  11. package/.claude/vibe/rules/languages/python-django.md +371 -371
  12. package/.claude/vibe/rules/languages/python-fastapi.md +386 -386
  13. package/.claude/vibe/rules/languages/rust.md +425 -425
  14. package/.claude/vibe/rules/languages/swift-ios.md +516 -516
  15. package/.claude/vibe/rules/languages/typescript-nextjs.md +441 -441
  16. package/.claude/vibe/rules/languages/typescript-node.md +375 -375
  17. package/.claude/vibe/rules/languages/typescript-nuxt.md +521 -521
  18. package/.claude/vibe/rules/languages/typescript-react-native.md +446 -446
  19. package/.claude/vibe/rules/languages/typescript-react.md +525 -525
  20. package/.claude/vibe/rules/languages/typescript-vue.md +353 -353
  21. package/.claude/vibe/rules/quality/bdd-contract-testing.md +388 -388
  22. package/.claude/vibe/rules/quality/checklist.md +276 -276
  23. package/.claude/vibe/rules/quality/testing-strategy.md +437 -437
  24. package/.claude/vibe/rules/standards/anti-patterns.md +369 -369
  25. package/.claude/vibe/rules/standards/code-structure.md +291 -291
  26. package/.claude/vibe/rules/standards/complexity-metrics.md +312 -312
  27. package/.claude/vibe/rules/standards/naming-conventions.md +198 -198
  28. package/.claude/vibe/setup.sh +31 -31
  29. package/.claude/vibe/templates/constitution-template.md +184 -184
  30. package/.claude/vibe/templates/contract-backend-template.md +517 -517
  31. package/.claude/vibe/templates/contract-frontend-template.md +594 -594
  32. package/.claude/vibe/templates/feature-template.md +96 -96
  33. package/.claude/vibe/templates/spec-template.md +199 -199
  34. package/CLAUDE.md +345 -323
  35. package/LICENSE +21 -21
  36. package/README.md +744 -724
  37. package/agents/compounder.md +261 -261
  38. package/agents/diagrammer.md +178 -178
  39. package/agents/e2e-tester.md +266 -266
  40. package/agents/explorer.md +48 -48
  41. package/agents/implementer.md +53 -53
  42. package/agents/research/best-practices-agent.md +139 -139
  43. package/agents/research/codebase-patterns-agent.md +147 -147
  44. package/agents/research/framework-docs-agent.md +181 -181
  45. package/agents/research/security-advisory-agent.md +167 -167
  46. package/agents/review/architecture-reviewer.md +107 -107
  47. package/agents/review/complexity-reviewer.md +116 -116
  48. package/agents/review/data-integrity-reviewer.md +88 -88
  49. package/agents/review/git-history-reviewer.md +103 -103
  50. package/agents/review/performance-reviewer.md +86 -86
  51. package/agents/review/python-reviewer.md +152 -152
  52. package/agents/review/rails-reviewer.md +139 -139
  53. package/agents/review/react-reviewer.md +144 -144
  54. package/agents/review/security-reviewer.md +80 -80
  55. package/agents/review/simplicity-reviewer.md +140 -140
  56. package/agents/review/test-coverage-reviewer.md +116 -116
  57. package/agents/review/typescript-reviewer.md +127 -127
  58. package/agents/searcher.md +54 -54
  59. package/agents/simplifier.md +119 -119
  60. package/agents/tester.md +49 -49
  61. package/agents/ui-previewer.md +137 -137
  62. package/commands/vibe.analyze.md +245 -180
  63. package/commands/vibe.reason.md +223 -183
  64. package/commands/vibe.review.md +200 -136
  65. package/commands/vibe.run.md +838 -836
  66. package/commands/vibe.spec.md +419 -383
  67. package/commands/vibe.utils.md +101 -101
  68. package/commands/vibe.verify.md +282 -241
  69. package/dist/cli/index.js +385 -385
  70. package/dist/lib/MemoryManager.d.ts.map +1 -1
  71. package/dist/lib/MemoryManager.js +119 -114
  72. package/dist/lib/MemoryManager.js.map +1 -1
  73. package/dist/lib/PythonParser.js +108 -108
  74. package/dist/lib/gemini-mcp.js +15 -15
  75. package/dist/lib/gemini-oauth.js +35 -35
  76. package/dist/lib/gpt-mcp.js +17 -17
  77. package/dist/lib/gpt-oauth.js +44 -44
  78. package/dist/tools/analytics/getUsageAnalytics.js +12 -12
  79. package/dist/tools/index.d.ts +50 -0
  80. package/dist/tools/index.d.ts.map +1 -0
  81. package/dist/tools/index.js +61 -0
  82. package/dist/tools/index.js.map +1 -0
  83. package/dist/tools/memory/createMemoryTimeline.js +10 -10
  84. package/dist/tools/memory/getMemoryGraph.js +12 -12
  85. package/dist/tools/memory/getSessionContext.js +9 -9
  86. package/dist/tools/memory/linkMemories.js +14 -14
  87. package/dist/tools/memory/listMemories.js +4 -4
  88. package/dist/tools/memory/recallMemory.js +4 -4
  89. package/dist/tools/memory/saveMemory.js +4 -4
  90. package/dist/tools/memory/searchMemoriesAdvanced.js +22 -22
  91. package/dist/tools/planning/generatePrd.js +46 -46
  92. package/dist/tools/prompt/enhancePromptGemini.js +160 -160
  93. package/dist/tools/reasoning/applyReasoningFramework.js +56 -56
  94. package/dist/tools/semantic/analyzeDependencyGraph.js +12 -12
  95. package/hooks/hooks.json +121 -103
  96. package/package.json +73 -69
  97. package/skills/git-worktree.md +178 -178
  98. package/skills/priority-todos.md +236 -236
@@ -1,369 +1,369 @@
1
- # 🚫 자동 안티패턴 회피
2
-
3
- ## TypeScript 안티패턴
4
-
5
- ### 1. any 타입 사용
6
-
7
- ```typescript
8
- // ❌ any 사용
9
- function processData(data: any) {
10
- return data.value; // 타입 안전성 상실
11
- }
12
-
13
- // ✅ unknown + type guard
14
- function processData(data: unknown) {
15
- if (isValidData(data)) {
16
- return data.value; // 타입 안전
17
- }
18
- throw new Error('Invalid data');
19
- }
20
-
21
- function isValidData(data: unknown): data is { value: string } {
22
- return typeof data === 'object' && data !== null && 'value' in data;
23
- }
24
- ```
25
-
26
- ### 2. as any 강제 타입 캐스팅
27
-
28
- ```typescript
29
- // ❌ as any로 타입 우회
30
- const user = response as any;
31
- user.name; // 런타임 에러 위험
32
-
33
- // ✅ 적절한 타입 정의
34
- interface User {
35
- name: string;
36
- email: string;
37
- }
38
-
39
- const user = response as User;
40
- user.name; // 타입 안전
41
- ```
42
-
43
- ### 3. @ts-ignore 남용
44
-
45
- ```typescript
46
- // ❌ @ts-ignore로 에러 무시
47
- // @ts-ignore
48
- const result = problematicCode();
49
-
50
- // ✅ 타입 문제 근본 해결
51
- interface Expected {
52
- id: string;
53
- }
54
-
55
- const result: Expected = {
56
- id: String(problematicCode()),
57
- };
58
- ```
59
-
60
- ## React 안티패턴
61
-
62
- ### 1. dangerouslySetInnerHTML 사용
63
-
64
- ```typescript
65
- // ❌ XSS 취약점
66
- function Component({ html }: { html: string }) {
67
- return <div dangerouslySetInnerHTML={{ __html: html }} />;
68
- }
69
-
70
- // ✅ 안전한 렌더링
71
- import DOMPurify from 'dompurify';
72
-
73
- function Component({ html }: { html: string }) {
74
- const sanitized = DOMPurify.sanitize(html);
75
- return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
76
- }
77
-
78
- // ✅ 더 나은 방법: 마크다운 라이브러리 사용
79
- import ReactMarkdown from 'react-markdown';
80
-
81
- function Component({ markdown }: { markdown: string }) {
82
- return <ReactMarkdown>{markdown}</ReactMarkdown>;
83
- }
84
- ```
85
-
86
- ### 2. Props Drilling (3단계 이상)
87
-
88
- ```typescript
89
- // ❌ Props drilling
90
- function App() {
91
- const [user, setUser] = useState<User>();
92
- return <Parent user={user} />;
93
- }
94
-
95
- function Parent({ user }: { user: User }) {
96
- return <Child user={user} />;
97
- }
98
-
99
- function Child({ user }: { user: User }) {
100
- return <GrandChild user={user} />;
101
- }
102
-
103
- function GrandChild({ user }: { user: User }) {
104
- return <div>{user.name}</div>;
105
- }
106
-
107
- // ✅ Context API 사용
108
- const UserContext = createContext<User | undefined>(undefined);
109
-
110
- function App() {
111
- const [user, setUser] = useState<User>();
112
- return (
113
- <UserContext.Provider value={user}>
114
- <Parent />
115
- </UserContext.Provider>
116
- );
117
- }
118
-
119
- function GrandChild() {
120
- const user = useContext(UserContext);
121
- return <div>{user?.name}</div>;
122
- }
123
- ```
124
-
125
- ### 3. useEffect 의존성 배열 누락
126
-
127
- ```typescript
128
- // ❌ 의존성 누락
129
- function Component({ userId }: { userId: string }) {
130
- const [user, setUser] = useState<User>();
131
-
132
- useEffect(() => {
133
- fetchUser(userId).then(setUser);
134
- }, []); // userId 의존성 누락!
135
-
136
- return <div>{user?.name}</div>;
137
- }
138
-
139
- // ✅ 모든 의존성 명시
140
- function Component({ userId }: { userId: string }) {
141
- const [user, setUser] = useState<User>();
142
-
143
- useEffect(() => {
144
- fetchUser(userId).then(setUser);
145
- }, [userId]); // 의존성 명시
146
-
147
- return <div>{user?.name}</div>;
148
- }
149
- ```
150
-
151
- ## JavaScript 안티패턴
152
-
153
- ### 1. var 사용
154
-
155
- ```typescript
156
- // ❌ var 사용
157
- var count = 0;
158
- if (true) {
159
- var count = 1; // 같은 변수!
160
- }
161
- console.log(count); // 1
162
-
163
- // ✅ const/let 사용
164
- let count = 0;
165
- if (true) {
166
- let count = 1; // 블록 스코프
167
- }
168
- console.log(count); // 0
169
- ```
170
-
171
- ### 2. == 사용 (느슨한 비교)
172
-
173
- ```typescript
174
- // ❌ == 사용
175
- if (value == null) { } // undefined도 매칭
176
- if ('5' == 5) { } // true (타입 강제 변환)
177
-
178
- // ✅ === 사용
179
- if (value === null) { }
180
- if (value === undefined) { }
181
- if ('5' === 5) { } // false
182
- ```
183
-
184
- ### 3. eval() 사용
185
-
186
- ```typescript
187
- // ❌ eval() 사용 (보안 위험)
188
- const code = userInput;
189
- eval(code); // 임의 코드 실행 가능
190
-
191
- // ✅ 대안 구현
192
- const allowedOperations = {
193
- add: (a: number, b: number) => a + b,
194
- subtract: (a: number, b: number) => a - b,
195
- };
196
-
197
- const operation = allowedOperations[userInput];
198
- if (operation) {
199
- result = operation(a, b);
200
- }
201
- ```
202
-
203
- ## CSS 안티패턴
204
-
205
- ### 1. !important 남용
206
-
207
- ```css
208
- /* ❌ !important 남용 */
209
- .button {
210
- color: blue !important;
211
- background: red !important;
212
- }
213
-
214
- /* ✅ 구체적인 선택자 사용 */
215
- .navigation .button.primary {
216
- color: blue;
217
- background: red;
218
- }
219
- ```
220
-
221
- ### 2. 인라인 스타일 남용
222
-
223
- ```typescript
224
- // ❌ 인라인 스타일
225
- function Button() {
226
- return (
227
- <button
228
- style={{
229
- backgroundColor: 'blue',
230
- color: 'white',
231
- padding: '10px',
232
- borderRadius: '5px',
233
- }}
234
- >
235
- Click me
236
- </button>
237
- );
238
- }
239
-
240
- // ✅ CSS 클래스 사용
241
- function Button() {
242
- return <button className="btn-primary">Click me</button>;
243
- }
244
-
245
- // styles.css
246
- .btn-primary {
247
- background-color: blue;
248
- color: white;
249
- padding: 10px;
250
- border-radius: 5px;
251
- }
252
- ```
253
-
254
- ## 성능 안티패턴
255
-
256
- ### 1. 불필요한 리렌더링
257
-
258
- ```typescript
259
- // ❌ 매번 새 객체/함수 생성
260
- function Parent() {
261
- return <Child config={{ theme: 'dark' }} onClick={() => {}} />;
262
- // 매 렌더마다 새 객체/함수 생성 → Child 리렌더
263
- }
264
-
265
- // ✅ useMemo/useCallback 사용
266
- function Parent() {
267
- const config = useMemo(() => ({ theme: 'dark' }), []);
268
- const handleClick = useCallback(() => {}, []);
269
-
270
- return <Child config={config} onClick={handleClick} />;
271
- }
272
- ```
273
-
274
- ### 2. 동기적 무거운 연산
275
-
276
- ```typescript
277
- // ❌ 메인 스레드 블로킹
278
- function Component({ data }: { data: number[] }) {
279
- const result = data
280
- .map(heavyComputation)
281
- .filter(x => x > 0)
282
- .reduce((a, b) => a + b);
283
-
284
- return <div>{result}</div>;
285
- }
286
-
287
- // ✅ useMemo로 메모이제이션
288
- function Component({ data }: { data: number[] }) {
289
- const result = useMemo(
290
- () =>
291
- data
292
- .map(heavyComputation)
293
- .filter(x => x > 0)
294
- .reduce((a, b) => a + b),
295
- [data]
296
- );
297
-
298
- return <div>{result}</div>;
299
- }
300
- ```
301
-
302
- ## 보안 안티패턴
303
-
304
- ### 1. 민감 정보 하드코딩
305
-
306
- ```typescript
307
- // ❌ API 키 하드코딩
308
- const API_KEY = 'sk-1234567890abcdef';
309
-
310
- // ✅ 환경 변수 사용
311
- const API_KEY = process.env.NEXT_PUBLIC_API_KEY;
312
- ```
313
-
314
- ### 2. SQL Injection 취약점
315
-
316
- ```typescript
317
- // ❌ 직접 문자열 연결
318
- const query = `SELECT * FROM users WHERE id = ${userId}`;
319
-
320
- // ✅ 파라미터화된 쿼리
321
- const query = 'SELECT * FROM users WHERE id = ?';
322
- db.execute(query, [userId]);
323
- ```
324
-
325
- ## 에러 처리 안티패턴
326
-
327
- ### 1. 빈 catch 블록
328
-
329
- ```typescript
330
- // ❌ 에러 무시
331
- try {
332
- riskyOperation();
333
- } catch (e) {
334
- // 아무것도 안 함
335
- }
336
-
337
- // ✅ 적절한 에러 처리
338
- try {
339
- riskyOperation();
340
- } catch (error) {
341
- console.error('Operation failed:', error);
342
- showErrorNotification(error);
343
- trackError(error);
344
- }
345
- ```
346
-
347
- ### 2. 에러 타입 확인 없이 처리
348
-
349
- ```typescript
350
- // ❌ 모든 에러 동일하게 처리
351
- try {
352
- await fetchData();
353
- } catch (error) {
354
- showError('Failed'); // 구체적이지 않음
355
- }
356
-
357
- // ✅ 에러 타입별 처리
358
- try {
359
- await fetchData();
360
- } catch (error) {
361
- if (error instanceof NetworkError) {
362
- showError('네트워크 연결을 확인해주세요');
363
- } else if (error instanceof AuthError) {
364
- redirectToLogin();
365
- } else {
366
- showError('알 수 없는 오류가 발생했습니다');
367
- }
368
- }
369
- ```
1
+ # 🚫 자동 안티패턴 회피
2
+
3
+ ## TypeScript 안티패턴
4
+
5
+ ### 1. any 타입 사용
6
+
7
+ ```typescript
8
+ // ❌ any 사용
9
+ function processData(data: any) {
10
+ return data.value; // 타입 안전성 상실
11
+ }
12
+
13
+ // ✅ unknown + type guard
14
+ function processData(data: unknown) {
15
+ if (isValidData(data)) {
16
+ return data.value; // 타입 안전
17
+ }
18
+ throw new Error('Invalid data');
19
+ }
20
+
21
+ function isValidData(data: unknown): data is { value: string } {
22
+ return typeof data === 'object' && data !== null && 'value' in data;
23
+ }
24
+ ```
25
+
26
+ ### 2. as any 강제 타입 캐스팅
27
+
28
+ ```typescript
29
+ // ❌ as any로 타입 우회
30
+ const user = response as any;
31
+ user.name; // 런타임 에러 위험
32
+
33
+ // ✅ 적절한 타입 정의
34
+ interface User {
35
+ name: string;
36
+ email: string;
37
+ }
38
+
39
+ const user = response as User;
40
+ user.name; // 타입 안전
41
+ ```
42
+
43
+ ### 3. @ts-ignore 남용
44
+
45
+ ```typescript
46
+ // ❌ @ts-ignore로 에러 무시
47
+ // @ts-ignore
48
+ const result = problematicCode();
49
+
50
+ // ✅ 타입 문제 근본 해결
51
+ interface Expected {
52
+ id: string;
53
+ }
54
+
55
+ const result: Expected = {
56
+ id: String(problematicCode()),
57
+ };
58
+ ```
59
+
60
+ ## React 안티패턴
61
+
62
+ ### 1. dangerouslySetInnerHTML 사용
63
+
64
+ ```typescript
65
+ // ❌ XSS 취약점
66
+ function Component({ html }: { html: string }) {
67
+ return <div dangerouslySetInnerHTML={{ __html: html }} />;
68
+ }
69
+
70
+ // ✅ 안전한 렌더링
71
+ import DOMPurify from 'dompurify';
72
+
73
+ function Component({ html }: { html: string }) {
74
+ const sanitized = DOMPurify.sanitize(html);
75
+ return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
76
+ }
77
+
78
+ // ✅ 더 나은 방법: 마크다운 라이브러리 사용
79
+ import ReactMarkdown from 'react-markdown';
80
+
81
+ function Component({ markdown }: { markdown: string }) {
82
+ return <ReactMarkdown>{markdown}</ReactMarkdown>;
83
+ }
84
+ ```
85
+
86
+ ### 2. Props Drilling (3단계 이상)
87
+
88
+ ```typescript
89
+ // ❌ Props drilling
90
+ function App() {
91
+ const [user, setUser] = useState<User>();
92
+ return <Parent user={user} />;
93
+ }
94
+
95
+ function Parent({ user }: { user: User }) {
96
+ return <Child user={user} />;
97
+ }
98
+
99
+ function Child({ user }: { user: User }) {
100
+ return <GrandChild user={user} />;
101
+ }
102
+
103
+ function GrandChild({ user }: { user: User }) {
104
+ return <div>{user.name}</div>;
105
+ }
106
+
107
+ // ✅ Context API 사용
108
+ const UserContext = createContext<User | undefined>(undefined);
109
+
110
+ function App() {
111
+ const [user, setUser] = useState<User>();
112
+ return (
113
+ <UserContext.Provider value={user}>
114
+ <Parent />
115
+ </UserContext.Provider>
116
+ );
117
+ }
118
+
119
+ function GrandChild() {
120
+ const user = useContext(UserContext);
121
+ return <div>{user?.name}</div>;
122
+ }
123
+ ```
124
+
125
+ ### 3. useEffect 의존성 배열 누락
126
+
127
+ ```typescript
128
+ // ❌ 의존성 누락
129
+ function Component({ userId }: { userId: string }) {
130
+ const [user, setUser] = useState<User>();
131
+
132
+ useEffect(() => {
133
+ fetchUser(userId).then(setUser);
134
+ }, []); // userId 의존성 누락!
135
+
136
+ return <div>{user?.name}</div>;
137
+ }
138
+
139
+ // ✅ 모든 의존성 명시
140
+ function Component({ userId }: { userId: string }) {
141
+ const [user, setUser] = useState<User>();
142
+
143
+ useEffect(() => {
144
+ fetchUser(userId).then(setUser);
145
+ }, [userId]); // 의존성 명시
146
+
147
+ return <div>{user?.name}</div>;
148
+ }
149
+ ```
150
+
151
+ ## JavaScript 안티패턴
152
+
153
+ ### 1. var 사용
154
+
155
+ ```typescript
156
+ // ❌ var 사용
157
+ var count = 0;
158
+ if (true) {
159
+ var count = 1; // 같은 변수!
160
+ }
161
+ console.log(count); // 1
162
+
163
+ // ✅ const/let 사용
164
+ let count = 0;
165
+ if (true) {
166
+ let count = 1; // 블록 스코프
167
+ }
168
+ console.log(count); // 0
169
+ ```
170
+
171
+ ### 2. == 사용 (느슨한 비교)
172
+
173
+ ```typescript
174
+ // ❌ == 사용
175
+ if (value == null) { } // undefined도 매칭
176
+ if ('5' == 5) { } // true (타입 강제 변환)
177
+
178
+ // ✅ === 사용
179
+ if (value === null) { }
180
+ if (value === undefined) { }
181
+ if ('5' === 5) { } // false
182
+ ```
183
+
184
+ ### 3. eval() 사용
185
+
186
+ ```typescript
187
+ // ❌ eval() 사용 (보안 위험)
188
+ const code = userInput;
189
+ eval(code); // 임의 코드 실행 가능
190
+
191
+ // ✅ 대안 구현
192
+ const allowedOperations = {
193
+ add: (a: number, b: number) => a + b,
194
+ subtract: (a: number, b: number) => a - b,
195
+ };
196
+
197
+ const operation = allowedOperations[userInput];
198
+ if (operation) {
199
+ result = operation(a, b);
200
+ }
201
+ ```
202
+
203
+ ## CSS 안티패턴
204
+
205
+ ### 1. !important 남용
206
+
207
+ ```css
208
+ /* ❌ !important 남용 */
209
+ .button {
210
+ color: blue !important;
211
+ background: red !important;
212
+ }
213
+
214
+ /* ✅ 구체적인 선택자 사용 */
215
+ .navigation .button.primary {
216
+ color: blue;
217
+ background: red;
218
+ }
219
+ ```
220
+
221
+ ### 2. 인라인 스타일 남용
222
+
223
+ ```typescript
224
+ // ❌ 인라인 스타일
225
+ function Button() {
226
+ return (
227
+ <button
228
+ style={{
229
+ backgroundColor: 'blue',
230
+ color: 'white',
231
+ padding: '10px',
232
+ borderRadius: '5px',
233
+ }}
234
+ >
235
+ Click me
236
+ </button>
237
+ );
238
+ }
239
+
240
+ // ✅ CSS 클래스 사용
241
+ function Button() {
242
+ return <button className="btn-primary">Click me</button>;
243
+ }
244
+
245
+ // styles.css
246
+ .btn-primary {
247
+ background-color: blue;
248
+ color: white;
249
+ padding: 10px;
250
+ border-radius: 5px;
251
+ }
252
+ ```
253
+
254
+ ## 성능 안티패턴
255
+
256
+ ### 1. 불필요한 리렌더링
257
+
258
+ ```typescript
259
+ // ❌ 매번 새 객체/함수 생성
260
+ function Parent() {
261
+ return <Child config={{ theme: 'dark' }} onClick={() => {}} />;
262
+ // 매 렌더마다 새 객체/함수 생성 → Child 리렌더
263
+ }
264
+
265
+ // ✅ useMemo/useCallback 사용
266
+ function Parent() {
267
+ const config = useMemo(() => ({ theme: 'dark' }), []);
268
+ const handleClick = useCallback(() => {}, []);
269
+
270
+ return <Child config={config} onClick={handleClick} />;
271
+ }
272
+ ```
273
+
274
+ ### 2. 동기적 무거운 연산
275
+
276
+ ```typescript
277
+ // ❌ 메인 스레드 블로킹
278
+ function Component({ data }: { data: number[] }) {
279
+ const result = data
280
+ .map(heavyComputation)
281
+ .filter(x => x > 0)
282
+ .reduce((a, b) => a + b);
283
+
284
+ return <div>{result}</div>;
285
+ }
286
+
287
+ // ✅ useMemo로 메모이제이션
288
+ function Component({ data }: { data: number[] }) {
289
+ const result = useMemo(
290
+ () =>
291
+ data
292
+ .map(heavyComputation)
293
+ .filter(x => x > 0)
294
+ .reduce((a, b) => a + b),
295
+ [data]
296
+ );
297
+
298
+ return <div>{result}</div>;
299
+ }
300
+ ```
301
+
302
+ ## 보안 안티패턴
303
+
304
+ ### 1. 민감 정보 하드코딩
305
+
306
+ ```typescript
307
+ // ❌ API 키 하드코딩
308
+ const API_KEY = 'sk-1234567890abcdef';
309
+
310
+ // ✅ 환경 변수 사용
311
+ const API_KEY = process.env.NEXT_PUBLIC_API_KEY;
312
+ ```
313
+
314
+ ### 2. SQL Injection 취약점
315
+
316
+ ```typescript
317
+ // ❌ 직접 문자열 연결
318
+ const query = `SELECT * FROM users WHERE id = ${userId}`;
319
+
320
+ // ✅ 파라미터화된 쿼리
321
+ const query = 'SELECT * FROM users WHERE id = ?';
322
+ db.execute(query, [userId]);
323
+ ```
324
+
325
+ ## 에러 처리 안티패턴
326
+
327
+ ### 1. 빈 catch 블록
328
+
329
+ ```typescript
330
+ // ❌ 에러 무시
331
+ try {
332
+ riskyOperation();
333
+ } catch (e) {
334
+ // 아무것도 안 함
335
+ }
336
+
337
+ // ✅ 적절한 에러 처리
338
+ try {
339
+ riskyOperation();
340
+ } catch (error) {
341
+ console.error('Operation failed:', error);
342
+ showErrorNotification(error);
343
+ trackError(error);
344
+ }
345
+ ```
346
+
347
+ ### 2. 에러 타입 확인 없이 처리
348
+
349
+ ```typescript
350
+ // ❌ 모든 에러 동일하게 처리
351
+ try {
352
+ await fetchData();
353
+ } catch (error) {
354
+ showError('Failed'); // 구체적이지 않음
355
+ }
356
+
357
+ // ✅ 에러 타입별 처리
358
+ try {
359
+ await fetchData();
360
+ } catch (error) {
361
+ if (error instanceof NetworkError) {
362
+ showError('네트워크 연결을 확인해주세요');
363
+ } else if (error instanceof AuthError) {
364
+ redirectToLogin();
365
+ } else {
366
+ showError('알 수 없는 오류가 발생했습니다');
367
+ }
368
+ }
369
+ ```