@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,446 +1,446 @@
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
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