푸시 알림을 간편하고 안정적으로 보내기 위해서 Firebase Cloud Messaging을 사용한다.
FCM(Firebase Cloud Messaging)은 무료로 메시지를 안정적으로 전송할 수 있는 교차 플랫폼 메시징 솔루션이다.
FCM을 사용하지 않고, 안드로이드 Service를 사용해 소켓 연결을 항상 유지하거나 polling 방식을 사용할 수도 있지만 배터리 성능이나 doze mode에서의 푸시 알림 처리, 교차 플랫폼 메시징 등을 고려했을 때 FCM을 사용하는 것이 합리적이다.
https://firebase.google.com/docs/cloud-messaging/fcm-architecture?hl=ko
FCM을 통해 푸시 알림을 보내는 과정은 아래 사진처럼 진행된다. 간단히 설명하면 푸시 서버에서 FCM backend로 알림 내용과 타겟이 되는 디바이스의 토큰을 담아 Post 요청을 보내면 FCM이 클라이언트로 알림을 보내는 것이다.
오래된 등록 토큰이 있는 비활성 장치에 메시지를 전송하여 리소스를 낭비하고 있을 수 있다. FCM 등록 토큰은 서버에서 저장하여 관리하는데, 클라이언트의 토큰과 active 토큰을 추적하면서 관리한다. 이를 관리하는데는 token timestamp 사용을 권장한다.
https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ko
FCM 메시지에는 notification과 data가 있다.
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification":{
"title":"Portugal vs. Denmark",
"body":"great match!"
}
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
}
}
}
notification 메시지
클라이언트 앱이 아닌 FCM이 자동으로 사용자 기기에 알림을 표시한다.
notification이 있으면 백그라운드 상태에서 onMessageReceived()
가 호출되지 않고 default 세팅으로 알림이 간다.
notification 내용이 없으면 백그라운드 상태에도 onMessageReceived()
가 호출되어 data에 담겨있는 값을 사용해 커스텀 알림을 보낼 수 있다.
data 메시지
클라이언트 앱이 메시지를 처리한다.
백그라운드 상태
onMessageReceived()
가 호출되어 클라이언트 앱 코드에 따라 커스텀된 알림을 띄울 수 있다.포그라운드 상태
플랫폼 공통 필드
message.notification.title
, message.notification.body
, message.data
는 플랫폼 관계없이 공통으로 사용된다. 특정 플랫폼에만 값을 보내려면 공통 필드가 아닌 플랫폼별 필드를 사용한다.메시지 구문
다운스트림 메시지 구문 이 링크에서 HTTP JSON 메시지의 대상, 옵션 및 페이로드를 확인할 수 있다.
REST Resource: projects.messages 이 링크에서 메시지 형식과 옵션을 확인할 수 있다.
FCM을 통해 푸시 알림을 보내면 서버와 클라이언트 앱과의 연결은 불필요하다. 하지만 클라이언트 앱이 오프라인 상태가 되는 등 FCM으로부터 메시지를 받을 수 없는 상황이 발생할 수 있다. FCM이 서버로부터 Post 요청을 받았다면 FCM은 기본적으로 클라이언트 앱이 메시지를 받을 때까지 계속 전송을 시도한다.
메시지 수명
https://firebase.googleblog.com/2019/02/life-of-a-message.html
https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ko#lifetime
https://firebase.blog/posts/2019/02/life-of-a-message
서버에서 FCM으로 푸시 요청을 보내면 FCM 백엔드에서 요청을 성공적으로 받았다는 success 응답을 준다. 하지만 이 응답이 클라이언트 앱 푸시를 성공했다는 의미는 아니다.
FCM이 요청을 받으면 아래 상황에 도달할 때까지 계속 클라이언트 앱으로 푸시 알림을 시도한다.
The message expires
정해진 메시지 수명이 끝나면 푸시 알림을 더 이상 보낼 수 없다. 디폴트 메시지 수명은 28일이다. 최대 28일동안 FCM은 전송 실패한 메시지를 보관하고 전송이 가능할 때까지 계속 시도한다.
다른 메시지로 인해 사라짐
collapse key를 설정했다면 기존 메시지가 새로운 메시지로 대체된다. collapse key를 별도로 설정하지 않았다면 FCM은 보내지 못한 메시지를 모두 보관한다.
디바이스가 한 달 이상 오프라인 상태인 경우
FCM는 디바이스가 온라인 상태인지, 언제부터 오프라인 상태였는지에 대한 정보를 가지고 있다. 한 달 이상 오프라인 상태였다가 온라인으로 돌아가면 FCM은 보관 중이던 메시지가 삭제 되었다는 것을 디바이스에게 알려준다.
대기 중인 메시지가 100개 초과
Android / iOS / Web 각 경우에 발생할 수 있는 delivery factors
https://firebase.blog/posts/2019/02/life-of-a-message#android-message-delivery-factors
안드로이드의 경우
Doze mode
기기의 화면이 꺼진 상태로 오래두면 안드로이드는 배터리 소모를 줄이기 위해 Doze mode가 활성화된다. 이 때 FCM은 중요도가 normal인 메시지는 보내지 않고 기기의 doze mode가 끝날 때까지 대기한다. 중요도가 high인 메시지는 전송된다.
Background Restrictions
안드로이드는 사용자가 강제로 앱의 백그라운드 실행을 제한할 수 있다. 이 경우 FCM이 푸시 알림을 전송하지 못할 수 있다.
전력 관리 제한
https://developer.android.com/topic/performance/power/power-details
App standby buckets에 따라 FCM의 푸시 알림의 중요도가 high여도 메시지가 전송되지 않을 수 있다. 예를 들어 기기가 절전 모드이고 앱이 빈도가 낮은 버킷에 있다면 메시지 전송이 연기될 수 있다.
Uninstalled application
FCM이 메시지 전송을 시도하는 중에 앱이 삭제되면 메시지는 즉시 폐기된다.
단일 기기에 대한 최대 메시지 속도
단일 기기에 전송할 수 있는 최대 메시지 수는 분당 240개, 시간당 5,000개다. 이 한도는 전송 로직 오류로 인해 의도치 않게 기기의 배터리가 방전되는 것을 방지한다.