timsquad 3.3.0 → 3.5.0

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 (196) hide show
  1. package/README.ko.md +288 -0
  2. package/README.md +158 -151
  3. package/dist/commands/compile.d.ts +3 -0
  4. package/dist/commands/compile.d.ts.map +1 -0
  5. package/dist/commands/compile.js +170 -0
  6. package/dist/commands/compile.js.map +1 -0
  7. package/dist/commands/daemon.d.ts.map +1 -1
  8. package/dist/commands/daemon.js +95 -5
  9. package/dist/commands/daemon.js.map +1 -1
  10. package/dist/commands/full.js +1 -0
  11. package/dist/commands/full.js.map +1 -1
  12. package/dist/commands/git/pr.js +6 -5
  13. package/dist/commands/git/pr.js.map +1 -1
  14. package/dist/commands/git/release.js +2 -7
  15. package/dist/commands/git/release.js.map +1 -1
  16. package/dist/commands/improve.js +2 -2
  17. package/dist/commands/improve.js.map +1 -1
  18. package/dist/commands/init.d.ts.map +1 -1
  19. package/dist/commands/init.js +12 -3
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/log.d.ts.map +1 -1
  22. package/dist/commands/log.js +2 -2
  23. package/dist/commands/log.js.map +1 -1
  24. package/dist/commands/metrics.d.ts.map +1 -1
  25. package/dist/commands/metrics.js +6 -2
  26. package/dist/commands/metrics.js.map +1 -1
  27. package/dist/commands/retro.js +8 -8
  28. package/dist/commands/retro.js.map +1 -1
  29. package/dist/commands/session.js +3 -3
  30. package/dist/commands/session.js.map +1 -1
  31. package/dist/commands/skills.d.ts +12 -0
  32. package/dist/commands/skills.d.ts.map +1 -0
  33. package/dist/commands/skills.js +228 -0
  34. package/dist/commands/skills.js.map +1 -0
  35. package/dist/commands/status.js +1 -1
  36. package/dist/commands/status.js.map +1 -1
  37. package/dist/commands/upgrade.d.ts.map +1 -1
  38. package/dist/commands/upgrade.js +23 -1
  39. package/dist/commands/upgrade.js.map +1 -1
  40. package/dist/daemon/entry.js +3 -3
  41. package/dist/daemon/entry.js.map +1 -1
  42. package/dist/daemon/event-queue.d.ts.map +1 -1
  43. package/dist/daemon/event-queue.js +2 -2
  44. package/dist/daemon/event-queue.js.map +1 -1
  45. package/dist/daemon/index.d.ts +4 -2
  46. package/dist/daemon/index.d.ts.map +1 -1
  47. package/dist/daemon/index.js +214 -52
  48. package/dist/daemon/index.js.map +1 -1
  49. package/dist/daemon/jsonl-watcher.d.ts +1 -0
  50. package/dist/daemon/jsonl-watcher.d.ts.map +1 -1
  51. package/dist/daemon/jsonl-watcher.js.map +1 -1
  52. package/dist/daemon/meta-cache.d.ts +1 -0
  53. package/dist/daemon/meta-cache.d.ts.map +1 -1
  54. package/dist/daemon/meta-cache.js +9 -0
  55. package/dist/daemon/meta-cache.js.map +1 -1
  56. package/dist/daemon/session-notes.d.ts +33 -0
  57. package/dist/daemon/session-notes.d.ts.map +1 -0
  58. package/dist/daemon/session-notes.js +74 -0
  59. package/dist/daemon/session-notes.js.map +1 -0
  60. package/dist/daemon/session-state.d.ts +27 -0
  61. package/dist/daemon/session-state.d.ts.map +1 -0
  62. package/dist/daemon/session-state.js +165 -0
  63. package/dist/daemon/session-state.js.map +1 -0
  64. package/dist/daemon/shutdown.d.ts.map +1 -1
  65. package/dist/daemon/shutdown.js +9 -1
  66. package/dist/daemon/shutdown.js.map +1 -1
  67. package/dist/index.js +4 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/lib/agent-generator.d.ts +4 -0
  70. package/dist/lib/agent-generator.d.ts.map +1 -1
  71. package/dist/lib/agent-generator.js +52 -3
  72. package/dist/lib/agent-generator.js.map +1 -1
  73. package/dist/lib/compile-rules.d.ts +66 -0
  74. package/dist/lib/compile-rules.d.ts.map +1 -0
  75. package/dist/lib/compile-rules.js +114 -0
  76. package/dist/lib/compile-rules.js.map +1 -0
  77. package/dist/lib/compiler.d.ts +105 -0
  78. package/dist/lib/compiler.d.ts.map +1 -0
  79. package/dist/lib/compiler.js +368 -0
  80. package/dist/lib/compiler.js.map +1 -0
  81. package/dist/lib/config.d.ts +1 -0
  82. package/dist/lib/config.d.ts.map +1 -1
  83. package/dist/lib/config.js +8 -1
  84. package/dist/lib/config.js.map +1 -1
  85. package/dist/lib/project.d.ts.map +1 -1
  86. package/dist/lib/project.js +8 -3
  87. package/dist/lib/project.js.map +1 -1
  88. package/dist/lib/skill-generator.d.ts.map +1 -1
  89. package/dist/lib/skill-generator.js +22 -1
  90. package/dist/lib/skill-generator.js.map +1 -1
  91. package/dist/lib/template.d.ts.map +1 -1
  92. package/dist/lib/template.js +6 -0
  93. package/dist/lib/template.js.map +1 -1
  94. package/dist/types/config.d.ts +1 -0
  95. package/dist/types/config.d.ts.map +1 -1
  96. package/dist/types/config.js +12 -1
  97. package/dist/types/config.js.map +1 -1
  98. package/dist/types/project.d.ts +1 -1
  99. package/dist/types/project.d.ts.map +1 -1
  100. package/dist/types/project.js +2 -0
  101. package/dist/types/project.js.map +1 -1
  102. package/package.json +4 -4
  103. package/templates/base/agents/base/tsq-architect.md +2 -2
  104. package/templates/base/agents/overlays/domain/mobile/_common.md +13 -0
  105. package/templates/base/knowledge/checklists/plan-quality.md +31 -0
  106. package/templates/base/knowledge/checklists/stability-verification.md +14 -0
  107. package/templates/base/skills/controller/SKILL.md +111 -0
  108. package/templates/base/skills/controller/references/README.md +35 -0
  109. package/templates/base/skills/controller/rules/README.md +18 -0
  110. package/templates/base/skills/mobile/dart/SKILL.md +69 -0
  111. package/templates/base/skills/mobile/dart/rules/async-patterns.md +112 -0
  112. package/templates/base/skills/mobile/dart/rules/code-style.md +96 -0
  113. package/templates/base/skills/mobile/dart/rules/null-safety.md +84 -0
  114. package/templates/base/skills/mobile/dart/rules/type-system.md +111 -0
  115. package/templates/base/skills/mobile/flutter/SKILL.md +89 -0
  116. package/templates/base/skills/mobile/flutter/ci-cd/SKILL.md +82 -0
  117. package/templates/base/skills/mobile/flutter/ci-cd/references/ci-cd-pipeline.md +314 -0
  118. package/templates/base/skills/mobile/flutter/ci-cd/rules/code-signing.md +106 -0
  119. package/templates/base/skills/mobile/flutter/ci-cd/rules/codemagic-setup.md +116 -0
  120. package/templates/base/skills/mobile/flutter/ci-cd/rules/fastlane-setup.md +105 -0
  121. package/templates/base/skills/mobile/flutter/ci-cd/rules/github-actions.md +112 -0
  122. package/templates/base/skills/mobile/flutter/ci-cd/rules/store-deployment.md +106 -0
  123. package/templates/base/skills/mobile/flutter/ci-cd/rules/versioning.md +107 -0
  124. package/templates/base/skills/mobile/flutter/i18n/SKILL.md +78 -0
  125. package/templates/base/skills/mobile/flutter/i18n/references/i18n-architecture.md +225 -0
  126. package/templates/base/skills/mobile/flutter/i18n/rules/arb-files.md +182 -0
  127. package/templates/base/skills/mobile/flutter/i18n/rules/locale-switching.md +226 -0
  128. package/templates/base/skills/mobile/flutter/i18n/rules/localization-setup.md +137 -0
  129. package/templates/base/skills/mobile/flutter/i18n/rules/plural-gender.md +159 -0
  130. package/templates/base/skills/mobile/flutter/i18n/rules/text-direction.md +199 -0
  131. package/templates/base/skills/mobile/flutter/monitoring/SKILL.md +81 -0
  132. package/templates/base/skills/mobile/flutter/monitoring/references/monitoring-architecture.md +269 -0
  133. package/templates/base/skills/mobile/flutter/monitoring/rules/analytics.md +227 -0
  134. package/templates/base/skills/mobile/flutter/monitoring/rules/crashlytics-setup.md +195 -0
  135. package/templates/base/skills/mobile/flutter/monitoring/rules/logging.md +258 -0
  136. package/templates/base/skills/mobile/flutter/monitoring/rules/performance-monitoring.md +248 -0
  137. package/templates/base/skills/mobile/flutter/monitoring/rules/sentry-integration.md +249 -0
  138. package/templates/base/skills/mobile/flutter/networking/SKILL.md +88 -0
  139. package/templates/base/skills/mobile/flutter/networking/references/api-client-architecture.md +305 -0
  140. package/templates/base/skills/mobile/flutter/networking/rules/caching.md +212 -0
  141. package/templates/base/skills/mobile/flutter/networking/rules/connectivity.md +213 -0
  142. package/templates/base/skills/mobile/flutter/networking/rules/dio-setup.md +159 -0
  143. package/templates/base/skills/mobile/flutter/networking/rules/error-handling.md +209 -0
  144. package/templates/base/skills/mobile/flutter/networking/rules/interceptors.md +205 -0
  145. package/templates/base/skills/mobile/flutter/networking/rules/retrofit-patterns.md +194 -0
  146. package/templates/base/skills/mobile/flutter/push-notifications/SKILL.md +87 -0
  147. package/templates/base/skills/mobile/flutter/push-notifications/references/notification-architecture.md +340 -0
  148. package/templates/base/skills/mobile/flutter/push-notifications/references/platform-setup.md +286 -0
  149. package/templates/base/skills/mobile/flutter/push-notifications/rules/background-processing.md +308 -0
  150. package/templates/base/skills/mobile/flutter/push-notifications/rules/deep-linking.md +217 -0
  151. package/templates/base/skills/mobile/flutter/push-notifications/rules/fcm-setup.md +164 -0
  152. package/templates/base/skills/mobile/flutter/push-notifications/rules/local-notifications.md +262 -0
  153. package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-handling.md +210 -0
  154. package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-permissions.md +246 -0
  155. package/templates/base/skills/mobile/flutter/push-notifications/rules/rich-notifications.md +320 -0
  156. package/templates/base/skills/mobile/flutter/references/freezed-patterns.md +162 -0
  157. package/templates/base/skills/mobile/flutter/references/project-structure.md +170 -0
  158. package/templates/base/skills/mobile/flutter/rules/animations.md +112 -0
  159. package/templates/base/skills/mobile/flutter/rules/architecture.md +121 -0
  160. package/templates/base/skills/mobile/flutter/rules/navigation-routing.md +117 -0
  161. package/templates/base/skills/mobile/flutter/rules/performance.md +112 -0
  162. package/templates/base/skills/mobile/flutter/rules/platform-adaptive.md +126 -0
  163. package/templates/base/skills/mobile/flutter/rules/state-management.md +110 -0
  164. package/templates/base/skills/mobile/flutter/rules/testing.md +131 -0
  165. package/templates/base/skills/mobile/flutter/rules/widget-conventions.md +122 -0
  166. package/templates/base/skills/mobile/flutter/security/SKILL.md +86 -0
  167. package/templates/base/skills/mobile/flutter/security/references/mobile-security-checklist.md +168 -0
  168. package/templates/base/skills/mobile/flutter/security/rules/api-key-protection.md +206 -0
  169. package/templates/base/skills/mobile/flutter/security/rules/authentication.md +248 -0
  170. package/templates/base/skills/mobile/flutter/security/rules/data-protection.md +271 -0
  171. package/templates/base/skills/mobile/flutter/security/rules/obfuscation.md +213 -0
  172. package/templates/base/skills/mobile/flutter/security/rules/secure-storage.md +171 -0
  173. package/templates/base/skills/mobile/flutter/security/rules/ssl-pinning.md +197 -0
  174. package/templates/base/skills/stability-verification/SKILL.md +64 -0
  175. package/templates/base/skills/stability-verification/references/release-checklist.md +34 -0
  176. package/templates/base/skills/stability-verification/references/security-fix-patterns.md +112 -0
  177. package/templates/base/skills/stability-verification/rules/verification-layers.md +67 -0
  178. package/templates/base/skills/stability-verification/rules/verification-workflow.md +69 -0
  179. package/templates/base/skills/stability-verification/scripts/verify.sh +294 -0
  180. package/templates/platforms/claude-code/CLAUDE.md.template +25 -0
  181. package/templates/platforms/claude-code/rules/build-gate.md +28 -0
  182. package/templates/platforms/claude-code/rules/completion-verification.md +30 -0
  183. package/templates/platforms/claude-code/rules/context-monitor.md +23 -0
  184. package/templates/platforms/claude-code/rules/plan-review.md +45 -0
  185. package/templates/platforms/claude-code/rules/quality-guards.md +43 -0
  186. package/templates/platforms/claude-code/rules/session-notes.md +18 -0
  187. package/templates/platforms/claude-code/rules/skill-suggest.md +27 -0
  188. package/templates/platforms/claude-code/scripts/build-gate.sh +73 -0
  189. package/templates/platforms/claude-code/scripts/completion-guard.sh +93 -0
  190. package/templates/platforms/claude-code/scripts/phase-guard.sh +79 -0
  191. package/templates/platforms/claude-code/scripts/safe-guard.sh +83 -0
  192. package/templates/platforms/claude-code/scripts/skill-rules.json +85 -0
  193. package/templates/platforms/claude-code/scripts/skill-suggest.sh +105 -0
  194. package/templates/platforms/claude-code/settings.json +111 -3
  195. package/templates/project-types/mobile-app/config.yaml +123 -0
  196. package/templates/project-types/mobile-app/process/workflow.xml +191 -0
