mobile(react-native) 에서 push notification 이 어떻게 동작하는지 개념을 이해해보자.
푸시를 구현하다보면 많은 라이브러리들을 만난다.
https://github.com/react-native-push-notification/ios
https://github.com/zo0r/react-native-push-notification
https://github.com/invertase/react-native-firebase/tree/main/packages/messaging
https://github.com/invertase/notifee
각 라이브러리를 사용하기 전에 우선 푸시 알림에 대한 개념을 잡을 필요가 있다.
quit - 앱이 완전히 종료된 상태
background - 앱이 켜져는 있으나, 포커스가 안된 상태
foreground - 현재 앱을 사용중인 상태
앱 아이콘에 나타나는 읽지 않은 메세지를 시각적으로 알려주는 뱃지이다.
iOS, Android 모두 Badge 라고 칭한다.
우리가 흔히 인식하고 있는 푸시 알림의 정체 바로 그것이다.
iOS 에서는 이를 Notification Banner(Alert) 라고 명칭한다.
Android 에서는 이를 Notification 이라고 명칭한다.
그러면 이 알림 배너를 띄우는 방법들은 무엇이 있을까?
예를 들어서, 작심삼일이 되지 않도록 3일마다 알림 배너를 띄워주는 어플리케이션을 만든다고 생각을 해보자.
디바이스에서 직접 코드를 호출하여 알림 배너를 띄울 수 있다.
어플리케이션에서 3일마다 local notification 기능을 통해서 알림 배너를 띄워주도록 만들면, 서버 구현 없이도 만들 수 있다.
일반적으로 플랫폼 구분 없이 모두 API 가 지원된다.
library.displayNotification({
title: '작심삼일 앱',
body: '마음을 다잡으세요...',
interval: '3day',
})
말 그대로 원격에서 알림 배너를 띄울 수 있다. 우리가 흔히 이용하는 서비스들에서 동작하는 방식들이 모두 이것이다.
디바이스 식별이 가능한 토큰을 발급하고 이를 서버에 보내서 등록한다.
서버에서는 추후에 디바이스에서 발급한 토큰으로, APNs/FCM 을 통해서 원격 푸시 알림을 전송한다.
3일마다 알림 배너를 띄워주도록 하려면, 서버에서 3일마다 디바이스에 알림을 전송해주면 된다.
// client
const token = library.getDeviceToken();
server.registerDeviceToken(token);
// server
scheduler.interval('3day').run(() => {
notification.sendToDevice('device-token', {
title: '작심삼일 앱',
body: '마음을 다잡으세요...',
});
});
APNs/FCM 은 각각 애플에서 관리하는 푸시 알림 플랫폼, 구글에서 관리하는 푸시 알림 플랫폼이다.
FCM 은 안드로이드 뿐만 아니라 iOS 푸시도 지원해주므로, 크로스 플랫폼에서는 일반적으로 FCM 을 선택하면 편하다.
이제 원격 알림에 대해서 알아보았으니, 원격 알림이 각 플랫폼별로 어떤 동작의 차이점이 있는지 알아보자.
ios 에서는 기본적으로 서버에서 디바이스에 푸시를 전송할 때 아래와 같은 페이로드를 구성해야 한다.
{
"aps" : {
"alert" : {
"title" : "Title",
"body" : "Body",
},
"badge" : 5,
},
"hello": "world",
}
전송하는 페이로드에 aps.alert
이 포함되어 있으면 quit, background 상태에서는 알림 배너가 기본으로 나타난다.
foreground 에서는 알림 배너가 나타나지 않고, 알림이 도착했다는 이벤트를 willPresentNotification delegate method 를 통해서 처리할 수 있다.
이벤트에는 notification 에 대한 정보가 포함되어 있고, 만약 이를 앱을 사용중인 유저에게 보여주고 싶다면
알림 배너를 보여주거나(willPresentNotification 에서 바로 Alert 을 보여주도록 처리)
앱의 UI 를 통해서 알림을 보여주거나(willPresentNotification 에서 RN 라이브러리를 통해 JS 로 이벤트 전송받아서 처리)
혹은 무시해도 된다.
iOS 에서 이러한 처리에 대해서 버전별로 많이 바뀌어 왔고, 라이브러리마다 처리를 가이드하는 방식이 다르기 때문에
위의 네이티브의 근본적 동작을 이해하고 있어야, 나의 상황에 맞게 처리할 수 있다.
안드로이드에서는 원격으로 전송하는것을 Remote Message 라고 한다.
서버에서 디바이스에 Message 를 전송하고, 이를 클라이언트에서 Notification 으로 보여준다는 콘셉이다.
안드로이드에서는 서버에서 디바이스에 메세지를 전송할 때 아래와 같은 페이로드를 구성해야 한다.
{
"message":{
"notification":{
"title":"Title",
"body":"body"
},
"data": {
"hello": "world",
}
}
}
전송하는 페이로드에 message.notification
이 포함되어 있으면 quit, background 상태에서는 알림 배너가 기본으로 나타난다.
foreground 에서는 알림 배너가 나타나지 않고, 알림이 도착했다는 이벤트를 FirebaseMessagingService 를 통해서 수신할 수 있다.
이벤트에는 마찬가지로 notification 에 대한 정보가 포함되어 있고, 만약 이를 앱을 사용중인 유저에게 보여주고 싶다면
알림 배너를 보여주거나 (이벤트로 받은 notification 데이터를 사용해 local notification 로 알림 배너를 띄우도록 처리)
앱의 UI 를 통해서 보여주거나 (이벤트로 받은 notification 데이터를 사용해 앱 UI 로 보여주도록 처리)
혹은 무시해도 된다.
Android 는 비교적 일관된 콘셉을 계속해서 유지해 오고 있다.
하지만 Andorid13 부터는 Message 와 Notification 을 개념적으로 완전히 분리하면서, 메세지는 받아도 POST_NOTIFICATION 권한이 없어서 알림 배너 를 못띄우는 케이스가 생길 수 있다.
가장 기본적인 원격 푸시 알림에 대해서 알아보았다.
이제 좀 더 심화적으로 들어가서, 원격 알림을 백그라운드에서 수신하여 특정 동작을 수행할 수 있는 방법들에 대해서 알아보자.
이 방법들은 앱이 완전히 종료되어 있는 상태에서 원격 알림을 보내서
디바이스를 백그라운드 상태로 실행을 시키고, 핸들러를 통해서 특정 처리를 하도록 할 수 있는 기능이다.
클라이언트의 데이터를 서버에 전송할 수도 있고, 서버로부터 전송받은 데이터를 가공하여 알림을 띄우는 등의 행위도 가능하다.
3rd party 의 경우 해당 토큰에 silent notification 을 보내서, 해당 유저가 앱을 삭제했는지 등을 체크하기도 한다.
iOS 에서는 silent notification 이라고 하고, 자세한 문서는 다음의 링크에서 읽어볼 수 있다.
https://developer.apple.com/documentation/usernotifications/pushing-background-updates-to-your-app?language=objc
이를 사용하기 위해서는 content-available:1
만을 전송해야 하고, aps.alert
등 다른 액션을 포함시키면 안된다. (추가적인 디테일은 생략)
{
"aps" : {
"content-available" : 1
},
"hello" : "world",
}
전송하면 수신받은 디바이스는 background 에서 메세지를 수신하게 되고 (앱이 quit 상태였다면 앱이 실행됨)
didReceiveRemoteNotification delegate method 를 통해서 이벤트를 처리할 수 있다.
여기서 물론 local notification 을 사용하여, 커스터마이징 된 알림 배너를 띄우는 등의 처리도 물론 가능하지만
문서의 제약사항을 보면 알겠지만, 그런 목적이 아니고 수신 또한 100% 보장하지 않으므로 권장되지 않는다.
Android 에서는 Remote data message 라고 하고, 자세한 문서는 다음의 링크에서 읽어볼 수 있다.
https://firebase.google.com/docs/cloud-messaging/concept-options?hl=en#data_messages
이를 사용하기 위해서는 message.data
만을 전송해야 하고, message.notification
을 포함시키면 안된다.
{
"message":{
"data":{
"hello" : "world",
}
}
}
전송하면 수신받은 디바이스는 background 에서 메세지를 수신하게 되고 (앱이 quit 상태였다면 앱이 실행됨)
foreground 와 동일하게 FirebaseMessagingService 를 통해서 수신받아 처리할 수 있다.
iOS 와는 다르게 수신을 아주 잘 보장한다.
때문에 데이터 처리 이외에도, 수신받은 데이터를 가공하여 local notification 으로 커스터마이징 된 알림 배너를 띄우는게 가능하다.
시스템에서 처리되어 띄워주는 알림 배너는 정형화 되어있기 때문에, 서비스의 특성에 맞게 변경을 할 수 없는 구조이다.
그렇기 때문에 우리는 local notification 을 사용하거나 데이터를 변형 하여 서비스 특성에 맞춘 커스텀된 알림 배너를 띄워주어야 한다.
NotificationService Extension 의 didReceiveNotificationRequest 를 구현하면 수신받은 Remote notification 을 보여주기 전에 전처리를 할 수 있다.
이를 사용하기 위해서는 mutable-content:1
을 전송할때 포함해주어야 한다.
{
"aps" : {
"mutable-content" : 1,
"alert" : {
"title" : "Title",
"body" : "Body",
},
},
"hello" : "world",
}
예를 들면 title 을 변경을 한다던가, 암호화된 메세지를 복호화 한다던가, 데이터를 받아서 액션등을 붙여준다던가..
가장 대표적이게는 안드로이드와는 다르게 iOS 에서는, 알림 배너에 이미지 조차 알아서 띄워주지 않는다..
때문에 이미지를 다운로드 받아서 붙여주는 처리가 필요한데 이런 작업들 모두 여기서 가능하다.
이미지를 붙이는 등의 작업은 일반적으로 Firebase 같은데서 제공해주므로 라이브러리를 설치해서 사용하면 해결된다.
참고: https://rnfirebase.io/messaging/ios-notification-images
위에서 언급한대로 안드로이드는 remote data message 를 통해서 처리할 수 있다.
data 페이로드에 원하는 데이터를 넣고, 이를 클라이언트에서 처리하여 Notifee 와 같은 라이브러리를 사용해 local notification 을 보여주면 된다.
참고: https://notifee.app/react-native/docs/android/interaction
이제 기본적인 개념들에 대해서는 다 알아보았으니, 라이브러리별 특징에 대해서 알아보자.
react-native-push-notification/ios 는 iOS 에서 푸시 알림을 처리하기 위한 라이브러리다. local, remote 모두 지원을 한다. 잘 유지보수가 되는 느낌은 아니다.
react-native-push-notification 는 Android, iOS 에서 푸시 알림을 처리하기 위한 라이브러리다. ios 로직에는 react-native-push-notification/ios
라이브러리를 사용하고 있다.
local, remote 모두 지원되나 firebase messaging 과 같이 사용할 때, FirebaseMessagingService 를 두개 등록하여 푸시 알림이 두개씩 뜨는 경우가 있을 수 있으니 주의하자. (보통 FCM만 사용하면 쓸 일이 없다.)
react-native-firebase/messaging 는 Android, iOS 에서 FCM 을 처리하기 위한 라이브러리다. FCM 과 관련된 remote message, remote notification 수신만을 지원한다. APNs 관련된 동작은 지원을 하지 않는다. 아주 활발하게 유지보수가 이루어진다.
notifee local notification 을 아주 풍부하게 처리하기 위한 라이브러리이다.
하지만 ios 의 경우, remote notification 와 local notification 핸들러가 완벽하게 모두 나눠져 있지는 않으므로, 처리를 할 때 완벽한 이해를 바탕으로 구현이 필요하다. (때문에 ios 에서 remote notification 에 대한 처리도 일부 가능하기는 하다.)
활발까지는 아니지만 유지보수가 잘 이루어진다.
2024.03.16 현재 didReceiveNotificationResponse 등의 delegate 를 덮어쓰면서, notifee 가 설치되어 있으면 react-native-push-notification/ios 라이브러리가 제대로 동작하지 않는 이슈가 있다... (notifee#984)
각자 목적에 맞는 라이브러리를 사용하면 되고, 일반적으로 FCM 만 사용하는 경우 react-native-firebase/messaging
과 notifee
두개로 모두 커버가 가능하다!
지금까지 본 최고의 RN message 정리네요
개인적으로 대략 2년전 저는 onesignal로 구현해서 약간의 이슈는 있었지만 1년넘게 운영하면서 큰 문제는 없었던 라이브러리 겸 제품이 있어서 위 리스트에는 없는 것 같아 추천드립니다
https://documentation.onesignal.com/docs/react-native-sdk-setup