[Flutter]Firebasae Cloud Messaging
서비스 앱을 만들 때, 거의 모든 웬만한 앱에는 다 들어가 있는 기능이 뭘까? 알람일 것이다. 이미 너무 많은 앱들은 알람 기능을 탑재하고 있고, 수 많은 앱들의 동시다발적인 알람은 사용자에게 많은 피로감을 준다. (나도 이 때문에, 앱깔면 제일 먼저 하는게 마케팅 수신동의 취소 누르기다)
그럼에도 기업들은 알람기능을 포기할 수 없다. 이 기능을 통해 리텐션을 올릴 수도 있고, 사용자가 서비스앱과 더 친근함을 느낄 수도 있고, 혹은 사용자의 귀차니즘을 해결하기 위해 무언가를(보통은 정보를) 알려주는 기능이 될 수도 있기 때문이다.
이런 편리?하고도 불편한 FCM을 구현해보자!
https://firebase.flutter.dev/docs/messaging/overview/
사실 이 문서에 정리가 비교적 잘 되어 있어서, 이 문서와 약간의(?) 서치를 겸용하면 만들 수 있다!
이 게시글은 내가 나중에 또 필요하면 보려고 작성하는 거라 일부 코드들이 어쩌면 생략 되어 있을 수도 있다ㅠㅠ
그리고 불확실하나 flutter에다가 firebase_messaging 라이브러리를 붙이고
await Firebase.*initializeApp*();
를 했다면, 앱에서 세팅을 하지 않았는데도, 푸쉬 알람이 간다. (만약 이전에 파베DB나 remote config나 이미 파베 기능과 붙어 있는 경우) 이 때문에, production에 테스트 알람이 가는 불상사가 생겼다. 기존에 파베랑 연결되어 있는 기능이 있다면, 아래 세팅을 안해도 알람이 갈 수 있으니, 확인해보자. (이 경우는 android 타켓으로만 진행한거라 ios는 또 별도인지 모르겠다 휴)
내가 생각하기에 하단의 세팅은 알람(noti)에 대한 컨트롤(소리, 화면에 표시하기 등등), 알람(noti)를 눌렀을 때, 화면 동작, 화면 띄우기 등을 세팅하는 것 같다. 나중에 제대로 테스트 앱을 하나 파서 테스트 해봐야지.
FCM 세팅하기
- Firebase 사이트, APPLE 개발자 사이트에 세팅하기 (only IOS)
만약 제공 앱 중에 IOS가 있다면 파베 사이트에다가 IOS앱에 대한 설정을 해주어야 한다.
https://firebase.flutter.dev/docs/messaging/apple-integration
이와 관련된 링크를 보며 따라해보자.
참고로 내가 헷갈렸던 부분인데, 만약 애플로그인을 체크한 identifier가 있으면, 그 계정에다가 APNS를 추가 체크해주면 되고, 애플로그인 할 때 받았던 인증키 그대로 사용하면 된다.
뭔가 다들 아는 내용이라 찾는 정보에 제대로 안나왔지만… 나는 앱린이라 이런거 다 써주셔야 안 헤맨다구요ㅠㅠ
그리고 파베에 인증키 업로드 할 때, 인증키 형식은 p8이다! p12랑 헷갈리지 말자!
- Flutter 세팅
firebase_messaging: ^11.2.0
먼저 pubspec.yaml 파일에다가 메세징 라이브러리를 집어 넣자!
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
}
main에 initializeApp()넣어주고
나 같은 경우에는 fcm을 처리해주는 controller를 따로 만들었다.
class FirebaseMessagingController extends GetxController{
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
@override
void onInit() {
_initNotification();
_firebaseMessagingForegroundHandler();
_firebaseMessagingBackgroundHandler();
super.onInit();
}
Future<void> _initNotification() async {
_firebaseMessaging.requestPermission();
}
Future<void> _firebaseMessagingForegroundHandler() async {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
//앱이 켜진 상태에서 알람을 눌렀을 때
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
_initMessage(); //resume상태에 있을 때, 알람을 눌렀을 때
});
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
_initMessage(); //백그라운드에 온 알람을 눌렀을 때
}
Future<void> _initMessage() async {
_firebaseMessaging.getInitialMessage().then((RemoteMessage? message) {
if (message != null) {
if (message.data['a'] != null) {
//a 가 key 값인 value를 비교
Get.toNamed("${message.data['a']}");
}
}
}
});
}
}
이런 식으로 하면, 알람에 따른 경로 분기처리 가능하다!
테스트하면서 local noti 처리 하는 것도 만들었는데, 꽤 귀찮다…
- local_notification 라이브러리를 추가해주고…
flutter_local_notifications: ^9.1.4
- android는 퍼미션 추가를 해준다.
**<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />**
<application
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true"
android:allowBackup="false"
>
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
**android:showWhenLocked="true"
android:turnScreenOn="true"**
- ios는 notification service를 만들어준다
그리고 다음과 같은 코드 작성 (swift 기준)
https://firebase.flutter.dev/docs/messaging/apple-integration/
import UserNotifications
import FirebaseMessaging
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
Messaging.serviceExtension().populateNotificationContent(bestAttemptContent, withContentHandler: contentHandler)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
- appdelegate에 다음과 같은 코드 추가
ios 10 이상부터는 다음 코드를 넣어줘야 동작!
Bool {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
- info.plist에도 추가
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
ios가 할게 많아서 그렇지 일단 세팅만 하면 코드는 공통으로 쓰는거라 훨씬 낫다.
FCM 구독하기
이제 세팅을 했다면, 또 뭘 할 수 있을까?
사용자를 나눠 원하는 사용자에게만 알림을 보내고 싶을 것이다.(예를 들어, 마케팅 수신을 한 사용자와 안한 사용자 등)
이런 경우엔 구독기능을 사용하면 된다! 방법도 아주아주 쉬운데, 다음과 같은 코드로 컨트롤 할 수 있다.
_firebaseMessaging.subscribeToTopic('something'); // 구독
_firebaseMessaging.unsubscribeFromTopic('something'); // 비구독
그럼 이 코드들로 유저를 나눴다면, 어떻게 알림을 구독자에게만 보낼 수 있을까?
파이어베이스 → 클라우드메세징 탭에서 다음과 같이 타겟에 something을 적어 something 구독자들에게만 알림을 날릴 수 있다! 이렇게 하면, 의도대로 사용자도 나누고 알림 날리기도 쉽고! 1석2조!
+)여담
안드로이드 FCM을 테스트하면서 어제까지만 해도 되던 FCM이 안되는 현상을 발견했다. 휴 또 왜지왜지 왜 안되지를 백번쯤 외쳤을 때, 앱의 배터리 최적화 퍼미션이 허용되어 있으면, 백그라운드에서 완전히 종료한 앱은 FCM이 안온다는 글을 읽었다.
왜 아무도 이런걸 안알려주냐구~! 안드로이드 일 때, 배터리 최적화 퍼미션 코드를 추가해보자
먼저 permission_handler
라이브러리를 추가하면, flutter에서 권한 획득하기 쉽다. 나는 이 라이브러리를 이용해 권한 alert를 띄웠다.
Future<void> _requestPermission() async {
if (Platform.isIOS) { // IOS일 때도 권한 허용은 해줘야 한다.
_firebaseMessaging.requestPermission();
} else {
if (PermissionStatus.denied == await Permission.ignoreBatteryOptimizations.status) {
WidgetsBinding.instance!.addPostFrameCallback((_) async {
await Permission.ignoreBatteryOptimizations.request();
});
}
}
}
이렇게 하면, 배터리 최적화 퍼미션을 받을 수 있다. 다만, 이 코드는 사용자가 퍼미션을 거부하면, 다음 접속 때도 또 배터리최적화 퍼미션이 뜬다. 다른 퍼미션과 다르게 영구적으로 안본다는 체크박스가 없는 듯 하다.
휴~ 이렇게 또 flutter 정복하기~ 성공~!
댓글남기기