@@ -0,0 +1,162 @@
1
+ ---
2
+ title: Freezed + JSON Serializable Patterns
3
+ category: guide
4
+ tags: freezed, json-serializable, immutable, dto
5
+ ---
6
+
7
+ ## Freezed + JSON Serializable
8
+
9
+ 불변 데이터 모델 + JSON 직렬화 패턴. Domain Entity와 DTO 분리.
10
+
11
+ ### 기본 설정
12
+
13
+ ```yaml
14
+ # pubspec.yaml
15
+ dependencies:
16
+ freezed_annotation: ^2.4.0
17
+ json_annotation: ^4.9.0
18
+
19
+ dev_dependencies:
20
+ freezed: ^2.5.0
21
+ json_serializable: ^6.8.0
22
+ build_runner: ^2.4.0
23
+ ```
24
+
25
+ ```bash
26
+ # 코드 생성
27
+ dart run build_runner build --delete-conflicting-outputs
28
+
29
+ # 감시 모드 (개발 중)
30
+ dart run build_runner watch --delete-conflicting-outputs
31
+ ```
32
+
33
+ ### Domain Entity (freezed)
34
+
35
+ ```dart
36
+ import 'package:freezed_annotation/freezed_annotation.dart';
37
+
38
+ part 'match.freezed.dart';
39
+
40
+ @freezed
41
+ class Match with _$Match {
42
+ const factory Match({
43
+ required String id,
44
+ required Sport sport,
45
+ required String title,
46
+ required DateTime scheduledAt,
47
+ required MatchLocation location,
48
+ required int maxPlayers,
49
+ @Default([]) List<String> playerIds,
50
+ @Default(MatchStatus.open) MatchStatus status,
51
+ }) = _Match;
52
+
53
+ // 커스텀 getter (const factory 밑에 private 생성자 필요)
54
+ const Match._();
55
+
56
+ bool get isFull => playerIds.length >= maxPlayers;
57
+ bool get isUpcoming => scheduledAt.isAfter(DateTime.now());
58
+ int get availableSlots => maxPlayers - playerIds.length;
59
+ }
60
+
61
+ enum Sport { tennis, badminton, pickleball, paddle }
62
+ enum MatchStatus { open, full, inProgress, completed, cancelled }
63
+ ```
64
+
65
+ ### DTO (freezed + json_serializable)
66
+
67
+ ```dart
68
+ import 'package:freezed_annotation/freezed_annotation.dart';
69
+
70
+ part 'match_dto.freezed.dart';
71
+ part 'match_dto.g.dart';
72
+
73
+ @freezed
74
+ class MatchDto with _$MatchDto {
75
+ const factory MatchDto({
76
+ required String id,
77
+ required String sport,
78
+ required String title,
79
+ @JsonKey(name: 'scheduled_at') required String scheduledAt,
80
+ required MatchLocationDto location,
81
+ @JsonKey(name: 'max_players') required int maxPlayers,
82
+ @JsonKey(name: 'player_ids') @Default([]) List<String> playerIds,
83
+ @Default('open') String status,
84
+ }) = _MatchDto;
85
+
86
+ factory MatchDto.fromJson(Map<String, dynamic> json) =>
87
+ _$MatchDtoFromJson(json);
88
+
89
+ const MatchDto._();
90
+
91
+ /// DTO → Domain 변환
92
+ Match toDomain() => Match(
93
+ id: id,
94
+ sport: Sport.values.byName(sport),
95
+ title: title,
96
+ scheduledAt: DateTime.parse(scheduledAt),
97
+ location: location.toDomain(),
98
+ maxPlayers: maxPlayers,
99
+ playerIds: playerIds,
100
+ status: MatchStatus.values.byName(status),
101
+ );
102
+
103
+ /// Domain → DTO 변환
104
+ factory MatchDto.fromDomain(Match match) => MatchDto(
105
+ id: match.id,
106
+ sport: match.sport.name,
107
+ title: match.title,
108
+ scheduledAt: match.scheduledAt.toIso8601String(),
109
+ location: MatchLocationDto.fromDomain(match.location),
110
+ maxPlayers: match.maxPlayers,
111
+ playerIds: match.playerIds,
112
+ status: match.status.name,
113
+ );
114
+ }
115
+ ```
116
+
117
+ ### Sealed Class + Freezed (상태)
118
+
119
+ ```dart
120
+ @freezed
121
+ sealed class AuthState with _$AuthState {
122
+ const factory AuthState.initial() = AuthInitial;
123
+ const factory AuthState.loading() = AuthLoading;
124
+ const factory AuthState.authenticated(User user) = AuthAuthenticated;
125
+ const factory AuthState.unauthenticated() = AuthUnauthenticated;
126
+ const factory AuthState.error(String message) = AuthError;
127
+ }
128
+
129
+ // 사용: 패턴 매칭
130
+ Widget build(BuildContext context) {
131
+ final authState = ref.watch(authProvider);
132
+
133
+ return authState.when(
134
+ initial: () => const SplashScreen(),
135
+ loading: () => const LoadingScreen(),
136
+ authenticated: (user) => const HomeScreen(),
137
+ unauthenticated: () => const LoginScreen(),
138
+ error: (msg) => ErrorScreen(message: msg),
139
+ );
140
+ }
141
+ ```
142
+
143
+ ### copyWith (불변 업데이트)
144
+
145
+ ```dart
146
+ // freezed가 자동 생성하는 copyWith
147
+ final updatedMatch = match.copyWith(
148
+ status: MatchStatus.full,
149
+ playerIds: [...match.playerIds, newPlayerId],
150
+ );
151
+ // 원본 match는 변경 없음 (불변)
152
+ ```
153
+
154
+ ### 규칙
155
+
156
+ - Domain Entity: `@freezed` (JSON 직렬화 없음, 순수 도메인)
157
+ - DTO: `@freezed` + `@JsonKey` + `fromJson`/`toJson`
158
+ - DTO → Domain: `.toDomain()` 메서드
159
+ - Domain → DTO: `DTO.fromDomain()` factory
160
+ - API 응답 key naming: `@JsonKey(name: 'snake_case')`
161
+ - 상태 모델: `@freezed sealed class` + `.when()` 패턴 매칭
162
+ - `build_runner watch` 개발 중 항상 실행
@@ -0,0 +1,170 @@
1
+ ---
2
+ title: Project Structure (Feature-First + Melos)
3
+ category: guide
4
+ tags: structure, monorepo, melos, feature-first
5
+ ---
6
+
7
+ ## Project Structure
8
+
9
+ Flutter Feature-first 디렉토리 구조 + Melos 모노레포 관리.
10
+
11
+ ### 단일 앱 구조
12
+
13
+ ```
14
+ my_app/
15
+ ├── lib/
16
+ │ ├── main.dart # 엔트리포인트
17
+ │ ├── app.dart # MaterialApp + ProviderScope
18
+ │ ├── core/
19
+ │ │ ├── constants/
20
+ │ │ │ ├── app_colors.dart
21
+ │ │ │ ├── app_spacing.dart
22
+ │ │ │ └── api_endpoints.dart
23
+ │ │ ├── extensions/
24
+ │ │ │ ├── context_ext.dart # BuildContext 확장
25
+ │ │ │ └── string_ext.dart
26
+ │ │ ├── router/
27
+ │ │ │ ├── app_router.dart # GoRouter 정의
28
+ │ │ │ └── route_names.dart # 경로 상수
29
+ │ │ ├── theme/
30
+ │ │ │ ├── app_theme.dart # ThemeData
31
+ │ │ │ └── typography.dart
32
+ │ │ ├── network/
33
+ │ │ │ ├── api_client.dart # Dio/http 설정
34
+ │ │ │ └── api_interceptor.dart
35
+ │ │ └── providers/
36
+ │ │ ├── auth_provider.dart # 전역 인증 상태
37
+ │ │ └── locale_provider.dart
38
+ │ │
39
+ │ ├── features/
40
+ │ │ ├── auth/
41
+ │ │ │ ├── data/
42
+ │ │ │ │ ├── datasources/
43
+ │ │ │ │ │ └── auth_remote_datasource.dart
44
+ │ │ │ │ ├── dtos/
45
+ │ │ │ │ │ └── auth_response_dto.dart
46
+ │ │ │ │ │ └── auth_response_dto.g.dart # json_serializable
47
+ │ │ │ │ │ └── auth_response_dto.freezed.dart
48
+ │ │ │ │ └── repositories/
49
+ │ │ │ │ └── auth_repository_impl.dart
50
+ │ │ │ ├── domain/
51
+ │ │ │ │ ├── entities/
52
+ │ │ │ │ │ └── user.dart
53
+ │ │ │ │ │ └── user.freezed.dart
54
+ │ │ │ │ └── repositories/
55
+ │ │ │ │ └── auth_repository.dart # abstract
56
+ │ │ │ └── presentation/
57
+ │ │ │ ├── providers/
58
+ │ │ │ │ └── auth_provider.dart
59
+ │ │ │ ├── screens/
60
+ │ │ │ │ ├── login_screen.dart
61
+ │ │ │ │ └── signup_screen.dart
62
+ │ │ │ └── widgets/
63
+ │ │ │ └── social_login_buttons.dart
64
+ │ │ │
65
+ │ │ ├── match/
66
+ │ │ │ ├── data/...
67
+ │ │ │ ├── domain/...
68
+ │ │ │ └── presentation/...
69
+ │ │ │
70
+ │ │ └── community/
71
+ │ │ ├── data/...
72
+ │ │ ├── domain/...
73
+ │ │ └── presentation/...
74
+ │ │
75
+ │ └── shared/
76
+ │ ├── widgets/
77
+ │ │ ├── app_button.dart
78
+ │ │ ├── app_text_field.dart
79
+ │ │ └── loading_indicator.dart
80
+ │ └── utils/
81
+ │ ├── date_formatter.dart
82
+ │ └── validators.dart
83
+
84
+ ├── test/
85
+ │ ├── features/
86
+ │ │ ├── auth/
87
+ │ │ │ ├── data/repositories/auth_repository_impl_test.dart
88
+ │ │ │ └── presentation/providers/auth_provider_test.dart
89
+ │ │ └── match/...
90
+ │ ├── fixtures/
91
+ │ │ └── test_data.dart # 공유 테스트 데이터
92
+ │ └── helpers/
93
+ │ └── pump_app.dart # testWidgets 헬퍼
94
+
95
+ ├── integration_test/
96
+ │ └── match_flow_test.dart
97
+
98
+ ├── assets/
99
+ │ ├── images/
100
+ │ ├── icons/
101
+ │ └── translations/ # ARB 파일 (i18n)
102
+
103
+ ├── pubspec.yaml
104
+ ├── analysis_options.yaml
105
+ └── l10n.yaml # 국제화 설정
106
+ ```
107
+
108
+ ### Melos 모노레포 구조
109
+
110
+ ```
111
+ project_root/
112
+ ├── melos.yaml
113
+ ├── apps/
114
+ │ ├── mobile/ # Flutter 앱
115
+ │ │ ├── lib/
116
+ │ │ └── pubspec.yaml
117
+ │ └── admin/ # 관리자 웹 (선택)
118
+ │ ├── lib/
119
+ │ └── pubspec.yaml
120
+ ├── packages/
121
+ │ ├── core/ # 공유 코어 (모델, 유틸)
122
+ │ │ ├── lib/
123
+ │ │ └── pubspec.yaml
124
+ │ ├── ui_kit/ # 공유 UI 컴포넌트
125
+ │ │ ├── lib/
126
+ │ │ └── pubspec.yaml
127
+ │ ├── api_client/ # API 클라이언트
128
+ │ │ ├── lib/
129
+ │ │ └── pubspec.yaml
130
+ │ └── auth/ # 인증 모듈
131
+ │ ├── lib/
132
+ │ └── pubspec.yaml
133
+ └── tools/
134
+ └── custom_lint_rules/ # 프로젝트 커스텀 린트
135
+ ```
136
+
137
+ ### melos.yaml
138
+
139
+ ```yaml
140
+ name: my_project
141
+ packages:
142
+ - apps/*
143
+ - packages/*
144
+
145
+ scripts:
146
+ analyze:
147
+ exec: dart analyze --fatal-infos
148
+ test:
149
+ exec: flutter test --coverage
150
+ format:
151
+ exec: dart format --set-exit-if-changed .
152
+ build_runner:
153
+ exec: dart run build_runner build --delete-conflicting-outputs
154
+ packageFilters:
155
+ dependsOn: build_runner
156
+ clean:
157
+ exec: flutter clean
158
+
159
+ command:
160
+ bootstrap:
161
+ usePubspecOverrides: true
162
+ ```
163
+
164
+ ### 규칙
165
+
166
+ - feature 안에서 다른 feature 직접 import 금지
167
+ - 공유 로직은 `core/` 또는 `shared/`
168
+ - 모노레포: `melos bootstrap`으로 의존성 연결
169
+ - 테스트 디렉토리는 `lib/` 미러링 구조
170
+ - generated 파일 (`.g.dart`, `.freezed.dart`) → `.gitignore`에 추가하지 않음 (CI에서 재생성 비용)
@@ -0,0 +1,112 @@
1
+ ---
2
+ title: Animations
3
+ impact: MEDIUM
4
+ tags: animation, implicit, explicit, hero
5
+ ---
6
+
7
+ ## Animations
8
+
9
+ Flutter 애니메이션 패턴. 암시적(간단) → 명시적(복잡) 선택 기준.
10
+
11
+ ### 선택 기준
12
+
13
+ | 복잡도 | 사용 | 예시 |
14
+ |--------|------|------|
15
+ | 단순 속성 변경 | `AnimatedContainer`, `AnimatedOpacity` | 페이드, 크기 변경 |
16
+ | 여러 위젯 전환 | `AnimatedSwitcher` | 탭 전환, 상태 전환 |
17
+ | 화면 간 공유 요소 | `Hero` | 카드 → 상세 전환 |
18
+ | 커스텀 곡선/시퀀스 | `AnimationController` + `Tween` | 복잡한 모션 |
19
+ | 반복/물리 기반 | `AnimationController` + `SpringSimulation` | 풀투리프레시 |
20
+
21
+ ### 암시적 애니메이션 (간단)
22
+
23
+ ```dart
24
+ // 상태에 따라 자동 전환
25
+ AnimatedContainer(
26
+ duration: const Duration(milliseconds: 300),
27
+ curve: Curves.easeInOut,
28
+ width: isExpanded ? 200 : 100,
29
+ height: isExpanded ? 200 : 100,
30
+ decoration: BoxDecoration(
31
+ color: isSelected ? Colors.blue : Colors.grey,
32
+ borderRadius: BorderRadius.circular(isExpanded ? 16 : 8),
33
+ ),
34
+ child: content,
35
+ );
36
+
37
+ // 위젯 교체 시 전환
38
+ AnimatedSwitcher(
39
+ duration: const Duration(milliseconds: 200),
40
+ child: isLoading
41
+ ? const CircularProgressIndicator(key: ValueKey('loading'))
42
+ : ContentView(key: ValueKey('content'), data: data),
43
+ );
44
+ ```
45
+
46
+ ### Hero 전환
47
+
48
+ ```dart
49
+ // 리스트 아이템
50
+ Hero(
51
+ tag: 'match-${match.id}',
52
+ child: MatchCard(match: match),
53
+ );
54
+
55
+ // 상세 화면
56
+ Hero(
57
+ tag: 'match-${match.id}',
58
+ child: MatchDetailHeader(match: match),
59
+ );
60
+ ```
61
+
62
+ ### 명시적 애니메이션 (복잡)
63
+
64
+ ```dart
65
+ class PulseWidget extends StatefulWidget {
66
+ const PulseWidget({super.key, required this.child});
67
+ final Widget child;
68
+
69
+ @override
70
+ State<PulseWidget> createState() => _PulseWidgetState();
71
+ }
72
+
73
+ class _PulseWidgetState extends State<PulseWidget>
74
+ with SingleTickerProviderStateMixin {
75
+ late final AnimationController _controller;
76
+ late final Animation<double> _scale;
77
+
78
+ @override
79
+ void initState() {
80
+ super.initState();
81
+ _controller = AnimationController(
82
+ duration: const Duration(milliseconds: 1000),
83
+ vsync: this,
84
+ )..repeat(reverse: true);
85
+
86
+ _scale = Tween(begin: 1.0, end: 1.1).animate(
87
+ CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
88
+ );
89
+ }
90
+
91
+ @override
92
+ void dispose() {
93
+ _controller.dispose();
94
+ super.dispose();
95
+ }
96
+
97
+ @override
98
+ Widget build(BuildContext context) {
99
+ return ScaleTransition(scale: _scale, child: widget.child);
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### 규칙
105
+
106
+ - 단순 속성 변경 → 암시적 (`Animated*` 위젯) 우선
107
+ - 위젯 교체 → `AnimatedSwitcher` + `key` 필수
108
+ - 화면 전환 공유 요소 → `Hero` (동일 tag)
109
+ - 커스텀 애니메이션 → `AnimationController` + `dispose()` 필수
110
+ - duration: 200-300ms (UI), 300-500ms (페이지 전환)
111
+ - `vsync: this` + `TickerProviderStateMixin` (프레임 동기화)
112
+ - 복잡한 시퀀스는 `staggered_animations` 패키지 검토
@@ -0,0 +1,121 @@
1
+ ---
2
+ title: Architecture (Feature-First + MVVM)
3
+ impact: CRITICAL
4
+ tags: architecture, feature-first, mvvm, clean-architecture
5
+ ---
6
+
7
+ ## Architecture (Feature-First + MVVM)
8
+
9
+ Flutter 공식 아키텍처 가이드(MVVM) + VGV 4-layer + Feature-first 구조 종합.
10
+
11
+ ### 3-Layer 아키텍처
12
+
13
+ ```
14
+ ┌─ Presentation (UI) ─────────────────┐
15
+ │ Widget → ViewModel (Notifier) │ UI 렌더링 + 사용자 입력
16
+ │ ref.watch로 상태 구독 │
17
+ └─────────────────────────────────────┘
18
+ ↕ (Domain Model)
19
+ ┌─ Domain ────────────────────────────┐
20
+ │ Entity, ValueObject, UseCase │ 비즈니스 규칙 (선택 레이어)
21
+ │ 플랫폼/프레임워크 의존성 없음 │
22
+ └─────────────────────────────────────┘
23
+ ↕ (Domain Model)
24
+ ┌─ Data ──────────────────────────────┐
25
+ │ Repository → DataSource │ API/DB/캐시 접근
26
+ │ DTO ↔ Domain Model 변환 │
27
+ └─────────────────────────────────────┘
28
+ ```
29
+
30
+ ### Feature-First 디렉토리
31
+
32
+ **Incorrect:**
33
+ ```
34
+ lib/
35
+ models/ # 모든 모델이 한 폴더
36
+ screens/ # 모든 화면이 한 폴더
37
+ services/ # 모든 서비스가 한 폴더
38
+ widgets/ # 모든 위젯이 한 폴더
39
+ ```
40
+
41
+ **Correct:**
42
+ ```
43
+ lib/
44
+ core/ # 공유 인프라
45
+ constants/
46
+ extensions/
47
+ router/
48
+ theme/
49
+ providers/ # 전역 provider (auth, locale 등)
50
+ features/
51
+ auth/
52
+ data/
53
+ datasources/ # AuthRemoteDataSource
54
+ dtos/ # AuthResponseDto (JSON 매핑)
55
+ repositories/ # AuthRepositoryImpl
56
+ domain/
57
+ entities/ # User
58
+ repositories/ # AuthRepository (abstract)
59
+ presentation/
60
+ providers/ # authProvider, loginNotifier
61
+ screens/ # LoginScreen, SignUpScreen
62
+ widgets/ # AuthForm, SocialLoginButtons
63
+ match/
64
+ data/...
65
+ domain/...
66
+ presentation/...
67
+ shared/
68
+ widgets/ # 재사용 UI 컴포넌트
69
+ utils/ # 순수 유틸리티 함수
70
+ ```
71
+
72
+ ### Repository 패턴
73
+
74
+ ```dart
75
+ // Domain layer — 추상 인터페이스
76
+ abstract class MatchRepository {
77
+ Future<List<Match>> getMatches({required Sport sport});
78
+ Future<void> joinMatch(String matchId);
79
+ Stream<Match> watchMatch(String matchId);
80
+ }
81
+
82
+ // Data layer — 구현
83
+ class MatchRepositoryImpl implements MatchRepository {
84
+ const MatchRepositoryImpl({
85
+ required this.remoteDataSource,
86
+ required this.localDataSource,
87
+ });
88
+
89
+ final MatchRemoteDataSource remoteDataSource;
90
+ final MatchLocalDataSource localDataSource;
91
+
92
+ @override
93
+ Future<List<Match>> getMatches({required Sport sport}) async {
94
+ try {
95
+ final dtos = await remoteDataSource.fetchMatches(sport.name);
96
+ final matches = dtos.map((dto) => dto.toDomain()).toList();
97
+ await localDataSource.cacheMatches(matches);
98
+ return matches;
99
+ } on NetworkException {
100
+ return localDataSource.getCachedMatches(sport);
101
+ }
102
+ }
103
+ }
104
+
105
+ // Provider 연결
106
+ final matchRepositoryProvider = Provider<MatchRepository>((ref) {
107
+ return MatchRepositoryImpl(
108
+ remoteDataSource: ref.watch(matchRemoteDataSourceProvider),
109
+ localDataSource: ref.watch(matchLocalDataSourceProvider),
110
+ );
111
+ });
112
+ ```
113
+
114
+ ### 규칙
115
+
116
+ - **Feature-first**: `lib/features/{name}/` 구조 필수
117
+ - **레이어 방향**: Presentation → Domain ← Data (의존성 역전)
118
+ - **Domain layer**: Flutter import 금지 (`dart:` 만 허용)
119
+ - **DTO ↔ Model 분리**: API 응답 구조와 도메인 모델 분리
120
+ - **Repository**: abstract (domain) + impl (data) 분리
121
+ - **1 feature = 1 독립 단위**: feature 간 직접 import 금지 → 공유 도메인은 `core/`
@@ -0,0 +1,117 @@
1
+ ---
2
+ title: Navigation & Routing
3
+ impact: HIGH
4
+ tags: go-router, navigation, deep-link, routing
5
+ ---
6
+
7
+ ## Navigation & Routing
8
+
9
+ go_router 기반 선언적 라우팅. 딥링크, 가드, 중첩 네비게이션.
10
+
11
+ ### 라우터 설정
12
+
13
+ ```dart
14
+ final routerProvider = Provider<GoRouter>((ref) {
15
+ final authState = ref.watch(authStateProvider);
16
+
17
+ return GoRouter(
18
+ initialLocation: '/',
19
+ redirect: (context, state) {
20
+ final isLoggedIn = authState.valueOrNull != null;
21
+ final isAuthRoute = state.matchedLocation.startsWith('/auth');
22
+
23
+ if (!isLoggedIn && !isAuthRoute) return '/auth/login';
24
+ if (isLoggedIn && isAuthRoute) return '/';
25
+ return null; // 리다이렉트 불필요
26
+ },
27
+ routes: [
28
+ // 인증 라우트 (바텀 네비 없음)
29
+ GoRoute(
30
+ path: '/auth/login',
31
+ builder: (context, state) => const LoginScreen(),
32
+ ),
33
+ // 메인 앱 (바텀 네비 + 중첩 라우팅)
34
+ StatefulShellRoute.indexedStack(
35
+ builder: (context, state, child) => MainShell(child: child),
36
+ branches: [
37
+ StatefulShellBranch(routes: [
38
+ GoRoute(
39
+ path: '/',
40
+ builder: (_, __) => const HomeScreen(),
41
+ routes: [
42
+ GoRoute(
43
+ path: 'match/:id',
44
+ builder: (_, state) => MatchDetailScreen(
45
+ matchId: state.pathParameters['id']!,
46
+ ),
47
+ ),
48
+ ],
49
+ ),
50
+ ]),
51
+ StatefulShellBranch(routes: [
52
+ GoRoute(
53
+ path: '/community',
54
+ builder: (_, __) => const CommunityScreen(),
55
+ ),
56
+ ]),
57
+ StatefulShellBranch(routes: [
58
+ GoRoute(
59
+ path: '/profile',
60
+ builder: (_, __) => const ProfileScreen(),
61
+ ),
62
+ ]),
63
+ ],
64
+ ),
65
+ ],
66
+ );
67
+ });
68
+ ```
69
+
70
+ ### 네비게이션 호출
71
+
72
+ **Incorrect:**
73
+ ```dart
74
+ // 명령형 — 히스토리 관리 어려움
75
+ Navigator.push(
76
+ context,
77
+ MaterialPageRoute(builder: (_) => MatchDetailScreen(id: matchId)),
78
+ );
79
+ ```
80
+
81
+ **Correct:**
82
+ ```dart
83
+ // 선언적 — URL 기반
84
+ context.go('/match/$matchId'); // 교체
85
+ context.push('/match/$matchId'); // 스택 추가
86
+ context.pop(); // 뒤로가기
87
+ ```
88
+
89
+ ### 딥링크 설정
90
+
91
+ ```dart
92
+ // go_router는 자동으로 URL 파싱
93
+ // Android: AndroidManifest.xml
94
+ // <intent-filter android:autoVerify="true">
95
+ // <data android:scheme="https" android:host="app.example.com" />
96
+ // </intent-filter>
97
+
98
+ // iOS: Info.plist
99
+ // Associated Domains: applinks:app.example.com
100
+
101
+ // Flutter 측: 라우트만 정의하면 딥링크 자동 처리
102
+ GoRoute(
103
+ path: '/match/:id',
104
+ builder: (_, state) => MatchDetailScreen(
105
+ matchId: state.pathParameters['id']!,
106
+ ),
107
+ ),
108
+ ```
109
+
110
+ ### 규칙
111
+
112
+ - `go_router` 선언적 라우팅 사용 (Navigator.push 금지)
113
+ - 라우트 정의는 `core/router/` 에 중앙화
114
+ - `redirect` 로 인증 가드 구현 (미들웨어 패턴)
115
+ - `StatefulShellRoute` 로 바텀 네비 + 각 탭 스택 유지
116
+ - path parameter는 `/:id` 패턴, query는 `state.uri.queryParameters`
117
+ - 라우트 경로 상수화 (`RouteNames` 또는 `RoutePaths` 클래스)