@su-record/vibe 2.0.0 β 2.0.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/.claude/agents/explorer.md +48 -48
- package/.claude/agents/implementer.md +53 -53
- package/.claude/agents/searcher.md +54 -54
- package/.claude/agents/simplifier.md +119 -119
- package/.claude/agents/tester.md +49 -49
- package/.claude/commands/vibe.analyze.md +239 -239
- package/.claude/commands/vibe.continue.md +88 -88
- package/.claude/commands/vibe.diagram.md +178 -178
- package/.claude/commands/vibe.reason.md +306 -306
- package/.claude/commands/vibe.run.md +760 -760
- package/.claude/commands/vibe.spec.md +339 -339
- package/.claude/commands/vibe.tool.md +153 -153
- package/.claude/commands/vibe.ui.md +137 -137
- package/.claude/commands/vibe.verify.md +238 -238
- package/.claude/settings.json +152 -152
- package/.claude/settings.local.json +4 -57
- package/.vibe/config.json +9 -0
- package/.vibe/constitution.md +184 -184
- package/.vibe/rules/core/communication-guide.md +104 -104
- package/.vibe/rules/core/development-philosophy.md +52 -52
- package/.vibe/rules/core/quick-start.md +120 -120
- package/.vibe/rules/quality/bdd-contract-testing.md +388 -388
- package/.vibe/rules/quality/checklist.md +276 -276
- package/.vibe/rules/quality/testing-strategy.md +437 -437
- package/.vibe/rules/standards/anti-patterns.md +369 -369
- package/.vibe/rules/standards/code-structure.md +291 -291
- package/.vibe/rules/standards/complexity-metrics.md +312 -312
- package/.vibe/rules/standards/naming-conventions.md +198 -198
- package/.vibe/rules/tools/mcp-hi-ai-guide.md +665 -665
- package/.vibe/rules/tools/mcp-workflow.md +51 -51
- package/.vibe/setup.sh +31 -31
- package/CLAUDE.md +122 -122
- package/LICENSE +21 -21
- package/README.md +568 -568
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +391 -406
- package/dist/cli/index.js.map +1 -1
- package/dist/lib/MemoryManager.js +92 -92
- package/dist/lib/PythonParser.js +108 -108
- package/dist/lib/gemini-mcp.js +15 -15
- package/dist/lib/gemini-oauth.js +35 -35
- package/dist/lib/gpt-mcp.js +17 -17
- package/dist/lib/gpt-oauth.js +44 -44
- package/dist/tools/analytics/getUsageAnalytics.js +12 -12
- package/dist/tools/memory/createMemoryTimeline.js +10 -10
- package/dist/tools/memory/getMemoryGraph.js +12 -12
- package/dist/tools/memory/getSessionContext.js +9 -9
- package/dist/tools/memory/linkMemories.js +14 -14
- package/dist/tools/memory/listMemories.js +4 -4
- package/dist/tools/memory/recallMemory.js +4 -4
- package/dist/tools/memory/saveMemory.js +4 -4
- package/dist/tools/memory/searchMemoriesAdvanced.js +22 -22
- package/dist/tools/planning/generatePrd.js +46 -46
- package/dist/tools/prompt/enhancePromptGemini.js +160 -160
- package/dist/tools/reasoning/applyReasoningFramework.js +56 -56
- package/dist/tools/semantic/analyzeDependencyGraph.js +12 -12
- package/package.json +67 -67
- package/templates/constitution-template.md +184 -184
- package/templates/contract-backend-template.md +517 -517
- package/templates/contract-frontend-template.md +594 -594
- package/templates/feature-template.md +96 -96
- package/templates/hooks-template.json +103 -103
- package/templates/spec-template.md +199 -199
- package/dist/lib/vibe-mcp.d.ts.map +0 -1
- package/dist/lib/vibe-mcp.js.map +0 -1
|
@@ -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
|
+
```
|