"Firestoreμ Firebase Cloud Messaging, Cloud Functionsλ₯Ό νμ©ν΄ μ€μκ° μλ¦Όμ μ 곡νλ SNS APP"
- μ΄λ―Έμ§μ ν¨κ» κ²μλ¬Όμ μμ±νκ³ , μ¬μ©μλ€μ λκΈκ³Ό μ’μμ μ΄λ²€νΈμ λν μ€μκ° μλ¦Όμ μ 곡νλ κ°λ¨ν SNS μ±
-
Β Β Β
Package Purpose flutter_riverpod
μν κ΄λ¦¬λ₯Ό μν΄ μ¬μ© shared_preferences
κ°λ¨ν λ°μ΄ν°λ₯Ό λ‘컬μ μ μ₯νκΈ° μν΄ μ¬μ©(λ‘κ·ΈμΈ μν, UserId λ±) go_router
νλ©΄ μ ν κ΄λ¦¬λ₯Ό μν΄ μ¬μ© get_it
μμ‘΄μ± κ΄λ¦¬λ₯Ό μν΄ μ¬μ© -
Β Β Β
Package Purpose firebase_auth
Firebase Authenticationμ μ΄μ©ν νμκ°μ μ§νμ μν΄ μ¬μ© cloud_firestore
Firestoreλ₯Ό μ΄μ©ν΄ μ¬μ©μ μ 보, κ²μκΈ, μλ¦Ό λ±μ λ°μ΄ν° μ μ₯νκΈ° μν΄ μ¬μ© firebase_messaging
μ’μμ, λκΈ μλ¦Όμ μ¬μ©μμκ² λ³΄λ΄κΈ° μν΄ μ¬μ© cloud_functions
μ’μμ, λκΈ μ΄λ²€νΈκ° λ°μνκ³ , Firestoreμ λ³κ²½μ¬νμ κ°μ§ν΄ ν΄λΉ μ¬μ©μμκ² μλ¦Όμ 보λ΄κΈ° μν΄ μ¬μ© firebase_storage
μ¬μ©μκ° μ λ‘λν κ²μλ¬Όμ μ¬μ§μ μ μ₯νκ³ , κ΄λ¦¬νκΈ° μν΄ μ¬μ©
-
Β Β Β
Β Β Β
Β Β Β
βββ core
β βββ constants
β β βββ colors.dart
β β βββ sizes.dart
β βββ di
β β βββ injector.dart
β βββ manager
β β βββ alert_manager.dart
β β βββ shared_preferences_manager.dart
β βββ router
β βββ router.dart
βββ data
β βββ datasources
β β βββ signin
β β βββ signup
β βββ models
β β βββ comment_model.dart
β β βββ notification_model.dart
β β βββ post_model.dart
β β βββ user_model.dart
β βββ repositories
β βββ signin
β βββ signup
β βββ user
βββ domain
β βββ repositories
β β βββ signin
β β βββ signup
β βββ usecases
β βββ signin
β βββ signup
βββ firebase_options.dart
βββ main.dart
βββ presentation
βββ screens
β βββ app
β βββ create_post
β βββ feed
β βββ notification
β βββ post_detail
β βββ profile
β βββ signin
β βββ signup
βββ widgets
βββ bottom_nav_bar.dart
βββ comment_card.dart
βββ custom_appbar.dart
βββ custom_floating_button.dart
βββ gesture_button.dart
βββ label_textfield.dart
βββ post_card.dart
- FCMκ³Ό Cloud Functionsλ₯Ό ν΅ν΄ μ€μκ° μλ¦Όμ μ 곡νλ λ°©λ²μ μ μ μμμ΅λλ€.
- μ΄κΈ°ν
Future<void> _initializeFCM() async { // μ±μ΄ μ€νμ€μ΄ μλ κ²½μ°, λ©μΈμ§λ₯Ό μ²λ¦¬νλ νΈλ€λ¬λ₯Ό μΆκ° FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); // Flutter Local Notificationμ μ΄κΈ°ν final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // μλλ‘μ΄λ μλ¦Ό μ±λ μμ± ( 8.0 μ΄μμμ νμ ) await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(const AndroidNotificationChannel( 'high_importance_channel', 'high_importance_notification', importance: Importance.max)); await flutterLocalNotificationsPlugin.initialize(const InitializationSettings( android: AndroidInitializationSettings("@mipmap/ic_launcher"))); // Foreground μνμμλ νμνκΈ° μν μ΅μ μ€μ await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( alert: true, badge: true, sound: true); }
- Cloud Functionsλ₯Ό μ΄κΈ°ν, index.js μμ±
- μμ ) μ’μμ μ΄λ²€νΈμ λν νΈλ¦¬κ±°λ₯Ό μ€μ ( firestoreμ document μμ± )
exports.sendLikeNotification = onDocumentCreated( "/posts/{postId}/likes/{uid}", async (event) => { /* [feed_notifier.dart] likeDocRef.set({ 'userId': userId, 'likedAt': FieldValue.serverTimestamp(), 'nickname': nickname, }); likesDocμ μμ μ½λλ₯Ό ν΅ν΄ μ’μμλ₯Ό ν΄λ¦ν μ μ κ° μΆκ°λλ€. */ // event -> onDocumentCreated("/posts/{postId}/likes/{uid}") // event.parms -> { postId: ..., uid: ... } const postId = event.params.postId; const uid = event.params.uid; /* "/posts/{postId}/likes/{uid}" κ²½λ‘μ μκΈ΄ λ°μ΄ν°λ₯Ό λ§νλ€. event.data -> { "userId": ..., "likedAt": ..., "nickname": ... } */ const likeData = event.data.data(); const postSnapshot = await admin.firestore().collection("posts").doc(postId).get(); const post = postSnapshot.data(); const authorId = post.uid; // μμ μ΄ μμ±ν κ²μλ¬Όμ μ’μμλ₯Ό λλ μ λλ μλ¦Ό μ²λ¦¬ X if (authorId === uid) { return; } // κ²μμμκ² μλ¦Ό 보λ΄κΈ° const userSnapshot = await admin.firestore().collection("users").doc(authorId).get(); const authorData = userSnapshot.data(); const token = authorData.fcmToken; const message = { notification: { title: "μλ‘μ΄ μ’μμ", body: `${likeData.nickname}λμ΄ κ²μλ¬Όμ μ’μν©λλ€.\nνμΈν΄λ³΄μΈμ :)`, }, token: token, // νμ μ ν΅ν΄ μμ΄μ½ μ΄κΈ°ν // IDλ₯Ό ν΅ν΄μ νλ©΄μ ν, κ²μλ¬Ό λ΄μ© νμ data: { type: "like", postId: postId, }, }; // FCMμ΄ λ©μΈμ§λ₯Ό 보λ΄λ μ½λ await admin.messaging().send(message); const notification = { type: "like", postId: postId, userId: uid, message: `${likeData.nickname}λμ΄ κ²μλ¬Όμ μ’μν©λλ€.`, createdAt: admin.firestore.FieldValue.serverTimestamp(), }; // firestoreμλ userμ notification 컬λ μ μ μΆκ°ν΄μ£ΌκΈ° await admin.firestore().collection("users").doc(authorId).collection("notifications").add(notification); }, );
- FCM 리μ€λλ₯Ό μ€μ νκ³ , μ²λ¦¬
-
appNotifier.dart μμ μλ¦Ό μμ μ νλ©΄μ νμλ μλ¦Ό κ°―μλ₯Ό μμ νλ€.
FirebaseMessaging.onMessage.listen((RemoteMessage message) async { RemoteNotification? notification = message.notification; if (notification != null) { FlutterLocalNotificationsPlugin().show( notification.hashCode, notification.title, notification.body, const NotificationDetails( android: AndroidNotificationDetails( 'high_importance_channel', 'high_importance_notification', importance: Importance.max, ))); // κ°―μ μΆκ° + μ μ₯ λ°μ΄ν° μ λ°μ΄νΈ int count = state.notificationCount; state = state.copyWith(notificationCount: count + 1); SharedPreferenceManager() .setPref<int>(PrefsType.unReadNotificationCount, count + 1); } });
- μ΄κΈ°ν
![]() |
μ§μ 리
- νλ‘ν νλ©΄ & νλ‘ν μμ νλ©΄ ꡬν - κ²μλ¬Ό μμ± νλ©΄ ꡬν |
![]() |
μ΄κ²½ν
- νμκ°μ & λ‘κ·ΈμΈ νλ©΄ κ΅¬μΆ λ° κΈ°λ₯ ꡬν - νΌλ νλ©΄ λ° κΈ°λ₯ ꡬν - Firestore, cloud storage ꡬμΆνκ³ κ΄λ ¨ λ‘μ§ μΆκ°(Create, Read) - Cloud Functionsλ₯Ό ν΅ν΄ μλ¦Ό μλν κ΅¬μΆ |