timsquad 3.3.0 โ 3.4.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.
- package/README.ko.md +288 -0
- package/README.md +158 -151
- package/dist/commands/compile.d.ts +3 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +170 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +94 -5
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/skills.d.ts +12 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +231 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/upgrade.js +5 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/daemon/entry.js +3 -3
- package/dist/daemon/entry.js.map +1 -1
- package/dist/daemon/index.d.ts +3 -2
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +137 -45
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/meta-cache.d.ts +1 -0
- package/dist/daemon/meta-cache.d.ts.map +1 -1
- package/dist/daemon/meta-cache.js +9 -0
- package/dist/daemon/meta-cache.js.map +1 -1
- package/dist/daemon/session-state.d.ts +19 -0
- package/dist/daemon/session-state.d.ts.map +1 -0
- package/dist/daemon/session-state.js +132 -0
- package/dist/daemon/session-state.js.map +1 -0
- package/dist/daemon/shutdown.d.ts.map +1 -1
- package/dist/daemon/shutdown.js +7 -1
- package/dist/daemon/shutdown.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/compile-rules.d.ts +66 -0
- package/dist/lib/compile-rules.d.ts.map +1 -0
- package/dist/lib/compile-rules.js +114 -0
- package/dist/lib/compile-rules.js.map +1 -0
- package/dist/lib/compiler.d.ts +105 -0
- package/dist/lib/compiler.d.ts.map +1 -0
- package/dist/lib/compiler.js +368 -0
- package/dist/lib/compiler.js.map +1 -0
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/template.d.ts.map +1 -1
- package/dist/lib/template.js +6 -0
- package/dist/lib/template.js.map +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +12 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/project.d.ts +1 -1
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +2 -0
- package/dist/types/project.js.map +1 -1
- package/package.json +1 -1
- package/templates/base/agents/overlays/domain/mobile/_common.md +13 -0
- package/templates/base/skills/controller/SKILL.md +111 -0
- package/templates/base/skills/controller/references/README.md +35 -0
- package/templates/base/skills/controller/rules/README.md +18 -0
- package/templates/base/skills/mobile/dart/SKILL.md +69 -0
- package/templates/base/skills/mobile/dart/rules/async-patterns.md +112 -0
- package/templates/base/skills/mobile/dart/rules/code-style.md +96 -0
- package/templates/base/skills/mobile/dart/rules/null-safety.md +84 -0
- package/templates/base/skills/mobile/dart/rules/type-system.md +111 -0
- package/templates/base/skills/mobile/flutter/SKILL.md +89 -0
- package/templates/base/skills/mobile/flutter/ci-cd/SKILL.md +82 -0
- package/templates/base/skills/mobile/flutter/ci-cd/references/ci-cd-pipeline.md +314 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/code-signing.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/codemagic-setup.md +116 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/fastlane-setup.md +105 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/github-actions.md +112 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/store-deployment.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/versioning.md +107 -0
- package/templates/base/skills/mobile/flutter/i18n/SKILL.md +78 -0
- package/templates/base/skills/mobile/flutter/i18n/references/i18n-architecture.md +225 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/arb-files.md +182 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/locale-switching.md +226 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/localization-setup.md +137 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/plural-gender.md +159 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/text-direction.md +199 -0
- package/templates/base/skills/mobile/flutter/monitoring/SKILL.md +81 -0
- package/templates/base/skills/mobile/flutter/monitoring/references/monitoring-architecture.md +269 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/analytics.md +227 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/crashlytics-setup.md +195 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/logging.md +258 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/performance-monitoring.md +248 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/sentry-integration.md +249 -0
- package/templates/base/skills/mobile/flutter/networking/SKILL.md +88 -0
- package/templates/base/skills/mobile/flutter/networking/references/api-client-architecture.md +305 -0
- package/templates/base/skills/mobile/flutter/networking/rules/caching.md +212 -0
- package/templates/base/skills/mobile/flutter/networking/rules/connectivity.md +213 -0
- package/templates/base/skills/mobile/flutter/networking/rules/dio-setup.md +159 -0
- package/templates/base/skills/mobile/flutter/networking/rules/error-handling.md +209 -0
- package/templates/base/skills/mobile/flutter/networking/rules/interceptors.md +205 -0
- package/templates/base/skills/mobile/flutter/networking/rules/retrofit-patterns.md +194 -0
- package/templates/base/skills/mobile/flutter/push-notifications/SKILL.md +87 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/notification-architecture.md +340 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/platform-setup.md +286 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/background-processing.md +308 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/deep-linking.md +217 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/fcm-setup.md +164 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/local-notifications.md +262 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-handling.md +210 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-permissions.md +246 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/rich-notifications.md +320 -0
- package/templates/base/skills/mobile/flutter/references/freezed-patterns.md +162 -0
- package/templates/base/skills/mobile/flutter/references/project-structure.md +170 -0
- package/templates/base/skills/mobile/flutter/rules/animations.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/architecture.md +121 -0
- package/templates/base/skills/mobile/flutter/rules/navigation-routing.md +117 -0
- package/templates/base/skills/mobile/flutter/rules/performance.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/platform-adaptive.md +126 -0
- package/templates/base/skills/mobile/flutter/rules/state-management.md +110 -0
- package/templates/base/skills/mobile/flutter/rules/testing.md +131 -0
- package/templates/base/skills/mobile/flutter/rules/widget-conventions.md +122 -0
- package/templates/base/skills/mobile/flutter/security/SKILL.md +86 -0
- package/templates/base/skills/mobile/flutter/security/references/mobile-security-checklist.md +168 -0
- package/templates/base/skills/mobile/flutter/security/rules/api-key-protection.md +206 -0
- package/templates/base/skills/mobile/flutter/security/rules/authentication.md +248 -0
- package/templates/base/skills/mobile/flutter/security/rules/data-protection.md +271 -0
- package/templates/base/skills/mobile/flutter/security/rules/obfuscation.md +213 -0
- package/templates/base/skills/mobile/flutter/security/rules/secure-storage.md +171 -0
- package/templates/base/skills/mobile/flutter/security/rules/ssl-pinning.md +197 -0
- package/templates/platforms/claude-code/CLAUDE.md.template +25 -0
- package/templates/platforms/claude-code/scripts/completion-guard.sh +57 -0
- package/templates/platforms/claude-code/scripts/phase-guard.sh +79 -0
- package/templates/platforms/claude-code/settings.json +75 -3
- package/templates/project-types/mobile-app/config.yaml +123 -0
- package/templates/project-types/mobile-app/process/workflow.xml +191 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Notification Service Architecture
|
|
3
|
+
category: guide
|
|
4
|
+
source: internal
|
|
5
|
+
tags: architecture, service, directory, testing, analytics
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Notification Service Architecture
|
|
9
|
+
|
|
10
|
+
์๋ฆผ ์๋น์ค ์ ์ฒด ์ํคํ
์ฒ. ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ, ์๋น์ค ๋ ์ด์ด, ํ
์คํธ ์ ๋ต, ๋ถ์.
|
|
11
|
+
|
|
12
|
+
## Key Concepts
|
|
13
|
+
|
|
14
|
+
- **์ค์ํ**: ์๋ฆผ ๊ด๋ จ ์ฝ๋๋ฅผ `core/notifications/` ์ ์ง์ค (feature ํก๋จ ๊ด์ฌ์ฌ)
|
|
15
|
+
- **์ถ์ํ**: `NotificationService` ์ธํฐํ์ด์ค โ FCM/Local/Mock ๊ตฌํ ๋ถ๋ฆฌ
|
|
16
|
+
- **ํ์ด๋ก๋ ํ์คํ**: ์๋ฒ-ํด๋ผ์ด์ธํธ ๊ฐ ์๋ฆผ ๋ฐ์ดํฐ ๊ณ์ฝ
|
|
17
|
+
- **ํ
์คํธ ๊ฐ๋ฅ์ฑ**: Mock ๊ตฌํ์ผ๋ก ์๋ฆผ ๋ก์ง ๋จ์ ํ
์คํธ
|
|
18
|
+
|
|
19
|
+
## Directory Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
lib/
|
|
23
|
+
โโโ core/
|
|
24
|
+
โ โโโ notifications/
|
|
25
|
+
โ โโโ notification_service.dart # ์ถ์ ์ธํฐํ์ด์ค
|
|
26
|
+
โ โโโ notification_service_impl.dart # ํตํฉ ๊ตฌํ์ฒด
|
|
27
|
+
โ โโโ fcm/
|
|
28
|
+
โ โ โโโ fcm_service.dart # FCM ์ด๊ธฐํ + ํ ํฐ
|
|
29
|
+
โ โ โโโ fcm_token_manager.dart # ํ ํฐ ์๋ฒ ๋๊ธฐํ
|
|
30
|
+
โ โ โโโ fcm_message_handler.dart # ๋ฉ์์ง ๋ผ์ฐํ
|
|
31
|
+
โ โโโ local/
|
|
32
|
+
โ โ โโโ local_notification_service.dart # flutter_local_notifications ๋ํผ
|
|
33
|
+
โ โ โโโ notification_channels.dart # Android ์ฑ๋ ์ ์
|
|
34
|
+
โ โ โโโ scheduled_notification.dart # ์ค์ผ์ค ์๋ฆผ ๊ด๋ฆฌ
|
|
35
|
+
โ โโโ models/
|
|
36
|
+
โ โ โโโ notification_payload.dart # ํ์ด๋ก๋ ๋ชจ๋ธ
|
|
37
|
+
โ โ โโโ notification_channel.dart # ์ฑ๋ enum + ์ค์
|
|
38
|
+
โ โ โโโ notification_action.dart # ์ก์
๋ฒํผ ๋ชจ๋ธ
|
|
39
|
+
โ โโโ navigation/
|
|
40
|
+
โ โ โโโ notification_navigator.dart # ๋ฅ๋งํฌ ๋ค๋น๊ฒ์ด์
|
|
41
|
+
โ โโโ permission/
|
|
42
|
+
โ โ โโโ permission_service.dart # ๊ถํ ๊ด๋ฆฌ
|
|
43
|
+
โ โ โโโ permission_prompt_controller.dart # ํ๋ฆฌํ๋กฌํํธ ๋ก์ง
|
|
44
|
+
โ โโโ background/
|
|
45
|
+
โ โ โโโ background_tasks.dart # top-level ์ฝ๋ฐฑ
|
|
46
|
+
โ โ โโโ background_task_manager.dart # ํ์คํฌ ๋ฑ๋ก/์ทจ์
|
|
47
|
+
โ โโโ providers/
|
|
48
|
+
โ โโโ notification_providers.dart # Riverpod providers
|
|
49
|
+
โ
|
|
50
|
+
โโโ features/
|
|
51
|
+
โ โโโ notifications/ # ์๋ฆผ ๋ชฉ๋ก UI (feature)
|
|
52
|
+
โ โโโ data/
|
|
53
|
+
โ โ โโโ datasources/
|
|
54
|
+
โ โ โ โโโ notification_local_datasource.dart
|
|
55
|
+
โ โ โโโ repositories/
|
|
56
|
+
โ โ โโโ notification_repository_impl.dart
|
|
57
|
+
โ โโโ domain/
|
|
58
|
+
โ โ โโโ entities/
|
|
59
|
+
โ โ โ โโโ app_notification.dart # ์ฑ ๋ด ์๋ฆผ ๋ชจ๋ธ
|
|
60
|
+
โ โ โโโ repositories/
|
|
61
|
+
โ โ โโโ notification_repository.dart # abstract
|
|
62
|
+
โ โโโ presentation/
|
|
63
|
+
โ โโโ screens/
|
|
64
|
+
โ โ โโโ notification_list_screen.dart
|
|
65
|
+
โ โโโ widgets/
|
|
66
|
+
โ โ โโโ notification_card.dart
|
|
67
|
+
โ โโโ providers/
|
|
68
|
+
โ โโโ notification_list_provider.dart
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Service Layer Design
|
|
72
|
+
|
|
73
|
+
```dart
|
|
74
|
+
/// ์๋ฆผ ์๋น์ค ์ธํฐํ์ด์ค (ํ
์คํธ ์ฉ์ด์ฑ)
|
|
75
|
+
abstract class NotificationService {
|
|
76
|
+
Future<void> initialize();
|
|
77
|
+
Future<void> showNotification({
|
|
78
|
+
required int id,
|
|
79
|
+
required String title,
|
|
80
|
+
required String body,
|
|
81
|
+
String? payload,
|
|
82
|
+
String? imageUrl,
|
|
83
|
+
String channelId,
|
|
84
|
+
});
|
|
85
|
+
Future<void> scheduleNotification({
|
|
86
|
+
required int id,
|
|
87
|
+
required String title,
|
|
88
|
+
required String body,
|
|
89
|
+
required DateTime scheduledTime,
|
|
90
|
+
String? payload,
|
|
91
|
+
});
|
|
92
|
+
Future<void> cancelNotification(int id);
|
|
93
|
+
Future<void> cancelAll();
|
|
94
|
+
Future<String?> getFcmToken();
|
|
95
|
+
Stream<String> get onTokenRefresh;
|
|
96
|
+
Future<AuthorizationStatus> requestPermission();
|
|
97
|
+
Future<AuthorizationStatus> getPermissionStatus();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// ํตํฉ ๊ตฌํ์ฒด
|
|
101
|
+
class NotificationServiceImpl implements NotificationService {
|
|
102
|
+
final FcmService _fcmService;
|
|
103
|
+
final LocalNotificationService _localService;
|
|
104
|
+
final PermissionService _permissionService;
|
|
105
|
+
|
|
106
|
+
NotificationServiceImpl({
|
|
107
|
+
required FcmService fcmService,
|
|
108
|
+
required LocalNotificationService localService,
|
|
109
|
+
required PermissionService permissionService,
|
|
110
|
+
}) : _fcmService = fcmService,
|
|
111
|
+
_localService = localService,
|
|
112
|
+
_permissionService = permissionService;
|
|
113
|
+
|
|
114
|
+
@override
|
|
115
|
+
Future<void> initialize() async {
|
|
116
|
+
await _fcmService.initialize();
|
|
117
|
+
await _localService.initialize();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@override
|
|
121
|
+
Future<void> showNotification({
|
|
122
|
+
required int id,
|
|
123
|
+
required String title,
|
|
124
|
+
required String body,
|
|
125
|
+
String? payload,
|
|
126
|
+
String? imageUrl,
|
|
127
|
+
String channelId = 'default_channel',
|
|
128
|
+
}) async {
|
|
129
|
+
await _localService.show(
|
|
130
|
+
id: id,
|
|
131
|
+
title: title,
|
|
132
|
+
body: body,
|
|
133
|
+
payload: payload,
|
|
134
|
+
imageUrl: imageUrl,
|
|
135
|
+
channelId: channelId,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ... ๋๋จธ์ง ๊ตฌํ
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// Riverpod Provider ๊ตฌ์ฑ
|
|
143
|
+
final notificationServiceProvider = Provider<NotificationService>((ref) {
|
|
144
|
+
return NotificationServiceImpl(
|
|
145
|
+
fcmService: ref.watch(fcmServiceProvider),
|
|
146
|
+
localService: ref.watch(localNotificationServiceProvider),
|
|
147
|
+
permissionService: ref.watch(permissionServiceProvider),
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Initialization Flow
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
์ฑ ์์ (main.dart)
|
|
156
|
+
โ
|
|
157
|
+
โโ 1. WidgetsFlutterBinding.ensureInitialized()
|
|
158
|
+
โโ 2. Firebase.initializeApp()
|
|
159
|
+
โโ 3. FirebaseMessaging.onBackgroundMessage(handler) โ top-level ๋ฑ๋ก
|
|
160
|
+
โโ 4. NotificationService.initialize()
|
|
161
|
+
โ โโ FcmService.initialize()
|
|
162
|
+
โ โ โโ FCM ํ ํฐ ํ๋ + ์๋ฒ ๋๊ธฐํ
|
|
163
|
+
โ โ โโ onTokenRefresh ๊ตฌ๋
|
|
164
|
+
โ โโ LocalNotificationService.initialize()
|
|
165
|
+
โ โโ Android ์ฑ๋ ์์ฑ
|
|
166
|
+
โ โโ ํญ ์ฝ๋ฐฑ ๋ฑ๋ก
|
|
167
|
+
โโ 5. Workmanager.initialize(callbackDispatcher)
|
|
168
|
+
โโ 6. runApp(ProviderScope(child: MyApp()))
|
|
169
|
+
โ
|
|
170
|
+
โโ MyApp.initState()
|
|
171
|
+
โโ NotificationDeepLinkHandler.initialize()
|
|
172
|
+
โโ getInitialMessage() โ ๋ฅ๋งํฌ (cold start)
|
|
173
|
+
โโ onMessageOpenedApp.listen โ ๋ฅ๋งํฌ (background)
|
|
174
|
+
โโ onMessage.listen โ ํฌ๊ทธ๋ผ์ด๋ ์๋ฆผ ํ์
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Testing Strategy
|
|
178
|
+
|
|
179
|
+
```dart
|
|
180
|
+
/// Mock ์๋ฆผ ์๋น์ค (๋จ์ ํ
์คํธ์ฉ)
|
|
181
|
+
class MockNotificationService implements NotificationService {
|
|
182
|
+
final List<Map<String, dynamic>> shownNotifications = [];
|
|
183
|
+
final List<Map<String, dynamic>> scheduledNotifications = [];
|
|
184
|
+
AuthorizationStatus _permissionStatus = AuthorizationStatus.authorized;
|
|
185
|
+
|
|
186
|
+
@override
|
|
187
|
+
Future<void> showNotification({
|
|
188
|
+
required int id,
|
|
189
|
+
required String title,
|
|
190
|
+
required String body,
|
|
191
|
+
String? payload,
|
|
192
|
+
String? imageUrl,
|
|
193
|
+
String channelId = 'default_channel',
|
|
194
|
+
}) async {
|
|
195
|
+
shownNotifications.add({
|
|
196
|
+
'id': id,
|
|
197
|
+
'title': title,
|
|
198
|
+
'body': body,
|
|
199
|
+
'payload': payload,
|
|
200
|
+
'channelId': channelId,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@override
|
|
205
|
+
Future<AuthorizationStatus> requestPermission() async {
|
|
206
|
+
return _permissionStatus;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
void setPermissionStatus(AuthorizationStatus status) {
|
|
210
|
+
_permissionStatus = status;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ... ๋๋จธ์ง Mock ๊ตฌํ
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/// ์๋ฆผ ๋ค๋น๊ฒ์ด์
ํ
์คํธ
|
|
217
|
+
void main() {
|
|
218
|
+
group('NotificationNavigator', () {
|
|
219
|
+
late MockGoRouter mockRouter;
|
|
220
|
+
late NotificationNavigator navigator;
|
|
221
|
+
|
|
222
|
+
setUp(() {
|
|
223
|
+
mockRouter = MockGoRouter();
|
|
224
|
+
navigator = NotificationNavigator(router: mockRouter);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('match payload navigates to match detail', () {
|
|
228
|
+
navigator.navigateFromMessage(RemoteMessage(
|
|
229
|
+
data: {'type': 'match', 'id': 'match_123'},
|
|
230
|
+
));
|
|
231
|
+
|
|
232
|
+
verify(() => mockRouter.push('/match/match_123')).called(1);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('unknown type navigates to notifications', () {
|
|
236
|
+
navigator.navigateFromMessage(RemoteMessage(
|
|
237
|
+
data: {'type': 'unknown'},
|
|
238
|
+
));
|
|
239
|
+
|
|
240
|
+
verify(() => mockRouter.push('/notifications')).called(1);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
group('NotificationPayload', () {
|
|
245
|
+
test('toRoute generates correct path', () {
|
|
246
|
+
final payload = NotificationPayload(type: 'chat', id: 'chat_456');
|
|
247
|
+
expect(payload.toRoute(), '/chat/chat_456');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('toRoute with explicit route overrides type', () {
|
|
251
|
+
final payload = NotificationPayload(
|
|
252
|
+
type: 'match',
|
|
253
|
+
id: 'match_123',
|
|
254
|
+
route: '/custom/path',
|
|
255
|
+
);
|
|
256
|
+
expect(payload.toRoute(), '/custom/path');
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Notification Analytics
|
|
263
|
+
|
|
264
|
+
```dart
|
|
265
|
+
/// ์๋ฆผ ๋ถ์ ์ด๋ฒคํธ
|
|
266
|
+
class NotificationAnalytics {
|
|
267
|
+
final AnalyticsService _analytics;
|
|
268
|
+
|
|
269
|
+
NotificationAnalytics(this._analytics);
|
|
270
|
+
|
|
271
|
+
/// ์๋ฆผ ์์ (ํฌ๊ทธ๋ผ์ด๋)
|
|
272
|
+
void trackReceived(RemoteMessage message) {
|
|
273
|
+
_analytics.logEvent('notification_received', parameters: {
|
|
274
|
+
'type': message.data['type'] ?? 'unknown',
|
|
275
|
+
'source': 'fcm',
|
|
276
|
+
'app_state': 'foreground',
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/// ์๋ฆผ ํญ (์ด๋ฆผ)
|
|
281
|
+
void trackOpened(NotificationPayload payload, String appState) {
|
|
282
|
+
_analytics.logEvent('notification_opened', parameters: {
|
|
283
|
+
'type': payload.type,
|
|
284
|
+
'action': payload.action ?? 'tap',
|
|
285
|
+
'app_state': appState, // foreground, background, terminated
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/// ์ก์
๋ฒํผ ํญ
|
|
290
|
+
void trackAction(String actionId, NotificationPayload payload) {
|
|
291
|
+
_analytics.logEvent('notification_action', parameters: {
|
|
292
|
+
'action_id': actionId,
|
|
293
|
+
'type': payload.type,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/// ๊ถํ ์์ฒญ ๊ฒฐ๊ณผ
|
|
298
|
+
void trackPermission(AuthorizationStatus status) {
|
|
299
|
+
_analytics.logEvent('notification_permission', parameters: {
|
|
300
|
+
'status': status.name,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/// ํ๋ฆฌํ๋กฌํํธ ๊ฒฐ๊ณผ
|
|
305
|
+
void trackPrePrompt(bool accepted) {
|
|
306
|
+
_analytics.logEvent('notification_pre_prompt', parameters: {
|
|
307
|
+
'accepted': accepted.toString(),
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Common Pitfalls
|
|
314
|
+
|
|
315
|
+
1. **Provider ์ ๊ทผ in Background**: ๋ฐฑ๊ทธ๋ผ์ด๋ ํธ๋ค๋ฌ์์ Riverpod Provider ์ฌ์ฉ ๋ถ๊ฐ โ ์ง์ ์์กด์ฑ ์์ฑ
|
|
316
|
+
2. **์ด๊ธฐํ ์์**: Firebase โ FCM ํธ๋ค๋ฌ ๋ฑ๋ก โ ๋ก์ปฌ ์๋ฆผ โ Workmanager (์์ ์ค์)
|
|
317
|
+
3. **๋ฅ๋งํฌ ๋ ์ด์ค ์ปจ๋์
**: `getInitialMessage()` ํธ์ถ ์ ๋ผ์ฐํฐ ๋ฏธ์ค๋น โ ์ง์ฐ ํ ๋ค๋น๊ฒ์ด์
|
|
318
|
+
4. **ํ ํฐ ๊ฐฑ์ ๋๋ฝ**: `onTokenRefresh` ๋ฏธ๊ตฌ๋
โ ์๋ฒ์ ์๋ชป๋ ํ ํฐ โ ์ ๋ฌ ์คํจ ์ฆ๊ฐ
|
|
319
|
+
5. **์ฑ๋ ์ค์๋ ๋ณ๊ฒฝ ๋ถ๊ฐ**: Android ์ฑ๋ ์์ฑ ํ ์ค์๋ ์ฝ๋ ๋ณ๊ฒฝ ๋ฌดํจ โ ์ ์ฑ๋ ID ํ์
|
|
320
|
+
6. **iOS ์๋ฎฌ๋ ์ดํฐ**: APNs ๋ฏธ์ง์ โ FCM ํ ํฐ ๋ฐ๊ธ ๋ถ๊ฐ โ ์ค๊ธฐ๊ธฐ ํ์
|
|
321
|
+
7. **Release ๋น๋ ์ฐจ์ด**: Debug์์๋ง ๋์ํ๋ ๋ก๊น
์ด Release์์ ํฌ๋์ ์ ๋ฐ ๊ฐ๋ฅ
|
|
322
|
+
8. **๋์ ์๋ฆผ**: ๊ฐ์ ID๋ก show() ํธ์ถ ์ ๋ฎ์ด์ฐ๊ธฐ โ ๊ณ ์ ID ์ ๋ต ํ์
|
|
323
|
+
|
|
324
|
+
## Examples
|
|
325
|
+
|
|
326
|
+
### ์ต์ ๊ตฌํ ์ฒดํฌ๋ฆฌ์คํธ
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
[ ] pubspec.yaml: firebase_core, firebase_messaging, flutter_local_notifications
|
|
330
|
+
[ ] flutterfire configure (google-services.json, GoogleService-Info.plist ์๋ ์์ฑ)
|
|
331
|
+
[ ] iOS: Push Notifications + Background Modes capability
|
|
332
|
+
[ ] Android: AndroidManifest.xml ๊ถํ + ๋ฉํ๋ฐ์ดํฐ
|
|
333
|
+
[ ] Android: ์๋ฆผ ์์ด์ฝ (ํฐ์+ํฌ๋ช
, PNG, 5์ข
ํฌ๊ธฐ)
|
|
334
|
+
[ ] main.dart: ์ด๊ธฐํ ์์ (Firebase โ FCM โ Local โ Workmanager)
|
|
335
|
+
[ ] ๋ฐฑ๊ทธ๋ผ์ด๋ ํธ๋ค๋ฌ: top-level, @pragma
|
|
336
|
+
[ ] ์๋ฆผ ์ฑ๋: ์ฑ ์์ ์ ์์ฑ (Android)
|
|
337
|
+
[ ] FCM ํ ํฐ: ์๋ฒ ๋๊ธฐํ + ๊ฐฑ์ ๊ตฌ๋
|
|
338
|
+
[ ] ๋ฅ๋งํฌ: 3๊ฐ์ง ์ฑ ์ํ (foreground/background/terminated) ์ฒ๋ฆฌ
|
|
339
|
+
[ ] ๊ถํ: ๋งฅ๋ฝ์ ์์ฒญ + ํ๋ฆฌํ๋กฌํํธ + ์ค์ ์ ๋
|
|
340
|
+
```
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Platform-Specific Setup (iOS APNs + Android)
|
|
3
|
+
category: guide
|
|
4
|
+
source: internal
|
|
5
|
+
tags: apns, android-channel, xcode, manifest, entitlements
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Platform-Specific Setup (iOS APNs + Android)
|
|
9
|
+
|
|
10
|
+
iOS APNs ์ธ์ฆ ํค ์ค์ , Xcode ์ค์ , Android ๋งค๋ํ์คํธ, ์๋ฆผ ์ฑ๋ ์์ธ.
|
|
11
|
+
ํ๋ก์ ํธ ์ด๊ธฐ ์ค์ ์ ์ฐธ์กฐ.
|
|
12
|
+
|
|
13
|
+
## Key Concepts
|
|
14
|
+
|
|
15
|
+
- **iOS**: APNs (Apple Push Notification service) โ FCM์ด APNs๋ฅผ ํตํด iOS ๊ธฐ๊ธฐ์ ์ ๋ฌ
|
|
16
|
+
- **Android**: FCM์ด ์ง์ ๊ธฐ๊ธฐ์ ์ ๋ฌ, Android 8+ (API 26) ์ฑ๋ ํ์
|
|
17
|
+
- **์ธ์ฆ ๋ฐฉ์**: APNs Auth Key (.p8) ๊ถ์ฅ (์ธ์ฆ์๋ณด๋ค ๊ด๋ฆฌ ์ฉ์ด, ๋ง๋ฃ ์์)
|
|
18
|
+
|
|
19
|
+
## iOS Setup
|
|
20
|
+
|
|
21
|
+
### 1. Apple Developer Console
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
1. Apple Developer > Certificates, Identifiers & Profiles
|
|
25
|
+
2. Keys > Create a Key
|
|
26
|
+
- Name: "FCM APNs Auth Key"
|
|
27
|
+
- Enable: Apple Push Notifications service (APNs)
|
|
28
|
+
- Download .p8 ํ์ผ (1ํ๋ง ๋ค์ด๋ก๋ ๊ฐ๋ฅ!)
|
|
29
|
+
- Key ID ๊ธฐ๋ก
|
|
30
|
+
|
|
31
|
+
3. App ID ์ค์
|
|
32
|
+
- Identifiers > ์ฑ ์ ํ > Capabilities
|
|
33
|
+
- Push Notifications ํ์ฑํ
|
|
34
|
+
|
|
35
|
+
4. Team ID ํ์ธ
|
|
36
|
+
- Membership ํ์ด์ง์์ Team ID ํ์ธ
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 2. Firebase Console์ APNs ํค ๋ฑ๋ก
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
1. Firebase Console > Project Settings > Cloud Messaging
|
|
43
|
+
2. iOS app ์ ํ
|
|
44
|
+
3. APNs authentication key ์
๋ก๋:
|
|
45
|
+
- .p8 ํ์ผ ์
๋ก๋
|
|
46
|
+
- Key ID ์
๋ ฅ
|
|
47
|
+
- Team ID ์
๋ ฅ
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. Xcode ์ค์
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
1. Runner.xcworkspace ์ด๊ธฐ
|
|
54
|
+
|
|
55
|
+
2. Signing & Capabilities:
|
|
56
|
+
+ Push Notifications (์๋ฆผ ์์ )
|
|
57
|
+
+ Background Modes:
|
|
58
|
+
โ Background fetch (๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ)
|
|
59
|
+
โ Remote notifications (์ฌ์ผ๋ฐํธ ํธ์)
|
|
60
|
+
โ Background processing (workmanager์ฉ, iOS 13+)
|
|
61
|
+
|
|
62
|
+
3. Info.plist (์๋ ์์ฑ๋์ง๋ง ํ์ธ):
|
|
63
|
+
- FirebaseAppDelegateProxyEnabled: YES (๊ธฐ๋ณธ๊ฐ)
|
|
64
|
+
|
|
65
|
+
4. (์ ํ) Notification Service Extension:
|
|
66
|
+
- File > New > Target > Notification Service Extension
|
|
67
|
+
- ๋ฆฌ์น ์๋ฆผ (์ด๋ฏธ์ง ์์ , ์ํธํ ํด์ ๋ฑ)์ ํ์
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 4. AppDelegate ์ค์
|
|
71
|
+
|
|
72
|
+
```swift
|
|
73
|
+
// ios/Runner/AppDelegate.swift
|
|
74
|
+
import UIKit
|
|
75
|
+
import Flutter
|
|
76
|
+
import Firebase
|
|
77
|
+
|
|
78
|
+
@main
|
|
79
|
+
@objc class AppDelegate: FlutterAppDelegate {
|
|
80
|
+
override func application(
|
|
81
|
+
_ application: UIApplication,
|
|
82
|
+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
|
83
|
+
) -> Bool {
|
|
84
|
+
FirebaseApp.configure()
|
|
85
|
+
|
|
86
|
+
// APNs ๋ฑ๋ก (firebase_messaging์ด ์๋ ์ฒ๋ฆฌํ์ง๋ง ๋ช
์์ ์ผ๋ก ํด๋ ๋ฌด๋ฐฉ)
|
|
87
|
+
if #available(iOS 10.0, *) {
|
|
88
|
+
UNUserNotificationCenter.current().delegate = self
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
GeneratedPluginRegistrant.register(with: self)
|
|
92
|
+
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ์ฌ์ผ๋ฐํธ ํธ์ ์์
|
|
96
|
+
override func application(
|
|
97
|
+
_ application: UIApplication,
|
|
98
|
+
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
|
|
99
|
+
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
|
|
100
|
+
) {
|
|
101
|
+
completionHandler(.newData)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 5. Podfile ์ค์
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
# ios/Podfile
|
|
110
|
+
platform :ios, '14.0' # Firebase ์ต์ ์๊ตฌ์ฌํญ
|
|
111
|
+
|
|
112
|
+
target 'Runner' do
|
|
113
|
+
use_frameworks!
|
|
114
|
+
use_modular_headers!
|
|
115
|
+
|
|
116
|
+
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Notification Service Extension (๋ฆฌ์น ์๋ฆผ ์ฌ์ฉ ์)
|
|
120
|
+
# target 'NotificationService' do
|
|
121
|
+
# use_frameworks!
|
|
122
|
+
# pod 'Firebase/Messaging'
|
|
123
|
+
# end
|
|
124
|
+
|
|
125
|
+
post_install do |installer|
|
|
126
|
+
installer.pods_project.targets.each do |target|
|
|
127
|
+
flutter_additional_ios_build_settings(target)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Android Setup
|
|
133
|
+
|
|
134
|
+
### 1. Firebase Console
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
1. Firebase Console > Project Settings > ์ฑ ์ถ๊ฐ > Android
|
|
138
|
+
2. Android ํจํค์ง๋ช
์
๋ ฅ (build.gradle์ applicationId)
|
|
139
|
+
3. google-services.json ๋ค์ด๋ก๋
|
|
140
|
+
4. android/app/google-services.json ์ ๋ฐฐ์น
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 2. build.gradle ์ค์
|
|
144
|
+
|
|
145
|
+
```groovy
|
|
146
|
+
// android/build.gradle (ํ๋ก์ ํธ ๋ ๋ฒจ)
|
|
147
|
+
buildscript {
|
|
148
|
+
dependencies {
|
|
149
|
+
classpath 'com.google.gms:google-services:4.4.2'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// android/app/build.gradle (์ฑ ๋ ๋ฒจ)
|
|
154
|
+
plugins {
|
|
155
|
+
id 'com.google.gms.google-services'
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
android {
|
|
159
|
+
compileSdk 35
|
|
160
|
+
|
|
161
|
+
defaultConfig {
|
|
162
|
+
minSdk 23 // FCM ์ต์ ์๊ตฌ
|
|
163
|
+
targetSdk 35
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 3. AndroidManifest.xml
|
|
169
|
+
|
|
170
|
+
```xml
|
|
171
|
+
<!-- android/app/src/main/AndroidManifest.xml -->
|
|
172
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
173
|
+
|
|
174
|
+
<!-- ์๋ฆผ ๊ถํ (Android 13+) -->
|
|
175
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
|
176
|
+
|
|
177
|
+
<!-- ์ ํํ ์๋ฆผ ์ค์ผ์ค๋ง (์ ํ) -->
|
|
178
|
+
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
|
179
|
+
|
|
180
|
+
<!-- ๋ฐฑ๊ทธ๋ผ์ด๋ ์์
(workmanager) -->
|
|
181
|
+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
|
182
|
+
|
|
183
|
+
<!-- ์ง๋ (์๋ฆผ์ฉ) -->
|
|
184
|
+
<uses-permission android:name="android.permission.VIBRATE"/>
|
|
185
|
+
|
|
186
|
+
<application
|
|
187
|
+
android:name="${applicationName}"
|
|
188
|
+
android:icon="@mipmap/ic_launcher">
|
|
189
|
+
|
|
190
|
+
<!-- FCM ๊ธฐ๋ณธ ์๋ฆผ ์ฑ๋ (์ฑ์ด ์์ฒด ์ฑ๋ ์์ฑ ์ ํด๋ฐฑ) -->
|
|
191
|
+
<meta-data
|
|
192
|
+
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
|
193
|
+
android:value="default_channel" />
|
|
194
|
+
|
|
195
|
+
<!-- FCM ๊ธฐ๋ณธ ์๋ฆผ ์์ด์ฝ -->
|
|
196
|
+
<meta-data
|
|
197
|
+
android:name="com.google.firebase.messaging.default_notification_icon"
|
|
198
|
+
android:resource="@mipmap/ic_notification" />
|
|
199
|
+
|
|
200
|
+
<!-- FCM ๊ธฐ๋ณธ ์๋ฆผ ์์ -->
|
|
201
|
+
<meta-data
|
|
202
|
+
android:name="com.google.firebase.messaging.default_notification_color"
|
|
203
|
+
android:resource="@color/notification_color" />
|
|
204
|
+
|
|
205
|
+
<activity
|
|
206
|
+
android:name=".MainActivity"
|
|
207
|
+
android:launchMode="singleTop">
|
|
208
|
+
<!-- ๋ฅ๋งํฌ ์ธํ
ํธ ํํฐ -->
|
|
209
|
+
<intent-filter>
|
|
210
|
+
<action android:name="FLUTTER_NOTIFICATION_CLICK"/>
|
|
211
|
+
<category android:name="android.intent.category.DEFAULT"/>
|
|
212
|
+
</intent-filter>
|
|
213
|
+
</activity>
|
|
214
|
+
|
|
215
|
+
</application>
|
|
216
|
+
</manifest>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 4. ์๋ฆผ ์์ด์ฝ ์ค๋น
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
Android ์๋ฆผ ์์ด์ฝ ์๊ตฌ์ฌํญ:
|
|
223
|
+
- ํฐ์ + ํฌ๋ช
๋ฐฐ๊ฒฝ (์์คํ
์ด ์์ ์ ์ฉ)
|
|
224
|
+
- PNG ํ์ (๋ฒกํฐ SVG ๋ถ๊ฐ)
|
|
225
|
+
- ํฌ๊ธฐ๋ณ ๋ฐฐ์น:
|
|
226
|
+
android/app/src/main/res/
|
|
227
|
+
โโโ mipmap-mdpi/ic_notification.png (24x24)
|
|
228
|
+
โโโ mipmap-hdpi/ic_notification.png (36x36)
|
|
229
|
+
โโโ mipmap-xhdpi/ic_notification.png (48x48)
|
|
230
|
+
โโโ mipmap-xxhdpi/ic_notification.png (72x72)
|
|
231
|
+
โโโ mipmap-xxxhdpi/ic_notification.png (96x96)
|
|
232
|
+
|
|
233
|
+
์๋ฆผ ์์:
|
|
234
|
+
android/app/src/main/res/values/colors.xml
|
|
235
|
+
<resources>
|
|
236
|
+
<color name="notification_color">#FF6B35</color>
|
|
237
|
+
</resources>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 5. Android ์๋ฆผ ์ฑ๋ ๊ฐ์ด๋
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
์ฑ๋ ์ค๊ณ ์์น:
|
|
244
|
+
- ์ฌ์ฉ์๊ฐ ๊ฐ๋ณ ์ ์ดํ ์ ์๋ ๋จ์๋ก ๋ถ๋ฆฌ
|
|
245
|
+
- ์ค์๋(Importance)์ ๋ฐ๋ผ ๋ถ๋ฅ
|
|
246
|
+
- ํ๋ฒ ์์ฑ๋ ์ฑ๋์ ์ค์๋๋ ์ฝ๋๋ก ๋ณ๊ฒฝ ๋ถ๊ฐ (์ฌ์ฉ์๋ง ๋ณ๊ฒฝ ๊ฐ๋ฅ)
|
|
247
|
+
|
|
248
|
+
๊ถ์ฅ ์ฑ๋ ๊ตฌ์ฑ:
|
|
249
|
+
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
250
|
+
โ Channel ID โ Importance โ ์ฉ๋ โ
|
|
251
|
+
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
|
|
252
|
+
โ matches โ HIGH โ ๋งค์น ์ด๋, ๋ณ๊ฒฝ, ์์ ์๋ฆผ โ
|
|
253
|
+
โ chat โ DEFAULT โ ์ฑํ
๋ฉ์์ง โ
|
|
254
|
+
โ reminders โ HIGH โ ๋งค์น ์์ ์ ๋ฆฌ๋ง์ธ๋ โ
|
|
255
|
+
โ system โ LOW โ ์ฑ ์
๋ฐ์ดํธ, ๊ณต์ง โ
|
|
256
|
+
โ silent โ MIN โ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ (์ฌ์ฉ์ ๋ฏธํ์) โ
|
|
257
|
+
โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
258
|
+
|
|
259
|
+
Importance ๋ ๋ฒจ:
|
|
260
|
+
- MAX: ํ๋ฉด ์๋จ ํผํฌ + ์๋ฆฌ + ์ง๋
|
|
261
|
+
- HIGH: ํค๋์
์๋ฆผ + ์๋ฆฌ + ์ง๋
|
|
262
|
+
- DEFAULT: ์๋ฆฌ + ์ง๋ (ํค๋์
X)
|
|
263
|
+
- LOW: ์๋ฆฌ X, ์ง๋ X (์ํ๋ฐ์๋ง)
|
|
264
|
+
- MIN: ์ํ๋ฐ์๋ ์ต์ ํ์ (์ ์ผ๋ฉด ๋ณด์)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Common Pitfalls
|
|
268
|
+
|
|
269
|
+
### iOS
|
|
270
|
+
1. **APNs ํค vs ์ธ์ฆ์**: .p8 Auth Key ์ฌ์ฉ ๊ถ์ฅ (๋ง๋ฃ ์์, ๋ชจ๋ ์ฑ์ ๊ณต์ ๊ฐ๋ฅ)
|
|
271
|
+
2. **์๋ฎฌ๋ ์ดํฐ**: iOS ์๋ฎฌ๋ ์ดํฐ๋ APNs ๋ฏธ์ง์ โ ์ค์ ๊ธฐ๊ธฐ์์๋ง ํธ์ ํ
์คํธ
|
|
272
|
+
3. **Provisional ์๋ฆผ**: iOS 12+ ์์ ๊ถํ โ ์๋ฆผ ์ผํฐ์ ์กฐ์ฉํ ์ ๋ฌ
|
|
273
|
+
4. **Background Modes ๋๋ฝ**: Xcode์์ Remote notifications ์ฒดํฌ ์ ํ๋ฉด ์ฌ์ผ๋ฐํธ ํธ์ ๋ฏธ์์
|
|
274
|
+
5. **Production vs Sandbox**: APNs ํ๊ฒฝ ์๋ ์ ํ (Debug=Sandbox, Release=Production)
|
|
275
|
+
|
|
276
|
+
### Android
|
|
277
|
+
1. **์ฑ๋ ์์ฑ ํ์ด๋ฐ**: ์ฑ ์์ ์ ์ฑ๋ ์์ฑ ํ์ (์๋ฆผ ํ์ ์ )
|
|
278
|
+
2. **์๋ฆผ ์์ด์ฝ**: ํฐ์+ํฌ๋ช
์ด ์๋๋ฉด ํ์ ์ฌ๊ฐํ์ผ๋ก ํ์
|
|
279
|
+
3. **targetSdk 34+**: `SCHEDULE_EXACT_ALARM` ๊ถํ์ด ๊ธฐ๋ณธ ๊ฑฐ๋ถ โ `canScheduleExactAlarms()` ์ฒดํฌ
|
|
280
|
+
4. **Doze ๋ชจ๋**: ๊ณ ์ฐ์ ์์ FCM์ Doze ํต๊ณผ, ์ผ๋ฐ ๋ฉ์์ง๋ ์ง์ฐ ๊ฐ๋ฅ
|
|
281
|
+
5. **์ฑ๋ ์ค์๋ ๋ณ๊ฒฝ**: ์ฝ๋๋ก ๋ณ๊ฒฝ ๋ถ๊ฐ โ ์ ์ฑ๋ ID ์์ฑํ๊ฑฐ๋ ์ฌ์ฉ์๊ฐ ์ง์ ๋ณ๊ฒฝ
|
|
282
|
+
|
|
283
|
+
### ๊ณตํต
|
|
284
|
+
1. **google-services.json / GoogleService-Info.plist**: ๋ฐ๋์ .gitignore์ ์ถ๊ฐ (๋ณด์)
|
|
285
|
+
2. **FlutterFire CLI**: `flutterfire configure` ๋ก ์๋ ์ค์ ๊ถ์ฅ
|
|
286
|
+
3. **์๋ฎฌ๋ ์ดํฐ**: Android ์๋ฎฌ๋ ์ดํฐ๋ Google Play Services ํฌํจ ์ด๋ฏธ์ง ํ์
|