Firebase Cloud Messaging 푸시 알림 Add-on
Android에서 Firebase Cloud Messaging(FCM)으로 원격 푸시 알림을 받는 Add-on입니다. Firebase Messaging SDK의 FirebaseMessaging을 감싸 디바이스 토큰 발급과 삭제, 앱을 실행시킨 알림의 메시지 조회, 메시지 수신과 토큰 갱신 이벤트를 제공합니다. 메시지는 가공하지 않고 FCM이 전달한 값을 그대로 전달합니다.
Add-on은 발급받은 토큰을 Hive Axyl 서버에 등록하지 않습니다. 토큰은 Push 모듈의 UpsertTokenAsync로 등록하고, ProviderType에는 Fcm을 지정하세요.
Add-on은 알림 권한을 선언하거나 요청하지 않습니다. Android 13 이상에서 알림을 표시하려면 앱의 매니페스트에 POST_NOTIFICATIONS 권한을 선언하고 앱이 직접 요청하세요.
모듈 정보
| 항목 | 값 |
|---|---|
| 패키지 | com.com2usplatform.hiveaxyl.push.addon.fcm |
| 인터페이스 | IFCMPlugin |
| 네임스페이스 | Hive.Axyl.Push.Addon.FCM |
| 등록 메서드 | AddFCM() |
| 지원 플랫폼 | Android |
| 최소 사양 | Android API 29+, Unity 6000.0+ |
사전 준비
이 Add-on은 Firebase를 직접 초기화하지 않고, 앱에 포함된 Firebase 설정 파일로 초기화된 Firebase를 사용합니다. Firebase 콘솔에서 내려받은 google-services.json을 Assets/Plugins/Android/google-services.json에 두세요. 빌드할 때 Add-on이 생성된 Android 프로젝트에 Google 서비스 Gradle 플러그인을 적용해 이 파일을 반영합니다. 파일의 package_name이 앱의 패키지 이름과 다르면 Gradle 빌드가 실패합니다. 파일이 없으면 빌드 경고만 표시되고, GetTokenAsync()와 DeleteTokenAsync()가 예외 대신 Code가 FailedPrecondition인 Failure를 반환합니다.
Firebase Unity SDK를 함께 쓰는 앱은 Firebase Unity SDK의 메시징 기능을 제외하세요. 이 Add-on과 Firebase Unity SDK가 각각 메시지를 받는 서비스를 등록하면 OS가 어느 서비스로 메시지를 전달할지 정해지지 않습니다.
등록과 획득
HiveBootstrap.Initialize의 등록 단계에서 Push 모듈과 함께 등록한 뒤 HiveCore.TryResolve<T>()로 가져옵니다.
Unity 에디터에서는 등록되지 않습니다
이 Add-on은 Android로 빌드한 플레이어에서만 등록됩니다. Unity 에디터에서는 플랫폼을 Android로 바꿔도 등록되지 않으므로, HiveCore.Resolve<T>()를 쓰면 RegistrationNotFoundException이 발생합니다. 반드시 TryResolve<T>()로 확인한 뒤 사용하세요.
메서드 요약
모든 메서드는 마지막 파라미터로 CancellationToken ct = default를 받습니다. 호출 규약은 호출 컨텍스트를 참조하세요.
| 메서드 | 설명 |
|---|---|
| GetTokenAsync() | FCM 디바이스 토큰을 가져옵니다. |
| DeleteTokenAsync() | FCM 디바이스 토큰을 삭제합니다. |
| GetColdStartMessageAsync() | 앱을 실행시킨 알림의 메시지를 조회합니다. |
메서드
GetTokenAsync
FirebaseMessaging.getToken()으로 현재 FCM 디바이스 토큰을 가져옵니다. Add-on은 토큰을 저장하지 않으므로 호출할 때마다 Firebase에서 토큰을 가져옵니다.
토큰 발급에는 알림 권한이 필요하지 않습니다. 알림 권한은 받은 알림을 표시할지에만 영향을 줍니다. 토큰을 발급받으려면 기기에 Google Play 서비스가 설치되어 있어야 합니다.
| 항목 | 값 |
|---|---|
| 응답 | GetTokenResponse |
결과 케이스 — FcmServiceGetTokenResult
| 결과 케이스 | 와이어 코드 | 설명 |
|---|---|---|
Success | — | 토큰을 가져왔습니다. 토큰은 Data.Token에 담깁니다. |
UnknownOutcome | UNKNOWN | 이 SDK 버전이 알지 못하는 새 결과입니다. |
Failure | FAILURE | 호출을 마치지 못했습니다. Problem의 HiveError로 원인을 확인합니다. |
Failure의 원인은 Problem.Code로 구분합니다.
Problem.Code | 원인 |
|---|---|
FailedPrecondition | Firebase가 초기화되지 않았습니다. google-services.json이 빌드에 포함되었는지 확인하세요. |
Unavailable | 기기에 Google Play 서비스가 없거나 버전이 낮습니다. 이때 Problem.ExternalCode는 PLAY_SERVICES_MISSING입니다. 네트워크 연결 문제로 FCM 서비스에 연결하지 못한 경우에도 이 코드입니다. |
Cancelled | CancellationToken으로 호출을 취소했습니다. |
Unknown | 그 밖의 Firebase 오류가 발생했습니다. |
호출 예시
토큰이 나중에 바뀌어도 다시 등록할 수 있도록 TokenRefreshed 이벤트를 먼저 구독한 뒤 토큰을 가져와 등록합니다.
using Hive.Axyl.Push;
using Hive.Axyl.Push.Addon.FCM;
fcm.TokenRefreshed += newToken =>
{
// 새 토큰을 UpsertTokenAsync로 다시 등록합니다.
};
var tokenResult = await fcm.GetTokenAsync();
if (tokenResult is FcmServiceGetTokenResult.Success token)
{
var result = await push.UpsertTokenAsync(new UpsertTokenRequest
{
Token = token.Data.Token,
ProviderType = UpsertTokenRequestProviderType.Fcm,
// TimezoneId, Country, Language, Agreement도 함께 지정합니다.
});
}
DeleteTokenAsync
FirebaseMessaging.deleteToken()으로 현재 FCM 디바이스 토큰을 삭제합니다. 삭제한 뒤 GetTokenAsync()를 호출하면 새 토큰을 받으며, FCM이 새 토큰을 발급하면 TokenRefreshed 이벤트로도 전달됩니다.
이 메서드는 Hive Axyl 서버에 등록한 토큰을 바꾸지 않습니다. Hive Axyl 서버에 등록한 토큰과 사용자의 연결을 해제하려면 DetachTokenIdentifierAsync를 참조하세요.
| 항목 | 값 |
|---|---|
| 응답 | DeleteTokenResponse |
결과 케이스 — FcmServiceDeleteTokenResult
| 결과 케이스 | 와이어 코드 | 설명 |
|---|---|---|
Success | — | 토큰을 삭제했습니다. |
UnknownOutcome | UNKNOWN | 이 SDK 버전이 알지 못하는 새 결과입니다. |
Failure | FAILURE | 호출을 마치지 못했습니다. Problem의 HiveError로 원인을 확인합니다. |
Failure의 원인은 Problem.Code로 구분합니다.
Problem.Code | 원인 |
|---|---|
FailedPrecondition | Firebase가 초기화되지 않았습니다. google-services.json이 빌드에 포함되었는지 확인하세요. |
Unavailable | 네트워크 연결 문제로 FCM 서비스에 연결하지 못했습니다. |
Cancelled | CancellationToken으로 호출을 취소했습니다. |
Unknown | 그 밖의 Firebase 오류가 발생했습니다. |
GetColdStartMessageAsync
앱이 종료된 상태에서 앱 사용자가 알림을 탭해 앱이 실행된 경우, 그 알림의 메시지를 반환합니다. 이 메서드는 앱이 실행된 뒤 처음 호출할 때만 메시지를 반환하므로, 앱이 실행 중일 때 탭한 알림의 메시지는 반환하지 않습니다. 앱을 시작할 때 한 번 호출하세요.
반환된 메시지의 Notification 필드는 비어 있을 수 있습니다. 탭한 알림에 따라 처리할 값은 알림 내용이 아니라 Data에서 읽으세요.
반환할 메시지가 없어도 Data.Message는 null이 아니라 필드가 비어 있는 메시지입니다. 알림 탭으로 실행되었는지는 Data.Message.MessageId가 비어 있지 않은지로 확인하세요. 알림 없이 앱을 실행했거나 두 번째 이후 호출이면 빈 메시지가 반환됩니다.
| 항목 | 값 |
|---|---|
| 응답 | GetColdStartMessageResponse |
결과 케이스 — FcmServiceGetColdStartMessageResult
| 결과 케이스 | 와이어 코드 | 설명 |
|---|---|---|
Success | — | 조회에 성공했습니다. 앱을 실행시킨 알림이 없으면 Data.Message는 빈 메시지입니다. |
UnknownOutcome | UNKNOWN | 이 SDK 버전이 알지 못하는 새 결과입니다. |
Failure | FAILURE | 호출을 마치지 못했습니다. Problem의 HiveError로 원인을 확인합니다. |
호출 예시
using System.Collections.Generic;
using Hive.Axyl.Push.Addon.FCM;
var result = await fcm.GetColdStartMessageAsync();
if (result is FcmServiceGetColdStartMessageResult.Success success
&& !string.IsNullOrEmpty(success.Data.Message.MessageId))
{
FcmRemoteMessage launchMessage = success.Data.Message; // 앱을 실행시킨 알림의 메시지
IReadOnlyDictionary<string, string> data = launchMessage.Data;
}
이벤트
두 이벤트 모두 엔진 메인 스레드에서 호출되므로 핸들러 안에서 엔진 API를 사용해도 됩니다. 이벤트는 발생한 시점에 구독 중인 핸들러에만 전달되며, 구독하기 전에 발생한 이벤트는 나중에 다시 전달되지 않습니다.
NotificationReceived
FCM이 FirebaseMessagingService.onMessageReceived로 메시지를 전달할 때 발생합니다. 앱이 포그라운드에 있으면 모든 메시지에서 발생하고, 앱이 백그라운드에 있으면 알림 내용 없이 데이터만 담긴 메시지에서만 발생합니다. 앱이 백그라운드에 있을 때 도착한 알림 메시지는 OS가 직접 표시하므로 이 이벤트가 발생하지 않습니다.
앱 서버가 리모트 푸시 전송으로 보내는 알림에는 항상 알림 내용이 담기므로, 이 이벤트는 앱이 포그라운드에 있을 때 발생합니다. 포그라운드에서 받은 메시지는 시스템 알림으로 표시되지 않으므로, 표시하려면 앱이 직접 표시하세요.
| 파라미터 | 타입 | 설명 |
|---|---|---|
| — | FcmRemoteMessage | FCM이 전달한 메시지입니다. |
TokenRefreshed
FCM이 FirebaseMessagingService.onNewToken으로 새 디바이스 토큰을 발급할 때 발생합니다. Add-on은 새 토큰을 Hive Axyl 서버에 등록하지 않으므로 이벤트로 받은 토큰을 UpsertTokenAsync로 다시 등록하세요.
| 파라미터 | 타입 | 설명 |
|---|---|---|
| — | string | 새로 발급된 FCM 디바이스 토큰입니다. |
데이터 타입
DeleteTokenResponse
필드가 없습니다.
FcmNotification
RemoteMessage.Notification의 원본 정보입니다. 값이 없는 필드는 빈 문자열이나 빈 목록입니다. 알림 내용 없이 데이터만 담긴 메시지이면 모든 필드가 빈 값입니다.
| 필드 | 타입 | 필수 여부 | 설명 |
|---|---|---|---|
Title | string | Required | 알림 제목인 getTitle() 값입니다. |
Body | string | Required | 알림 본문인 getBody() 값입니다. |
ImageUrl | string | Required | 알림 이미지 URL인 getImageUrl() 값입니다. |
Icon | string | Required | 알림 아이콘 리소스 이름인 getIcon() 값입니다. |
Sound | string | Required | 알림음 이름인 getSound() 값입니다. |
Tag | string | Required | 알림 태그인 getTag() 값입니다. 같은 태그의 알림은 새 알림으로 교체됩니다. |
ClickAction | string | Required | 알림을 탭했을 때 실행할 액션인 getClickAction() 값입니다. |
ChannelId | string | Required | Android 알림 채널 ID인 getChannelId() 값입니다. |
Color | string | Required | #rrggbb 형식의 알림 아이콘 색상인 getColor() 값입니다. |
Link | string | Required | 알림에 연결된 링크인 getLink() 값입니다. |
TitleLocKey | string | Required | 알림 제목에 사용할 현지화 문자열 키인 getTitleLocalizationKey() 값입니다. |
TitleLocArgs | IReadOnlyList<string> | Required | 제목 현지화 문자열에 넣을 인자 목록인 getTitleLocalizationArgs() 값입니다. |
BodyLocKey | string | Required | 알림 본문에 사용할 현지화 문자열 키인 getBodyLocalizationKey() 값입니다. |
BodyLocArgs | IReadOnlyList<string> | Required | 본문 현지화 문자열에 넣을 인자 목록인 getBodyLocalizationArgs() 값입니다. |
FcmRemoteMessage
com.google.firebase.messaging.RemoteMessage의 원본 정보입니다. 알림 내용과 데이터 중 무엇을 우선할지, 딥링크를 어떻게 꺼낼지 같은 해석은 앱이 담당합니다.
| 필드 | 타입 | 필수 여부 | 설명 |
|---|---|---|---|
MessageId | string | Required | 메시지 ID인 getMessageId() 값입니다. GetColdStartMessageAsync()가 빈 메시지를 반환하면 빈 문자열입니다. |
SentTimeUnixMillis | long | Required | 메시지를 보낸 시각인 getSentTime() 값입니다. Unix epoch 밀리초입니다. |
SentTime | DateTimeOffset | Required | SentTimeUnixMillis를 UTC DateTimeOffset으로 바꾼 값입니다. |
From | string | Required | 메시지 발신자인 getFrom() 값입니다. |
CollapseKey | string | Required | 같은 키의 메시지를 하나로 합칠 때 쓰는 getCollapseKey() 값입니다. |
Priority | int | Required | 메시지가 전달된 우선순위인 getPriority() 값입니다. 0은 알 수 없음, 1은 높음, 2는 보통입니다. |
TtlSeconds | int | Required | 메시지의 유효 기간인 getTtl() 값입니다. 초 단위입니다. |
Ttl | TimeSpan | Required | TtlSeconds를 TimeSpan으로 바꾼 값입니다. |
Data | IReadOnlyDictionary<string, string> | Required | 메시지에 담긴 데이터인 getData() 값입니다. 앱 서버가 리모트 푸시 전송의 options.customData로 보낸 데이터가 여기에 담깁니다. 데이터가 없으면 빈 딕셔너리입니다. |
Notification | FcmNotification | Required | 알림 내용인 getNotification() 값입니다. 데이터만 담긴 메시지이면 모든 필드가 빈 값입니다. |
GetColdStartMessageResponse
| 필드 | 타입 | 필수 여부 | 설명 |
|---|---|---|---|
Message | FcmRemoteMessage | Required | 앱을 실행시킨 알림의 메시지입니다. 반환할 메시지가 없으면 null이 아니라 필드가 비어 있는 메시지이며, MessageId가 비어 있는지로 구분합니다. |
GetTokenResponse
| 필드 | 타입 | 필수 여부 | 설명 |
|---|---|---|---|
Token | string | Required | FCM 디바이스 토큰입니다. Push 모듈의 UpsertTokenRequest.Token에 넣습니다. 민감 정보이므로 SDK 로그에 평문으로 기록되지 않습니다. |