Future<void> main() async {
...
await FCMService().init();
FirebaseMessaging.onBackgroundMessage(
_firebaseMessagingBackgroundHandler); //TOP-Level
....
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
debugPrint('A background message just showed up : ${message.messageId}');
}
...
이렇게 작성된 코드가 있는데, 이에 대해서 설명해줘. 무엇을 하기 위한 코드고 어떻게 동작하고 어떠한 원리를 갖고있는지 궁금했다.
내가 이해한것은 우선은 이러하다.
await FCMService().init();
를 통해 커스텀해놓은 FCM 앱을 초기화하고 설정한다.Firebase.initializeApp
코드가 존재. )앱이 실행하면서 실행한 디바이스 및 기기에 대한 토큰값을 받아오고, FCM에 대한 초기화를 진행하는 코드라고 생각했다
_firebaseMessagingBackgroundHandler
를 실행한다하지만 여기서부터 궁금했던 것이 생긴다.
이미 초기화를 했는데, 왜 다시 백그라운드 일 떄 초기화할까?
에 대한 궁금점이 생겨서 좀 찾아봤다.
Firebase 초기화를 백그라운드에서 다시 실행시키는 이유는 Firebase 앱을 백그라운드 상태에서도 제대로 동작하도록 보장하기 위한 조치입니다. Firebase는 앱이 시작될 때 초기화되어야 하지만, 앱이 백그라운드에서 푸시 알림을 수신하거나 백그라운드 작업을 수행할 때 Firebase 기능을 계속 사용해야 합니다. Firebase는 이러한 상황에서도 제대로 동작하려면 Firebase 앱을 백그라운드에서 다시 초기화해야 합니다.
일반적으로 앱이 포그라운드(화면이 표시되는 상태)일 때 Firebase가 초기화됩니다. 이때 Firebase는 사용자와 상호작용하며 푸시 알림을 표시하거나 데이터를 처리하는 등의 작업을 수행합니다. 그러나 앱이 백그라운드로 전환되면 일부 플랫폼에서 Firebase 초기화가 중지될 수 있습니다. 이때 백그라운드에서 Firebase 기능을 계속 사용하려면 Firebase를 백그라운드에서 다시 초기화해야 합니다.
따라서 await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); 코드는 백그라운드에서 Firebase를 다시 초기화하여 Firebase 기능을 계속 사용할 수 있도록 합니다. 이렇게 하면 백그라운드에서 푸시 알림을 정상적으로 수신하고 처리할 수 있으며, 필요한 경우 데이터 처리 및 작업을 수행할 수 있습니다.
즉, Firebase 초기화는 앱의 현재 상태에 따라 Firebase 서비스를 사용할 수 있도록 유지하는 데 필요한 조치입니다.
[10/26 추가]
백그라운드에서 메세지를 처리하는 프로세스는 네이티브(Android 및 Apple) 플랫폼과 웹 기반 플랫폼에서 다릅니다.
나는 보통 푸시 알람을 모바일 네이티브(Android 및 Apple)에서 사용하려고 하기 때문에, 웹은 배제하기로 한다.
이 Android 및 apple 플랫폼에서 사용하려면 , onBackgroundMessage 핸들러를 등록하여 백그라운드 메시지를 처리한다.
여기서 주의사항으로는
- 익명 함수가 아니어야 한다.
- 최상위 수준 함수여야 한다.
- Flutter 버전 3.3.0 이상을 사용하는 경우 메시지 핸들러는 함수 선언 바로 위에 `@pragma('vm:entry-point')로 주석을 달아야 한다(그렇지 않으면 출시 모드의 경우 트리 쉐이킹 중에 삭제 가능성이 있음)
('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
왜 최상위 수준에서 함수를 작성해야하고 익명함수가 아니어야 할까?
❗ 핸들러는애플리케이션 컨텍스트 외부에서 독립적으로 실행되므로 애플리케이션 상태를 업데이트 하거나 UI에영향을 주는 로직을 실행할 수 없습니다. 그러나 HTTP요청과 같은 로직을 수행하고 IO작업(로컬스토리지업데이트)를 수행하며 다른 플러그인과 통신할 수 있다.
그렇기에 가능한 한 빨리 로직을 완료하는 것이 좋게, 집약적이로 오래 실행되는 태스크를 실행하면 기기 성능에 영향을 가기 때문에 30초가 넘게되면 자동으로 프로세스를 종료하는등, 애플리케이션의 Top-Level, 상위수준에서 로직이 이루어져야 한다.
[Firebase Docs] https://firebase.google.com/docs/cloud-messaging/flutter/receive?hl=ko