해당 글에서는 FCM 서버로 메시지를 전송하는 protocol에 대해서만 살펴볼 것이며,
SDK를 이용한 클라이언트 작업은 https://firebase.google.com/docs/cloud-messaging 에서 자세히 확인할 수 있다.
FCM Provider 에 대한 예제는 https://github.com/chulman/firebase-cloud-messaging 에 작성하였다.
Protocol
- 기본적으로 HTTP와 xmpp를 통해 메시지를 전송할 수 있다.
HTTP
XMPP
UpStream/DownStream
다운스트림 전용
최대 4kb의 데이터
업스트림/다운스트림
최대 4kb의 데이터
메시징 방식
Request & Response
-> 응답받기 전까지 다른 메시지를 보내지 못하도록 차단.
승인 또는 실패(ACK or NACK Json 인코딩 XMPP 메시지 형태)를 비동기적으로 보냄
JSON
HTTP POST로 전송된
JSon message
JSON MESSAGE가 XMPP 메시지로 캡슐화 됨.
일반텍스트
HTTP POST로 전송된
일반 텍스트 메시지
지원하지 않음.
멀티캐스트 다운스트림이 여러 등록 토큰으로 전송
JSON Message 형식에서 지원
지원하지 않음.
HTTP API
- Http API로 메시지를 전송하는 포맷은 http api 와 http v1 api 2가지가 존재한다.
- http v1 api를 사용하려면 google api 대쉬보드를 통해 등록해야 한다. (참조: Google API 대쉬보드에 FCM API 설정하기 )
1. http api VS http v1 api
- http api는 가장 심플한 레거시 방식으로 오랫동안 많은 사람들이 써왔던 방식이다. 특정 url로 규격에 맞는 json 포맷을 통해 전송하기 만하면 된다.
- http v1 api는 http api를 보안한 api라고 생각하면 될 것 같다. 가장 크게 강조하는 2가지는 보안과 멀티 플랫폼이다.
+ (1) 보안 : http v1 api 는 oauth 방식을 통해 fcm 서버와 연결하고 인증한다. (때문에 좀 더 까다롭다..)
+ (2) 멀티 플랫폼 : 기존 http api를 통해 메시지를 전송할 때는 보내는 형식이 기기 중심적이었다. 여러 기기를 묶어서 공통된 메시지를 보낼 수 밖에 없었는데, 이를 보완하고 플랫폼 별(ios, android..) 다양한 메시지 내용과 포맷을 하나의 json 포맷으로 구성할 수 있고 때문에 한 번의 전송으로 여러 플랫폼에 메시지 전송이 가능하다.
(해당 fcm 블로그에서 자세한 사항을 확인할 수 있다.) - https://firebase.googleblog.com/2017/11/whats-new-with-fcm-customizing-messages.html
2. request header
- request option : 메시지를 전송 할 때는 POST 방식이어야 한다.
- url :
+ http api : https://fcm.googleapis.com/fcm/send
+ http v1 api: https://fcm.googleapis.com/v1/ {parent=projects/*}/messages:send
- port : 주의할 점은 htps를 통한 연결이 때문에 443 포트에 대한 방화벽 정책이 허용되어야 한다.
- content type : application / json
- parameter :
+ http api : "authrization" : key = "api 서버 키"
+ http v1 api: "authrization" : bearer + "api 서버 키"
3. request body
- 메시지를 전송할 때 body에 json 형식의 payload를 보낼 수 있는데 format은 아래에서 확인할 수 있다.
+ https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ko
FCM 메시지 정보 | Firebase
Firebase 클라우드 메시징(FCM)은 다양한 메시징 옵션과 기능을 제공합니다. 이 페이지의 정보는 다양한 유형의 FCM 메시지에 관한 이해를 돕고 FCM으로 구현할 수 있는 기능을 소개하기 위한 내용입니다. 메시지 유형 FCM을 통해 2가지 유형의 메시지를 클라이언트에 보낼 수 있습니다. 알림 메시지: 종종 '표시 메시지'로 간주됩니다. FCM SDK에서 자동으로 처리합니다. 데이터 메시지: 클라이언트 앱에서 처리합니다. 알림 메시지에는 사용자에게
firebase.google.com
3.1 http api 요청 포맷 예
아래 포맷을 보면 특정 토큰(기기)에 notification이라는 메시지 형식을 통해 알림 형태를 지정하며, 커스텀 데이터를 추가해서 사용자가 메시지를 좀 더 다양하게 파악 할 수 있다.
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification":{
"title":"Portugal vs. Denmark",
"body":"great match!"
},
"data" : {
"Nick" : "Mario",
"Room" : "PortugalVSDenmark"
}
}
}
- 여러 토큰에 타겟해서 보낼 경우 registration_ids를 설정해서 보낼 수 있는데, 배열 갯수의 제한은 1000개 이다. 즉 한 번 전송시 1000개까지 밖에 전송할 수 없다.
{
"message":{
"notification":{
"title":"Portugal vs. Denmark",
"body":"great match!"
},
"data" : {
"Nick" : "Mario",
"Room" : "PortugalVSDenmark"
},
"registration_ids": ["bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...", ...]
}
}
- 때문에 FCM에서는 TOPIC이라는 키를 활용해서 좀 더 많은 디바이스에 한번에 전송할 수 있는 방법을 제공한다. 다음과 같이 메시지를 전송하면 "NEWS"라는 토픽을 등록한 기기에 모두 전송한다.
{
"message":{
"topic" : "NEWS",
"notification":{
"title":"Portugal vs. Denmark",
"body":"great match!"
},
"data" : {
"Nick" : "Mario",
"Room" : "PortugalVSDenmark"
}
}
}
3.2 http v1 api 요청 포맷 예
- 여러 플랫폼에 전송할 수 있는 형식을 사용할 수 있다.
{
"message":{
"topic":"subscriber-updates",
"notification":{
"body" : "This week's edition is now available.",
"title" : "NewsMagazine.com",
},
"data" : {
"volume" : "3.21.15",
"contents" : "http://www.news-magazine.com/world-week/21659772"
},
"android":{
"priority":"normal"
},
"apns":{
"headers":{
"apns-priority":"5"
}
},
"webpush": {
"headers": {
"Urgency": "high"
}
}
}
}
4. response header & Error Code
- 참조: https://firebase.google.com/docs/cloud-messaging/http-server-ref?hl=ko#error-codes
5. response body
- 마찬가지로 http api와 http v1 api의 response body는 다르다.
5.1 response body
- 전송 시 registration_ids 키값 에 5개의 기기를 포함했을 때, body 다음과 같은 형식이 될 것이고 5기기의 결과 값이 result 배열에 포함된다.
- http v1 api로 전송시 성공한다면 다음과 같은 형식으로 응답을 내 뱉는다.
{"name": "projects/{project-name}/messages/2280331808525169080"}
- 가장 큰 문제점은 에러가 발생했을 때 처리 부분인데, 에러코드가 너무 빈약하다는 점이다. 자세하게 확인하려면 admin sdk를 통해 따로 요청하는 등 방법이 존재하는데 이에 따른 비용이 더 생길 우려가 있다.