본문 바로가기

✏️/Flutter

[flutter] fcm 알림 - background 처리하기 (1편, 설정)

728x90

이 글은 flutter v.1.22 / firebase_messaging: ^7.0.3 기준으로 작성되었습니다.

지난 포스팅으로, local notification을 다루면서 fcm 포그라운드 환경 처리를 해주었다.
(이전글) - [flutter] local notification - FCM foreground 처리하기

+)

만약, foreground와 background에 대해서 의미가 헷갈리다면, 이 부분을 확인하길 바란다.

FCM 공식문서에서 나와있는, device 상태에 따른 설명

- Foreground포그라운드 : 앱을 사용하고 있을 때
- Background백그라운드 : 다른 앱을 사용하고 있어 최소화 되어 있을 때 등
- Terminated : 앱이 완전히 종료되었을 때, 또는 디바이스가 lock 상태일 때 등

또, 추가로 FCM 공식문서에서 디바이스 상태에 따른 페이로드 수신을 어떤 콜백으로 처리하고 있는지, 안내되어 있으니 참고!!

디바이스 상태에 따라 fcm 처리 하는 콜백

 

서론이 조금 길었지만, 이제 호다닥 background 상황에서의 처리에 대해서 알아보자.  

Background 

flutter에서 FCM이 어떻게 처리되는지 코드로 다시 한번 살펴보면 다음과 같다.

// 파이어베이스 메시징 설정하는 코드
firebaseMessaging.configure(
	// 포그라운드 콜백
    onMessage: ... 
    
    // 👀 백그라운드 콜백
    // 메시지가 데이터 메시지이거나 데이터 페이로드가 있는 알림 메시지인 경우에만 호출
    onBackgroundMessage: ...
    
    // 앱 종료 콜백
    onLaunch: ..
    // 앱 종료되었지만, 백그라운드에 있는 경우
    onResume: .. 
);

 

위 코드를 통해서 onBackgroundMessage 콜백함수가 백그라운드 상황일 때 처리를 해주고 있음을 확인 할 수 있다.
그럼 이제 background 일 경우, fcm 처리를 해보자!!!🔥

 

Android

android의 경우 백그라운드인 경우, 데이터에 해당하는 payload는 launcher의 인텐트로 부가정보가 전송을 해준다. 
(firebase - android fcm 설명 참고)

그렇기 때문에, AndroidManifest.xml 파일에서 intent-fliter를 추가해주자!

<application 
	android:name=".Application"
    ..
    >
    
   
    ..
    <activity 
    	android:name=".MainActivity"
        ...
        >
        
        <!-- 메인액티비티에 intent-filter 추가! -->
        <intent-filter>
                <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
   
   </activity>
 
 
 	...
 </application>

 

이 코드의 의미는, flutter firebase-message 플러그인이 안드로이드 환경에서 data에 해당하는 페이로드를 "FLUTTER_NOTIFICATION_CLICK"로 받기 때문에, 설정해주는 것이다.

(데이터가 있는 알림이 아니라고 해도, 설정해줘야 수신이 가능하다.)

 

백그라운드 메시지를 처리하기 위해서는, 다음과 같이 설정하라고 안내되어 있다.

It must be a top-level function (e.g. not a class method which requires initialization).
-> Define a top level Dart method to handle background messages
-> 백그라운드 메시지를 처리하는 Dart의 메소드는 최상위에 있어야 한다.

 

그럼, 그 에 맞춰서 background 콜백에 다음과 같이 설정해주자.

  static Future<dynamic> backgroundMessageHandler(Map<String, dynamic> message) {
    if (message.containsKey('data')) {
      // Handle data message
      final dynamic data = message['data'];
    }

    if (message.containsKey('notification')) {
      // Handle notification message
      final dynamic notification = message['notification'];
    }
  }

 

여기서 주의할 점은, 보통 FCM 함수의 경우 로그인 되었을 때 로직을 구성한다.
// 디바이스 토큰을 통해 유저의 상황에 맞는 푸시를 전송하기 때문이다.

따라서 앱이 시작하고 나서 (main 함수 호출 후) 로그인 로직이 끝나는 지점에서 fcm 코드를 위치하도록하자.

그리고 최상단에 위치해야 하기 때문에, static 함수를 클래스 밖에 선언해주면 된다!

핸들러까지 설정이 완료되면, 백그라운드 수신이 될 때 새롭게 선언한 backgroundMessageHandler가 실행될 것이다.

 

iOS의 경우, 위와같이 핸들러를 통한 수신이 이뤄지기 않기 때문에 그에 맞춰 콜백 설정을 해주면 ㅜㅜ 안드 끝!

_firebaseMessageing.configure(
...
	onBackgroundMessage: Platform.isIOS ? null : backgroundMessageHandler,
    // 안드로이드인 경우 backgroundMessageHandler 실행
...
);

 

iOS

iOS의 경우 Push provider에 의해 페이로드가 작성된다.  

따라서, 우리가 코드 단에서 크게 설정해줘야 하는 부분은 없다 (헤헤ㅋ)
// 만약, 이전편에 이어서 보지 않았으면 appdelegate 파일에 설정을 해줘야한다.

// AppDelegate.swift

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application( _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    
    
    // 이 코드 추가!
    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }
   
   ...
}

하지만 앱 내에서 백그라운드 수신을 허용해주면되는데, 방법은 매우 쉽다.

Xcode에서 설정해줘야 하는 부분이기 때문에, ios/Runner.xcworkspace 파일을 열어주자.

- Runner > TARGETS > Signing&Capabilities > Background Modes 추가
- Background fetch / Remote notifications 옵션 선택

그리고 마지막으로, Info.plist 로 가서 권한을 설정하면 끝!

// Info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	...
    
	<key>FirebaseAppDelegateProxyEnabled</key>
	<string>0</string>
    ...
</dict>

 

-
참고

https://github.com/FirebaseExtended/flutterfire/issues/199