Push Notification

D A S H B O A R D
D E V E L O P
S E C U R I T Y
IOS 및 안드로이드의 Notification 설정이 끝난 것을 가정하고 코드 예시

 flutter 설정

1. Main.dart

Future<void> backgroundHandler(RemoteMessage message) async { print(message.data.toString()); print(message.notification!.title); print(message.notification!.android); print('================================================================'); } // ignore: prefer_const_constructors void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); FirebaseMessaging.onBackgroundMessage(backgroundHandler); runApp(const RestartWidget(child: MyApp())); }
Dart
복사
1.
Firebase.initializeApp() Flutter SDK 초기화 후 Firebase 초기화를 가장 먼저해줌.
이 때 options로 플랫폼 별로 설정해줄 수 있음 → FirebaseOptions(DefaultFirebaseOptions)
2.
FirebaseMessaging.onBackgroundMessage(backgroundHandler);
앱이 백그라운드에 있을 때의 행동을 결정
핸들러 안에서 notification을 띄운다던가 필요한 행동을 하면 될 것 같다.

2. FirebaseMessage.dart

import 'dart:io'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; class LocalNotificationService { static final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin(); static const AndroidNotificationChannel channel = AndroidNotificationChannel( "채널 ID", "APP명", description: "description", // description importance: Importance.max, // 우선순위 높게 해줘야 백그라운드에서도 알림이 잘 온다. ); static void initialize(BuildContext context) async { const InitializationSettings initializationSettings = InitializationSettings( android: AndroidInitializationSettings("@mipmap/ic_launcher"), iOS: DarwinInitializationSettings()); await _notificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(channel); _notificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: (route) async { Navigator.of(context).pushNamed(route as String); }); } static void display(RemoteMessage message) async { try { NotificationDetails notificationDetails = NotificationDetails( android: AndroidNotificationDetails( channel.id, channel.name, channelDescription: channel.description, importance: Importance.max, priority: Priority.high, ), iOS: const DarwinNotificationDetails(), ); if (message.notification != null && Platform.isAndroid) { await _notificationsPlugin.show( message.notification.hashCode, message.notification?.title, message.notification?.body, notificationDetails, payload: message.data["route"], ); } } on Exception catch (e) { print(e); } } }
Dart
복사
flutter_local_notifications를 통해 알림 받을 채널 생성 및 시각화
1.
initialize() 가 직접적인 채널 생성 함수
initializationSettings 변수를 이용해여 Android와 IOS의 기본 설정 저장
AndroidFlutterLocalNotificationsPlugin 을 통해 Android 채널을 따로 생성해 줌 → Android 8 이상부터는 꼭 해주어야 함!!
_notificationsPlugin.initialize 나머지 플랫폼의 채널 생성 → onDidReceiveNotificationResponse : Android의 forground 상태에서의 Handling
2.
display() 를 이용해 팝업 창 등을 구현
notificationDetails() → IOS의 경우 Apple의 Push Notification UI를 이용 → Android의 경우는 UI를 만들어주어야 하기 때문에 채널우선순위 추가
_notificationsPlugin.show() 알림을 보여줄 UI 생성 → Android일 때만 사용

3. MainPage.dart(Main.dart에 해도 무방, 웬만하면 top-level에 구현)

void initState() { super.initState(); LocalNotificationService.initialize(context); // FirebaseMessaging.instance.setAutoInitEnabled(true); FirebaseMessaging.instance.requestPermission( alert: true, announcement: false, badge: true, carPlay: true, criticalAlert: false, provisional: false, sound: true, ); FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( alert: true, // Required to display a heads up notification badge: true, sound: true, ); FirebaseMessaging.instance.getAPNSToken().then((APNStoken) { // print(APNStoken); }); ///gives you the message on which user taps ///and it opened the app from terminated state FirebaseMessaging.instance.getInitialMessage().then((message) { if (message != null) { final routeFromMessage = message.data["route"]; Navigator.of(context).pushNamed(routeFromMessage); } }); ///forground work FirebaseMessaging.onMessage.listen((message) { if (message.notification != null) { LocalNotificationService.display(message); } }); ///When the app is in background but opened and user taps ///on the notification FirebaseMessaging.onMessageOpenedApp.listen((message) { if (message.notification != null) { final routeFromMessage = message.data["route"]; Navigator.of(context).pushNamed(routeFromMessage); } }); }
Dart
복사
1.
LocalNotificationService.initialize(context); → firebasemessage.dart에서 구현한 함수로 채널 생성 및 초기화
2.
FirebaseMessaging.instance.requestPermission() → 권한 요청
3.
FirebaseMessaging.instance.setForegroundNotificationPresentationOptions() → 소리, 배너, 팝업 등 알림이 어떻게 보일지 설정
4.
FirebaseMessaging.instance.getAPNSToken().then((APNStoken) {}); → Apple의 경우 APNS 토큰을 사용하는데, 이를 받아오는 역할
5.
FirebaseMessaging.instance.getInitialMessage().then((message) {});앱이 종료된 상태에서의 Handling → 보통 눌렀을 때 페이지 이동 구현
6.
FirebaseMessaging.onMessage.listen((message) {});어플리케이션이 켜져있을 경우의 Handling → 내부에 dispaly() 함수는 firebasemessage.dart에서 생성한 함수로 안드로이드일 경우에만 UI를 만들어주는 코드
IOS일 경우에도 하도록 하면 알림이 2개가 오니 주의!!
7.
FirebaseMessaging.onMessageOpenedApp.listen((message){});백그라운드일 경우의 Handling(어플이 탭에 있는 경우)
하지만 IOS일 경우 알림을 누르면 이쪽으로 오게 됨 → 즉, iOS 는 Foreground 상태일 때 onMessage.listen을 통해 푸시 알람을 받고 → 이 푸시 알람을 눌렀을 때 onMessageOpendApp.listen으로 핸들링

서버 설정

const schedule = require('node-schedule'); const admin = require('firebase-admin'); var serviceAccount = require("{firebase 콘솔에서 받은 server용 json파일}"); var certPath = admin.credential.cert(serviceAccount) admin.initializeApp({ credential : certPath }); //알림 보내기 1분마다 동작하는 스케줄링 코드 schedule.scheduleJob(' */1 * * * *', function () { //... //원하는 로직..... notificationHandler(when, token ) }) // 내 서버 예시로 자신의 알림 로직에 맞게 변경해야함 function notificationHandler(when, token){ //... //원하는 로직..... console.log("send") sendPushNotification(token, "제목", "내용"); } function sendPushNotification(target_token, title, body) { //target_token은 푸시 메시지를 받을 디바이스의 토큰값입니다 let message = { notification: { title: title, body: body, }, data: { route: 'main' // flutter 환경에서 알림 클릭시 라우팅 해주기 위한 데이터 없어도 됨 }, android:{ priority:"high", notification: { channelId: //firebase Channel ID(android_channel_id) } }, token: target_token, apns: { payload: { aps: {// 얘네는 IOS 설정인데 웬만하면 유지하되 잘 찾아보고 설정하는 것을 추천한다. badge: 2.0, "apns-priority": 5, sound: 'default' }, }, }, } admin.messaging().send(message) .then(()=> { console.log("success"); }) }
JavaScript
복사
1.
sendPushNotification() 에서 android 항목에서 priority와 channer ID를 반드시 지정해주어야 함 → 안그러면 우선순위를 낮게 처리해 새로운 채널을 스스로 생성해 기타 채널로 들어감 → 기타 채널의 경우 우선순위가 낮기 때문에 팝업이 뜨지 않음

 Another

FirebaseOptions.dart(DefaultFirebaseOptions)

import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; import 'package:flutter/foundation.dart' show defaultTargetPlatform, TargetPlatform; class DefaultFirebaseOptions { static FirebaseOptions get currentPlatform { switch (defaultTargetPlatform) { case TargetPlatform.android: return android; case TargetPlatform.iOS: return ios; default: throw UnsupportedError( 'DefaultFirebaseOptions are not supported for this platform.', ); } } // 아래 값들은 예시를 위해 비슷한 아무 값이나 넣은 것이니 자신의 것으로 바꿔서 잘 넣길 바란다. static const FirebaseOptions android = FirebaseOptions( apiKey: 'AIzaSyC4XdjP_4_NwqZlgD8_121234512345Vt4', appId: '1:585309393841:ios:65cce1a2f460531wcvc1234', messagingSenderId: '123456789012', projectId: 'project-12345' ); static const FirebaseOptions ios = FirebaseOptions( apiKey: 'AIzaSyC4XdjP_4_NwqZlgD8_121234512345Vt4', appId: '1:585309393841:ios:65cce1a2f460531wcvc1234', messagingSenderId: '123456789012', projectId: 'project-12345', androidClientId: '123456789012-171vxy4x70dc717u8ul7s49ampk13lul.apps.googleusercontent.com', iosClientId: //완료 '123456789012-630881ql99k5djna2ivlic6krg0coole.apps.googleusercontent.com', iosBundleId: 'com.project', //완료 ); }
Dart
복사