@su-record/vibe 0.4.5 โ 0.4.6
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/.claude/agents/simplifier.md +1 -1
- package/.claude/commands/vibe.analyze.md +1 -1
- package/.claude/commands/vibe.run.md +1 -1
- package/.claude/commands/vibe.spec.md +2 -2
- package/.claude/commands/vibe.verify.md +1 -1
- package/.claude/settings.local.json +3 -1
- package/README.md +4 -4
- package/bin/vibe +41 -13
- package/package.json +1 -1
- package/templates/hooks-template.json +1 -1
- package/.agent/rules/core/communication-guide.md +0 -104
- package/.agent/rules/core/development-philosophy.md +0 -53
- package/.agent/rules/core/quick-start.md +0 -121
- package/.agent/rules/languages/dart-flutter.md +0 -509
- package/.agent/rules/languages/go.md +0 -396
- package/.agent/rules/languages/java-spring.md +0 -586
- package/.agent/rules/languages/kotlin-android.md +0 -491
- package/.agent/rules/languages/python-django.md +0 -371
- package/.agent/rules/languages/python-fastapi.md +0 -386
- package/.agent/rules/languages/rust.md +0 -425
- package/.agent/rules/languages/swift-ios.md +0 -516
- package/.agent/rules/languages/typescript-nextjs.md +0 -441
- package/.agent/rules/languages/typescript-node.md +0 -375
- package/.agent/rules/languages/typescript-react-native.md +0 -446
- package/.agent/rules/languages/typescript-react.md +0 -525
- package/.agent/rules/languages/typescript-vue.md +0 -353
- package/.agent/rules/quality/bdd-contract-testing.md +0 -388
- package/.agent/rules/quality/checklist.md +0 -276
- package/.agent/rules/quality/testing-strategy.md +0 -437
- package/.agent/rules/standards/anti-patterns.md +0 -369
- package/.agent/rules/standards/code-structure.md +0 -291
- package/.agent/rules/standards/complexity-metrics.md +0 -312
- package/.agent/rules/standards/naming-conventions.md +0 -198
- package/.agent/rules/tools/mcp-hi-ai-guide.md +0 -665
- package/.agent/rules/tools/mcp-workflow.md +0 -51
|
@@ -1,369 +0,0 @@
|
|
|
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,291 +0,0 @@
|
|
|
1
|
-
# ๐๏ธ ์ฝ๋ ๊ตฌ์กฐ ์๋ํ ๊ท์น
|
|
2
|
-
|
|
3
|
-
## ์ปดํฌ๋ํธ ๊ตฌ์กฐ (์๊ฒฉํ ์์)
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
// 1. Import ๋ฌธ
|
|
7
|
-
import React, { useState, useEffect } from 'react';
|
|
8
|
-
|
|
9
|
-
// 2. ํ์
/์ธํฐํ์ด์ค ์ ์
|
|
10
|
-
interface Props {
|
|
11
|
-
userId: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// 3. ์ปดํฌ๋ํธ ์ ์
|
|
15
|
-
function UserProfile({ userId }: Props) {
|
|
16
|
-
// 4. State & Refs
|
|
17
|
-
const [user, setUser] = useState<User | null>(null);
|
|
18
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
19
|
-
|
|
20
|
-
// 5. Custom Hooks
|
|
21
|
-
const { isAuthenticated } = useAuth();
|
|
22
|
-
const { data, loading } = useUserData(userId);
|
|
23
|
-
|
|
24
|
-
// 6. Event Handlers
|
|
25
|
-
const handleSubmit = (e: FormEvent) => {
|
|
26
|
-
e.preventDefault();
|
|
27
|
-
// ...
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// 7. Effects
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
// ...
|
|
33
|
-
}, [userId]);
|
|
34
|
-
|
|
35
|
-
// 8. Early returns
|
|
36
|
-
if (loading) return <Spinner />;
|
|
37
|
-
if (!user) return <NotFound />;
|
|
38
|
-
|
|
39
|
-
// 9. Main return JSX
|
|
40
|
-
return (
|
|
41
|
-
<div>
|
|
42
|
-
{/* ... */}
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## ํจ์ ๋ถ๋ฆฌ ๊ธฐ์ค
|
|
49
|
-
|
|
50
|
-
### 1. ํจ์ ๊ธธ์ด ๊ธฐ์ค
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
// โ 20์ค ์ด๊ณผ - ๋ถ๋ฆฌ ํ์
|
|
54
|
-
function processUserData(user: User) {
|
|
55
|
-
// 30์ค์ ๋ณต์กํ ๋ก์ง
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// โ
๋จ์ผ ์ฑ
์์ผ๋ก ๋ถ๋ฆฌ
|
|
59
|
-
function processUserData(user: User) {
|
|
60
|
-
const validated = validateUser(user);
|
|
61
|
-
const transformed = transformUserData(validated);
|
|
62
|
-
return saveUserData(transformed);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function validateUser(user: User) { /* ... */ }
|
|
66
|
-
function transformUserData(user: User) { /* ... */ }
|
|
67
|
-
function saveUserData(user: User) { /* ... */ }
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### 2. ์ปดํฌ๋ํธ JSX ๊ธธ์ด ๊ธฐ์ค
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
// โ JSX 50์ค ์ด๊ณผ - ๋ถ๋ฆฌ ํ์
|
|
74
|
-
function Dashboard() {
|
|
75
|
-
return (
|
|
76
|
-
<div>
|
|
77
|
-
{/* 60์ค์ ๋ณต์กํ JSX */}
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// โ
์๋ธ ์ปดํฌ๋ํธ ์ถ์ถ
|
|
83
|
-
function Dashboard() {
|
|
84
|
-
return (
|
|
85
|
-
<div>
|
|
86
|
-
<DashboardHeader />
|
|
87
|
-
<DashboardContent />
|
|
88
|
-
<DashboardFooter />
|
|
89
|
-
</div>
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function DashboardHeader() { /* ... */ }
|
|
94
|
-
function DashboardContent() { /* ... */ }
|
|
95
|
-
function DashboardFooter() { /* ... */ }
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### 3. ์ค์ฒฉ ๊น์ด ๊ธฐ์ค
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
// โ ์ค์ฒฉ 3๋จ๊ณ ์ด๊ณผ
|
|
102
|
-
function processData(data: Data) {
|
|
103
|
-
if (data) {
|
|
104
|
-
if (data.isValid) {
|
|
105
|
-
if (data.user) {
|
|
106
|
-
if (data.user.isActive) {
|
|
107
|
-
// ๋๋ฌด ๊น์ ์ค์ฒฉ
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// โ
Early return์ผ๋ก ํํํ
|
|
115
|
-
function processData(data: Data) {
|
|
116
|
-
if (!data) return null;
|
|
117
|
-
if (!data.isValid) return null;
|
|
118
|
-
if (!data.user) return null;
|
|
119
|
-
if (!data.user.isActive) return null;
|
|
120
|
-
|
|
121
|
-
// ๋ก์ง ์คํ
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### 4. Cyclomatic Complexity > 10
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
// โ ๋ณต์ก๋ ๋์ (15)
|
|
129
|
-
function calculatePrice(item: Item) {
|
|
130
|
-
let price = item.basePrice;
|
|
131
|
-
if (item.discount) price *= 0.9;
|
|
132
|
-
if (item.bulk) price *= 0.8;
|
|
133
|
-
if (item.seasonal) price *= 0.95;
|
|
134
|
-
if (item.member) price *= 0.85;
|
|
135
|
-
if (item.firstTime) price *= 0.9;
|
|
136
|
-
// ... ๋ ๋ง์ ์กฐ๊ฑด
|
|
137
|
-
return price;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// โ
๋ณต์ก๋ ๊ฐ์ (3)
|
|
141
|
-
function calculatePrice(item: Item) {
|
|
142
|
-
const basePrice = item.basePrice;
|
|
143
|
-
const discounts = getApplicableDiscounts(item);
|
|
144
|
-
return applyDiscounts(basePrice, discounts);
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### 5. Cognitive Complexity > 15
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
// โ ์ธ์ง ๋ณต์ก๋ ๋์
|
|
152
|
-
function processOrder(order: Order) {
|
|
153
|
-
if (order.isPremium) {
|
|
154
|
-
for (let item of order.items) {
|
|
155
|
-
if (item.category === 'electronics') {
|
|
156
|
-
if (item.price > 1000) {
|
|
157
|
-
// ์ค์ฒฉ๋ ๋ณต์กํ ๋ก์ง
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// โ
์ธ์ง ๋ณต์ก๋ ๊ฐ์
|
|
165
|
-
function processOrder(order: Order) {
|
|
166
|
-
if (!order.isPremium) return;
|
|
167
|
-
|
|
168
|
-
const electronics = filterElectronics(order.items);
|
|
169
|
-
const expensive = filterExpensive(electronics);
|
|
170
|
-
|
|
171
|
-
processItems(expensive);
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## ํ์ผ ๊ตฌ์กฐ ํ์ค
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
// ๐ user-profile.component.tsx
|
|
179
|
-
|
|
180
|
-
// 1. Imports
|
|
181
|
-
import { ... } from 'react';
|
|
182
|
-
import { ... } from '@/lib';
|
|
183
|
-
|
|
184
|
-
// 2. Types
|
|
185
|
-
interface UserProfileProps { }
|
|
186
|
-
type UserRole = 'admin' | 'user';
|
|
187
|
-
|
|
188
|
-
// 3. Constants
|
|
189
|
-
const MAX_BIO_LENGTH = 500;
|
|
190
|
-
const DEFAULT_AVATAR = '/avatar.png';
|
|
191
|
-
|
|
192
|
-
// 4. Helper Functions (๋ด๋ถ ์ ์ฉ)
|
|
193
|
-
function formatUserName(name: string) { }
|
|
194
|
-
|
|
195
|
-
// 5. Main Component
|
|
196
|
-
export function UserProfile() { }
|
|
197
|
-
|
|
198
|
-
// 6. Sub Components (exportํ์ง ์์)
|
|
199
|
-
function ProfileHeader() { }
|
|
200
|
-
function ProfileContent() { }
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## ๋ชจ๋ ๊ตฌ์ฑ ์์น
|
|
204
|
-
|
|
205
|
-
### 1. ์์ง๋ (Cohesion)
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
// โ
๋์ ์์ง๋ - ๊ด๋ จ ๊ธฐ๋ฅ๋ง ๋ชจ์
|
|
209
|
-
// ๐ user.service.ts
|
|
210
|
-
export class UserService {
|
|
211
|
-
getUser(id: string) { }
|
|
212
|
-
updateUser(id: string, data: User) { }
|
|
213
|
-
deleteUser(id: string) { }
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// โ ๋ฎ์ ์์ง๋ - ๊ด๋ จ ์๋ ๊ธฐ๋ฅ ํผ์ฌ
|
|
217
|
-
// ๐ utils.ts (์ํฐํจํด)
|
|
218
|
-
export class Utils {
|
|
219
|
-
validateEmail(email: string) { }
|
|
220
|
-
formatCurrency(amount: number) { }
|
|
221
|
-
uploadFile(file: File) { }
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### 2. ๊ฒฐํฉ๋ (Coupling)
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
// โ
๋์จํ ๊ฒฐํฉ - ์ธํฐํ์ด์ค ์์กด
|
|
229
|
-
interface Storage {
|
|
230
|
-
save(key: string, value: unknown): void;
|
|
231
|
-
load(key: string): unknown;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
class UserService {
|
|
235
|
-
constructor(private storage: Storage) { }
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// โ ๊ฐํ ๊ฒฐํฉ - ๊ตฌํ์ฒด ์ง์ ์์กด
|
|
239
|
-
class UserService {
|
|
240
|
-
private storage = new LocalStorage(); // ์ง์ ์์กด
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## ํจ์ ๋งค๊ฐ๋ณ์ ์ ํ
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
// โ ๋งค๊ฐ๋ณ์ 5๊ฐ ์ด๊ณผ
|
|
248
|
-
function createUser(
|
|
249
|
-
name: string,
|
|
250
|
-
email: string,
|
|
251
|
-
age: number,
|
|
252
|
-
address: string,
|
|
253
|
-
phone: string,
|
|
254
|
-
role: string
|
|
255
|
-
) { }
|
|
256
|
-
|
|
257
|
-
// โ
๊ฐ์ฒด๋ก ๊ทธ๋ฃนํ
|
|
258
|
-
interface CreateUserParams {
|
|
259
|
-
name: string;
|
|
260
|
-
email: string;
|
|
261
|
-
age: number;
|
|
262
|
-
address: string;
|
|
263
|
-
phone: string;
|
|
264
|
-
role: string;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function createUser(params: CreateUserParams) { }
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## ์ํ ์์กด์ฑ ๋ฐฉ์ง
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
// โ ์ํ ์์กด์ฑ
|
|
274
|
-
// fileA.ts
|
|
275
|
-
import { funcB } from './fileB';
|
|
276
|
-
export function funcA() { funcB(); }
|
|
277
|
-
|
|
278
|
-
// fileB.ts
|
|
279
|
-
import { funcA } from './fileA'; // ์ํ!
|
|
280
|
-
export function funcB() { funcA(); }
|
|
281
|
-
|
|
282
|
-
// โ
๊ณตํต ๋ชจ๋ ๋ถ๋ฆฌ
|
|
283
|
-
// shared.ts
|
|
284
|
-
export function sharedFunc() { }
|
|
285
|
-
|
|
286
|
-
// fileA.ts
|
|
287
|
-
import { sharedFunc } from './shared';
|
|
288
|
-
|
|
289
|
-
// fileB.ts
|
|
290
|
-
import { sharedFunc } from './shared';
|
|
291
|
-
```
|