@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.
Files changed (35) hide show
  1. package/.claude/agents/simplifier.md +1 -1
  2. package/.claude/commands/vibe.analyze.md +1 -1
  3. package/.claude/commands/vibe.run.md +1 -1
  4. package/.claude/commands/vibe.spec.md +2 -2
  5. package/.claude/commands/vibe.verify.md +1 -1
  6. package/.claude/settings.local.json +3 -1
  7. package/README.md +4 -4
  8. package/bin/vibe +41 -13
  9. package/package.json +1 -1
  10. package/templates/hooks-template.json +1 -1
  11. package/.agent/rules/core/communication-guide.md +0 -104
  12. package/.agent/rules/core/development-philosophy.md +0 -53
  13. package/.agent/rules/core/quick-start.md +0 -121
  14. package/.agent/rules/languages/dart-flutter.md +0 -509
  15. package/.agent/rules/languages/go.md +0 -396
  16. package/.agent/rules/languages/java-spring.md +0 -586
  17. package/.agent/rules/languages/kotlin-android.md +0 -491
  18. package/.agent/rules/languages/python-django.md +0 -371
  19. package/.agent/rules/languages/python-fastapi.md +0 -386
  20. package/.agent/rules/languages/rust.md +0 -425
  21. package/.agent/rules/languages/swift-ios.md +0 -516
  22. package/.agent/rules/languages/typescript-nextjs.md +0 -441
  23. package/.agent/rules/languages/typescript-node.md +0 -375
  24. package/.agent/rules/languages/typescript-react-native.md +0 -446
  25. package/.agent/rules/languages/typescript-react.md +0 -525
  26. package/.agent/rules/languages/typescript-vue.md +0 -353
  27. package/.agent/rules/quality/bdd-contract-testing.md +0 -388
  28. package/.agent/rules/quality/checklist.md +0 -276
  29. package/.agent/rules/quality/testing-strategy.md +0 -437
  30. package/.agent/rules/standards/anti-patterns.md +0 -369
  31. package/.agent/rules/standards/code-structure.md +0 -291
  32. package/.agent/rules/standards/complexity-metrics.md +0 -312
  33. package/.agent/rules/standards/naming-conventions.md +0 -198
  34. package/.agent/rules/tools/mcp-hi-ai-guide.md +0 -665
  35. 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
- ```