@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,446 +0,0 @@
1
- # ๐Ÿ“ฑ TypeScript + React Native ํ’ˆ์งˆ ๊ทœ์น™
2
-
3
- ## ํ•ต์‹ฌ ์›์น™ (core + React์—์„œ ์ƒ์†)
4
-
5
- ```markdown
6
- โœ… ๋‹จ์ผ ์ฑ…์ž„ (SRP)
7
- โœ… ์ค‘๋ณต ์ œ๊ฑฐ (DRY)
8
- โœ… ์žฌ์‚ฌ์šฉ์„ฑ
9
- โœ… ๋‚ฎ์€ ๋ณต์žก๋„
10
- โœ… ํ•จ์ˆ˜ โ‰ค 30์ค„, JSX โ‰ค 50์ค„
11
- โœ… React ๊ทœ์น™ ๋ชจ๋‘ ์ ์šฉ
12
- ```
13
-
14
- ## React Native ํŠนํ™” ๊ทœ์น™
15
-
16
- ### 1. ํ”Œ๋žซํผ๋ณ„ ์ฝ”๋“œ ๋ถ„๋ฆฌ
17
-
18
- ```typescript
19
- // โœ… ํŒŒ์ผ ํ™•์žฅ์ž๋กœ ๋ถ„๋ฆฌ
20
- Button.ios.tsx // iOS ์ „์šฉ
21
- Button.android.tsx // Android ์ „์šฉ
22
- Button.tsx // ๊ณตํ†ต
23
-
24
- // โœ… Platform API ์‚ฌ์šฉ
25
- import { Platform, StyleSheet } from 'react-native';
26
-
27
- const styles = StyleSheet.create({
28
- container: {
29
- ...Platform.select({
30
- ios: {
31
- shadowColor: '#000',
32
- shadowOffset: { width: 0, height: 2 },
33
- shadowOpacity: 0.25,
34
- },
35
- android: {
36
- elevation: 4,
37
- },
38
- }),
39
- },
40
- });
41
-
42
- // โœ… Platform.OS ์ฒดํฌ
43
- if (Platform.OS === 'ios') {
44
- // iOS ์ „์šฉ ๋กœ์ง
45
- } else if (Platform.OS === 'android') {
46
- // Android ์ „์šฉ ๋กœ์ง
47
- }
48
- ```
49
-
50
- ### 2. StyleSheet ์‚ฌ์šฉ (์ธ๋ผ์ธ ์Šคํƒ€์ผ ์ง€์–‘)
51
-
52
- ```typescript
53
- // โŒ ์ธ๋ผ์ธ ์Šคํƒ€์ผ (์„ฑ๋Šฅ ์ €ํ•˜)
54
- <View style={{ flex: 1, padding: 16, backgroundColor: '#fff' }} />
55
-
56
- // โœ… StyleSheet (์ตœ์ ํ™”๋จ)
57
- import { StyleSheet } from 'react-native';
58
-
59
- const styles = StyleSheet.create({
60
- container: {
61
- flex: 1,
62
- padding: 16,
63
- backgroundColor: '#fff',
64
- },
65
- });
66
-
67
- <View style={styles.container} />
68
-
69
- // โœ… ์กฐ๊ฑด๋ถ€ ์Šคํƒ€์ผ
70
- <View style={[
71
- styles.container,
72
- isActive && styles.active,
73
- { marginTop: offset }, // ๋™์  ๊ฐ’๋งŒ ์ธ๋ผ์ธ
74
- ]} />
75
- ```
76
-
77
- ### 3. FlatList ์ตœ์ ํ™”
78
-
79
- ```typescript
80
- // โœ… FlatList ์„ฑ๋Šฅ ์ตœ์ ํ™”
81
- interface User {
82
- id: string;
83
- name: string;
84
- avatar: string;
85
- }
86
-
87
- const UserList = ({ users }: { users: User[] }) => {
88
- const renderItem = useCallback(({ item }: { item: User }) => {
89
- return <UserCard user={item} />;
90
- }, []);
91
-
92
- const keyExtractor = useCallback((item: User) => item.id, []);
93
-
94
- return (
95
- <FlatList
96
- data={users}
97
- renderItem={renderItem}
98
- keyExtractor={keyExtractor}
99
- // ์„ฑ๋Šฅ ์ตœ์ ํ™” ์˜ต์…˜
100
- removeClippedSubviews={true}
101
- maxToRenderPerBatch={10}
102
- updateCellsBatchingPeriod={50}
103
- initialNumToRender={10}
104
- windowSize={5}
105
- // ํ—ค๋” ๊ณ ์ •
106
- stickyHeaderIndices={[0]}
107
- // ๋ฆฌ์ŠคํŠธ ๋ถ„๋ฆฌ
108
- ItemSeparatorComponent={() => <View style={styles.separator} />}
109
- // ๋นˆ ์ƒํƒœ
110
- ListEmptyComponent={<EmptyState />}
111
- />
112
- );
113
- };
114
-
115
- // โœ… UserCard ๋ฉ”๋ชจ์ด์ œ์ด์…˜
116
- const UserCard = React.memo<{ user: User }>(({ user }) => {
117
- return (
118
- <View style={styles.card}>
119
- <Image source={{ uri: user.avatar }} style={styles.avatar} />
120
- <Text>{user.name}</Text>
121
- </View>
122
- );
123
- });
124
- ```
125
-
126
- ### 4. Navigation (React Navigation)
127
-
128
- ```typescript
129
- // โœ… ํƒ€์ž… ์•ˆ์ „ํ•œ ๋„ค๋น„๊ฒŒ์ด์…˜
130
- import { NavigationContainer } from '@react-navigation/native';
131
- import { createNativeStackNavigator } from '@react-navigation/native-stack';
132
-
133
- // ๋„ค๋น„๊ฒŒ์ด์…˜ ํƒ€์ž… ์ •์˜
134
- type RootStackParamList = {
135
- Home: undefined;
136
- UserProfile: { userId: string };
137
- Settings: { section?: string };
138
- };
139
-
140
- const Stack = createNativeStackNavigator<RootStackParamList>();
141
-
142
- function App() {
143
- return (
144
- <NavigationContainer>
145
- <Stack.Navigator>
146
- <Stack.Screen name="Home" component={HomeScreen} />
147
- <Stack.Screen name="UserProfile" component={UserProfileScreen} />
148
- <Stack.Screen name="Settings" component={SettingsScreen} />
149
- </Stack.Navigator>
150
- </NavigationContainer>
151
- );
152
- }
153
-
154
- // โœ… ํƒ€์ž… ์•ˆ์ „ํ•œ ๋„ค๋น„๊ฒŒ์ด์…˜ ํ›…
155
- import { NativeStackNavigationProp } from '@react-navigation/native-stack';
156
- import { useNavigation } from '@react-navigation/native';
157
-
158
- type HomeScreenNavigationProp = NativeStackNavigationProp<
159
- RootStackParamList,
160
- 'Home'
161
- >;
162
-
163
- function HomeScreen() {
164
- const navigation = useNavigation<HomeScreenNavigationProp>();
165
-
166
- const navigateToProfile = (userId: string) => {
167
- navigation.navigate('UserProfile', { userId }); // ํƒ€์ž… ์•ˆ์ „
168
- };
169
-
170
- return <Button onPress={() => navigateToProfile('123')} title="Profile" />;
171
- }
172
- ```
173
-
174
- ### 5. AsyncStorage (๋ฐ์ดํ„ฐ ์ €์žฅ)
175
-
176
- ```typescript
177
- import AsyncStorage from '@react-native-async-storage/async-storage';
178
-
179
- // โœ… ํƒ€์ž… ์•ˆ์ „ํ•œ Storage ๋ž˜ํผ
180
- class Storage {
181
- static async set<T>(key: string, value: T): Promise<void> {
182
- await AsyncStorage.setItem(key, JSON.stringify(value));
183
- }
184
-
185
- static async get<T>(key: string): Promise<T | null> {
186
- const value = await AsyncStorage.getItem(key);
187
- return value ? JSON.parse(value) : null;
188
- }
189
-
190
- static async remove(key: string): Promise<void> {
191
- await AsyncStorage.removeItem(key);
192
- }
193
- }
194
-
195
- // ์‚ฌ์šฉ
196
- interface User {
197
- id: string;
198
- name: string;
199
- }
200
-
201
- await Storage.set<User>('user', { id: '123', name: 'John' });
202
- const user = await Storage.get<User>('user');
203
- ```
204
-
205
- ### 6. ์ด๋ฏธ์ง€ ์ตœ์ ํ™”
206
-
207
- ```typescript
208
- import { Image } from 'react-native';
209
- import FastImage from 'react-native-fast-image';
210
-
211
- // โœ… FastImage ์‚ฌ์šฉ (์บ์‹ฑ, ์„ฑ๋Šฅ)
212
- <FastImage
213
- source={{
214
- uri: user.avatar,
215
- priority: FastImage.priority.high,
216
- }}
217
- style={styles.avatar}
218
- resizeMode={FastImage.resizeMode.cover}
219
- />
220
-
221
- // โœ… ๋กœ์ปฌ ์ด๋ฏธ์ง€
222
- <Image source={require('./assets/logo.png')} style={styles.logo} />
223
-
224
- // โœ… ์กฐ๊ฑด๋ถ€ ๋กœ๋”ฉ
225
- {imageUrl && (
226
- <Image
227
- source={{ uri: imageUrl }}
228
- defaultSource={require('./assets/placeholder.png')}
229
- />
230
- )}
231
- ```
232
-
233
- ### 7. SafeAreaView (์•ˆ์ „ ์˜์—ญ)
234
-
235
- ```typescript
236
- import { SafeAreaView } from 'react-native-safe-area-context';
237
-
238
- // โœ… SafeAreaView ์‚ฌ์šฉ (๋…ธ์น˜/์ƒํƒœ๋ฐ” ๋Œ€์‘)
239
- function Screen() {
240
- return (
241
- <SafeAreaView style={styles.container} edges={['top', 'bottom']}>
242
- <Text>Content</Text>
243
- </SafeAreaView>
244
- );
245
- }
246
-
247
- // โœ… useSafeAreaInsets ํ›…
248
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
249
-
250
- function CustomHeader() {
251
- const insets = useSafeAreaInsets();
252
-
253
- return (
254
- <View style={{ paddingTop: insets.top }}>
255
- <Text>Header</Text>
256
- </View>
257
- );
258
- }
259
- ```
260
-
261
- ### 8. Hooks ์ตœ์ ํ™”
262
-
263
- ```typescript
264
- // โœ… useCallback (์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ)
265
- const handlePress = useCallback(() => {
266
- navigation.navigate('UserProfile', { userId });
267
- }, [navigation, userId]);
268
-
269
- // โœ… useMemo (๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ)
270
- const sortedUsers = useMemo(() => {
271
- return users.sort((a, b) => a.name.localeCompare(b.name));
272
- }, [users]);
273
-
274
- // โœ… Custom Hook (๋กœ์ง ์žฌ์‚ฌ์šฉ)
275
- function useKeyboard() {
276
- const [isVisible, setIsVisible] = useState(false);
277
-
278
- useEffect(() => {
279
- const showSubscription = Keyboard.addListener('keyboardDidShow', () => {
280
- setIsVisible(true);
281
- });
282
- const hideSubscription = Keyboard.addListener('keyboardDidHide', () => {
283
- setIsVisible(false);
284
- });
285
-
286
- return () => {
287
- showSubscription.remove();
288
- hideSubscription.remove();
289
- };
290
- }, []);
291
-
292
- return isVisible;
293
- }
294
- ```
295
-
296
- ### 9. ๊ถŒํ•œ ์ฒ˜๋ฆฌ
297
-
298
- ```typescript
299
- import { check, request, PERMISSIONS, RESULTS } from 'react-native-permissions';
300
- import { Platform } from 'react-native';
301
-
302
- // โœ… ๊ถŒํ•œ ์ฒดํฌ ๋ฐ ์š”์ฒญ
303
- async function requestCameraPermission(): Promise<boolean> {
304
- const permission =
305
- Platform.OS === 'ios'
306
- ? PERMISSIONS.IOS.CAMERA
307
- : PERMISSIONS.ANDROID.CAMERA;
308
-
309
- const result = await check(permission);
310
-
311
- switch (result) {
312
- case RESULTS.GRANTED:
313
- return true;
314
- case RESULTS.DENIED:
315
- const requested = await request(permission);
316
- return requested === RESULTS.GRANTED;
317
- case RESULTS.BLOCKED:
318
- // ์„ค์ •์œผ๋กœ ์ด๋™ ์•ˆ๋‚ด
319
- return false;
320
- default:
321
- return false;
322
- }
323
- }
324
- ```
325
-
326
- ### 10. ์—๋Ÿฌ ๊ฒฝ๊ณ„ (Error Boundary)
327
-
328
- ```typescript
329
- // โœ… React Native์šฉ Error Boundary
330
- import React, { Component, ErrorInfo, ReactNode } from 'react';
331
- import { View, Text, Button } from 'react-native';
332
-
333
- interface Props {
334
- children: ReactNode;
335
- }
336
-
337
- interface State {
338
- hasError: boolean;
339
- error?: Error;
340
- }
341
-
342
- class ErrorBoundary extends Component<Props, State> {
343
- constructor(props: Props) {
344
- super(props);
345
- this.state = { hasError: false };
346
- }
347
-
348
- static getDerivedStateFromError(error: Error): State {
349
- return { hasError: true, error };
350
- }
351
-
352
- componentDidCatch(error: Error, errorInfo: ErrorInfo) {
353
- console.error('Error caught:', error, errorInfo);
354
- // ์—๋Ÿฌ ๋กœ๊น… ์„œ๋น„์Šค (Sentry ๋“ฑ)
355
- }
356
-
357
- handleReset = () => {
358
- this.setState({ hasError: false, error: undefined });
359
- };
360
-
361
- render() {
362
- if (this.state.hasError) {
363
- return (
364
- <View style={styles.errorContainer}>
365
- <Text style={styles.errorText}>Something went wrong</Text>
366
- <Button title="Try Again" onPress={this.handleReset} />
367
- </View>
368
- );
369
- }
370
-
371
- return this.props.children;
372
- }
373
- }
374
- ```
375
-
376
- ## ์•ˆํ‹ฐํŒจํ„ด
377
-
378
- ```typescript
379
- // โŒ ScrollView๋กœ ๊ธด ๋ฆฌ์ŠคํŠธ
380
- <ScrollView>
381
- {users.map(user => <UserCard key={user.id} user={user} />)}
382
- </ScrollView>
383
-
384
- // โœ… FlatList ์‚ฌ์šฉ
385
- <FlatList data={users} renderItem={renderItem} />
386
-
387
- // โŒ ์ค‘์ฒฉ๋œ FlatList (์„ฑ๋Šฅ ์ €ํ•˜)
388
- <FlatList
389
- data={categories}
390
- renderItem={({ item }) => (
391
- <FlatList data={item.items} renderItem={renderItem} />
392
- )}
393
- />
394
-
395
- // โœ… ๋‹จ์ผ FlatList + ์„น์…˜
396
- <SectionList sections={sections} renderItem={renderItem} />
397
-
398
- // โŒ ๋น„๋™๊ธฐ setState in useEffect cleanup
399
- useEffect(() => {
400
- return () => {
401
- setData(null); // โŒ ์–ธ๋งˆ์šดํŠธ ํ›„ setState
402
- };
403
- }, []);
404
-
405
- // โœ… isMounted ์ฒดํฌ
406
- useEffect(() => {
407
- let isMounted = true;
408
-
409
- fetchData().then(data => {
410
- if (isMounted) setData(data);
411
- });
412
-
413
- return () => {
414
- isMounted = false;
415
- };
416
- }, []);
417
- ```
418
-
419
- ## ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋„๊ตฌ
420
-
421
- ```bash
422
- # Flipper (๋””๋ฒ„๊น…)
423
- npx react-native-flipper
424
-
425
- # Bundle ๋ถ„์„
426
- npx react-native bundle --platform android --dev false \
427
- --entry-file index.js --bundle-output android.bundle
428
-
429
- # ๋ฉ”๋ชจ๋ฆฌ ํ”„๋กœํŒŒ์ผ๋ง (Flipper ์‚ฌ์šฉ)
430
- ```
431
-
432
- ## ์ฒดํฌ๋ฆฌ์ŠคํŠธ
433
-
434
- React Native ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ:
435
-
436
- - [ ] StyleSheet ์‚ฌ์šฉ (์ธ๋ผ์ธ ์ง€์–‘)
437
- - [ ] FlatList ์ตœ์ ํ™” (๊ธด ๋ฆฌ์ŠคํŠธ)
438
- - [ ] Platform ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ
439
- - [ ] ํƒ€์ž… ์•ˆ์ „ํ•œ Navigation
440
- - [ ] SafeAreaView ์‚ฌ์šฉ
441
- - [ ] FastImage ์‚ฌ์šฉ (์ด๋ฏธ์ง€)
442
- - [ ] useCallback/useMemo ์ตœ์ ํ™”
443
- - [ ] ๊ถŒํ•œ ์ฒ˜๋ฆฌ (์นด๋ฉ”๋ผ, ์œ„์น˜ ๋“ฑ)
444
- - [ ] Error Boundary ์ ์šฉ
445
- - [ ] AsyncStorage ํƒ€์ž… ๋ž˜ํผ
446
- - [ ] ๋ณต์žก๋„ โ‰ค 10