장치의 상태에 따라 수신 메시지가 다르게 처리됩니다. 이러한 시나리오와 FCM을 자체 애플리케이션에 통합하는 방법을 이해하려면 먼저 기기가 있을 수 있는 다양한 상태를 설정하는 것이 중요합니다.
상태 | 설명 |
---|---|
전경 | 응용 프로그램이 열려 있고, 보고 있고, 사용 중일 때. |
배경 | 애플리케이션이 열려 있지만 백그라운드에 있을 때(최소화됨). 이는 일반적으로 사용자가 기기의 "홈" 버튼을 누르거나, 앱 전환기를 사용하여 다른 앱으로 전환하거나, 다른 탭(웹)에서 애플리케이션을 연 경우에 발생합니다. |
종료됨 | 기기가 잠겨 있거나 애플리케이션이 실행되고 있지 않을 때. |
애플리케이션이 FCM을 통해 메시지 페이로드를 수신하기 전에 충족되어야 하는 몇 가지 전제 조건이 있습니다.
- FCM에 등록할 수 있도록 애플리케이션을 한 번 이상 열어야 합니다.
- iOS에서 사용자가 앱 전환기에서 애플리케이션을 스와이프하면 백그라운드 메시지가 다시 작동하려면 수동으로 다시 열어야 합니다.
- Android에서 사용자가 기기 설정에서 앱을 강제 종료한 경우 메시지가 작동하려면 앱을 수동으로 다시 열어야 합니다.
- 웹에서는 웹 푸시 인증서로 토큰을 요청했어야 합니다(
getToken()
사용).
메시지 수신 권한 요청(Apple 및 Web)
iOS, macOS 및 웹에서 기기에서 FCM 페이로드를 수신하려면 먼저 사용자의 허가를 받아야 합니다.
firebase_messaging
패키지는 requestPermission
메서드를 통해 권한을 요청하기 위한 간단한 API를 제공합니다. 이 API는 알림 페이로드를 포함하는 메시지가 사운드를 트리거할 수 있는지 또는 Siri를 통해 메시지를 읽을 수 있는지 여부와 같이 요청하려는 권한 유형을 정의하는 여러 명명된 인수를 허용합니다. 기본적으로 이 메서드는 합리적인 기본 권한을 요청합니다. 참조 API는 각 권한에 대한 전체 문서를 제공합니다.
시작하려면 애플리케이션에서 메서드를 호출합니다(iOS에서는 네이티브 모달이 표시되고 웹에서는 브라우저의 네이티브 API 흐름이 트리거됨).
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
요청에서 반환된 NotificationSettings
개체의 authorizationStatus
속성을 사용하여 사용자의 전반적인 결정을 결정할 수 있습니다.
-
authorized
: 사용자가 권한을 부여했습니다. -
denied
: 사용자가 권한을 거부했습니다. -
notDetermined
: 사용자가 권한 부여 여부를 아직 선택하지 않았습니다. -
provisional
: 임시 권한을 부여받은 사용자
NotificationSettings
의 다른 속성은 현재 장치에서 특정 권한의 활성화, 비활성화 또는 지원 여부를 반환합니다.
권한이 부여되고 다양한 유형의 기기 상태가 이해되면 이제 애플리케이션이 수신 FCM 페이로드를 처리하기 시작할 수 있습니다.
메시지 처리
애플리케이션의 현재 상태에 따라 다양한 메시지 유형 의 수신 페이로드를 처리하려면 다양한 구현이 필요합니다.
포그라운드 메시지
애플리케이션이 포그라운드에 있는 동안 메시지를 처리하려면 onMessage
스트림을 듣습니다.
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
스트림에는 페이로드의 출처, 고유 ID, 전송 시간, 알림 포함 여부 등과 같은 페이로드에 대한 다양한 정보를 자세히 설명하는 RemoteMessage
가 포함되어 있습니다. 애플리케이션이 포그라운드에 있는 동안 메시지가 검색되었으므로 Flutter 애플리케이션의 상태 및 컨텍스트에 직접 액세스할 수 있습니다.
전경 및 알림 메시지
애플리케이션이 포그라운드에 있는 동안 도착하는 알림 메시지는 기본적으로 Android 및 iOS 모두에서 눈에 보이는 알림을 표시하지 않습니다. 그러나 이 동작을 무시할 수 있습니다.
- Android에서는 "높은 우선 순위" 알림 채널을 만들어야 합니다.
- iOS에서는 애플리케이션의 프레젠테이션 옵션을 업데이트할 수 있습니다.
백그라운드 메시지
백그라운드 메시지를 처리하는 프로세스는 네이티브(Android 및 Apple) 및 웹 기반 플랫폼에서 다릅니다.
애플 플랫폼과 안드로이드
onBackgroundMessage
처리기를 등록하여 백그라운드 메시지를 처리합니다. 메시지가 수신되면 isolate가 생성되어(Android만 해당, iOS/macOS는 별도의 isolate가 필요하지 않음) 애플리케이션이 실행되지 않는 경우에도 메시지를 처리할 수 있습니다.
백그라운드 메시지 처리기에 대해 염두에 두어야 할 몇 가지 사항이 있습니다.
- 익명 함수가 아니어야 합니다.
- 최상위 함수여야 합니다(예: 초기화가 필요한 클래스 메서드가 아님).
- 함수 선언 바로 위에
@pragma('vm:entry-point')
주석을 달아야 합니다(그렇지 않으면 릴리스 모드를 위한 트리 쉐이킹 중에 제거될 수 있음).
@pragma('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 작업(예: 로컬 저장소 업데이트)을 수행하고, 다른 플러그인과 통신할 수 있습니다.
또한 가능한 한 빨리 논리를 완성하는 것이 좋습니다. 길고 집약적인 작업을 실행하면 장치 성능에 영향을 미치고 OS가 프로세스를 종료할 수 있습니다. 작업이 30초 이상 실행되면 장치에서 자동으로 프로세스를 종료할 수 있습니다.
편물
웹에서 백그라운드에서 실행되는 JavaScript 서비스 작업자 를 작성합니다. 서비스 워커를 사용하여 백그라운드 메시지를 처리합니다.
시작하려면 web
디렉토리에 새 파일을 만들고 firebase-messaging-sw.js
.
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js");
firebase.initializeApp({
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
});
const messaging = firebase.messaging();
// Optional:
messaging.onBackgroundMessage((message) => {
console.log("onBackgroundMessage", message);
});
파일은 앱과 메시징 SDK를 모두 가져와서 Firebase를 초기화하고 messaging
변수를 노출해야 합니다.
다음으로 근로자를 등록해야 합니다. 항목 파일 내에서 main.dart.js
파일이 로드된 후 작업자를 등록합니다.
<html>
<body>
...
<script src="main.dart.js" type="application/javascript"></script>
<script>
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// ADD THIS LINE
navigator.serviceWorker.register('/firebase-messaging-sw.js');
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
// ...
});
}
</script>
다음으로 Flutter 애플리케이션을 다시 시작합니다. 작업자가 등록되고 백그라운드 메시지가 이 파일을 통해 처리됩니다.
상호 작용 처리
알림은 눈에 보이는 신호이기 때문에 사용자가 알림과 상호작용(누르기)하는 것이 일반적입니다. Android 및 iOS의 기본 동작은 애플리케이션을 여는 것입니다. 응용 프로그램이 종료되면 시작됩니다. 백그라운드에 있으면 포그라운드로 가져옵니다.
알림 내용에 따라 응용 프로그램이 열릴 때 사용자의 상호 작용을 처리할 수 있습니다. 예를 들어 알림을 통해 새 채팅 메시지가 전송되고 사용자가 메시지를 누르면 애플리케이션이 열릴 때 특정 대화를 열 수 있습니다.
firebase-messaging
패키지는 이 상호작용을 처리하는 두 가지 방법을 제공합니다.
-
getInitialMessage()
: 응용 프로그램이 종료된 상태에서 열리면RemoteMessage
를 포함하는Future
가 반환됩니다. 소비되면RemoteMessage
가 제거됩니다. -
onMessageOpenedApp
: 애플리케이션이 백그라운드 상태에서 열릴 때RemoteMessage
를 게시하는Stream
입니다.
사용자에게 원활한 UX를 보장하기 위해 두 시나리오를 모두 처리하는 것이 좋습니다. 아래 코드 예제는 이를 달성할 수 있는 방법을 설명합니다.
class Application extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
// It is assumed that all messages contain a data field with the key 'type'
Future<void> setupInteractedMessage() async {
// Get any messages which caused the application to open from
// a terminated state.
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
// If the message also contains a data property with a "type" of "chat",
// navigate to a chat screen
if (initialMessage != null) {
_handleMessage(initialMessage);
}
// Also handle any interaction when the app is in the background via a
// Stream listener
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
}
void _handleMessage(RemoteMessage message) {
if (message.data['type'] == 'chat') {
Navigator.pushNamed(context, '/chat',
arguments: ChatArguments(message),
);
}
}
@override
void initState() {
super.initState();
// Run code required to handle interacted messages in an async function
// as initState() must not be async
setupInteractedMessage();
}
@override
Widget build(BuildContext context) {
return Text("...");
}
}
상호 작용을 처리하는 방법은 애플리케이션 설정에 따라 다릅니다. 위의 예는 StatefulWidget을 사용하는 기본 그림을 보여줍니다.
메시지 현지화
두 가지 방법으로 지역화된 문자열을 보낼 수 있습니다.
- 각 사용자의 선호 언어를 서버에 저장하고 각 언어에 대한 사용자 지정 알림을 보냅니다.
- 앱에 현지화된 문자열을 포함하고 운영 체제의 기본 로캘 설정을 활용합니다.
두 번째 방법을 사용하는 방법은 다음과 같습니다.
기계적 인조 인간
resources/values/strings.xml
에서 기본 언어 메시지를 지정하십시오.<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>
values- language
디렉터리에 번역된 메시지를 지정합니다. 예를 들어,resources/values-fr/strings.xml
에 프랑스어 메시지를 지정합니다.<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>
서버 페이로드에서
title
,message
및body
키를 사용하는 대신 현지화된 메시지에title_loc_key
및body_loc_key
를 사용하고 표시하려는 메시지의name
속성으로 설정합니다.메시지 페이로드는 다음과 같습니다.
{ "data": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" }, }
아이폰 OS
Base.lproj/Localizable.strings
에서 기본 언어 메시지를 지정합니다."NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";
language .lproj
디렉토리에 번역된 메시지를 지정하십시오. 예를 들어fr.lproj/Localizable.strings
에 프랑스어 메시지를 지정합니다."NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";
메시지 페이로드는 다음과 같습니다.
{ "data": { "title_loc_key": "NOTIFICATION_TITLE", "body_loc_key": "NOTIFICATION_MESSAGE" }, }
메시지 전달 데이터 내보내기 활성화
추가 분석을 위해 메시지 데이터를 BigQuery로 내보낼 수 있습니다. BigQuery를 사용하면 BigQuery SQL을 사용하여 데이터를 분석하거나 다른 클라우드 제공업체로 내보내거나 사용자 지정 ML 모델에 데이터를 사용할 수 있습니다. BigQuery로 내보내기에는 메시지 유형이나 메시지가 API 또는 알림 작성기를 통해 전송되는지 여부에 관계없이 메시지에 사용 가능한 모든 데이터가 포함됩니다.
내보내기를 활성화하려면 먼저 여기에 설명 된 단계를 따른 다음 다음 지침을 따르십시오.
기계적 인조 인간
다음 코드를 사용할 수 있습니다. dart await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
아이폰 OS
iOS의 경우 다음 내용으로 AppDelegate.m
을 변경해야 합니다.
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import <Firebase/Firebase.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}
@end
편물
Web의 경우 v9 버전의 SDK를 사용하기 위해서는 서비스 워커를 변경해야 합니다. v9 버전은 번들로 제공되어야 하므로 예를 들어 서비스 워커가 작동하도록 하려면 esbuild
와 같은 번들러를 사용해야 합니다. 이를 달성하는 방법을 보려면 예제 앱 을 참조하십시오.
v9 SDK로 마이그레이션한 후에는 다음 코드를 사용할 수 있습니다.
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
서비스 워커의 새 버전을 web
폴더로 내보내려면 yarn build
를 실행하는 것을 잊지 마세요.