로그인
회원가입
Loading...
Loading...

Board

게시판

FCM 푸시 알림 안드로이드 앱 가이드

FCM(Firebase Cloud Messaging)은 기존의 GCM을 대체하는 구글의 새로운 메시징 서비스이다.
iOS에 APNS가 있다면 안드로이드에서는 FCM이 대응하는 관계이고, FCM을 경유해서 APNS망을 이용할 수 있다는 점에도 메리트가 있다.

공식 가이드 : https://firebase.google.com/docs/cloud-messaging/

파이어베이스 콘솔구글 플레이 디벨로퍼 콘솔과는 별개로 가입해야 한다.
가입후 파이어베이스 프로젝트 생성시 기존 구글 프로젝트를 가져올 수도 있고, 새로이 프로젝트를 만들 수도 있다.
프로젝트 생성시 입력할 패키지 네임은 안드로이드 스튜디오 프로젝트상의 앱 레벨 build.gradle에서 applicationId를 찾아서 통일시키자.


- google-service.json -



프로젝트를 생성하고 나면 셋팅으로


google-service.json 을 다운받는다.


다운받은 파일은 <프로젝트 경로\프로젝트 네임\app>폴더에 복사해 두면 된다.


- build.gradle -



이후 루트 레벨의 build.gradle과 앱 레벨의 build,gradle에 각각 SDK사용을 명시해 준다.
참고 : https://firebase.google.com/docs/android/setup

루트 레벨 build.gradle
dependencies {
classpath 'com.google.gms:google-services:3.0.0'
}

앱 레벨 build.gradle
apply plugin: 'com.android.application'
dependencies {
compile 'com.google.firebase:firebase-core:10.0.1'
compile 'com.google.firebase:firebase-messaging:10.0.1'
}
apply plugin: 'com.google.gms.google-services'


이후 과정은 거두절미하고 https://github.com/firebase/quickstart-android/tree/master/messaging/app/src/main 공식 샘플을 참고하자.


- AndroidManifest.xml -



github 샘플 : AndroidManifest.xml

FCM 아이콘 메타는 무시해도 상관이 없다.
중요한건 하단의 서비스 정의

<!-- [START firebase_service] -->
<service
android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<!-- [END firebase_service] -->
<!-- [START firebase_iid_service] -->
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<!-- [END firebase_iid_service] -->

기존의 GCM 설정에 비하면 한결 깔끔해졌다.


- MyFirebaseInstanceIDService.java -



github 샘플 : MyFirebaseInstanceIDService.java

다음 처럼 MyFirebaseInstanceIDService.java 클래스를 추가하자.



@Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);

sendRegistrationToServer(refreshedToken);
}
private void sendRegistrationToServer(String token) {
// TODO: Implement this method to send token to your app server.
}

디바이스에 앱을 설치할때 리프레시되는 토큰을 조회하는 파트와, 알아낸 토큰을 차후 앱 서버에 전송하는 빈 함수가 있다.


- MyFirebaseMessagingService.java -



마찬가지로 MyFirebaseMessagingService.java 클래스를 추가

github 샘플 : MyFirebaseMessagingService.java

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "From: " + remoteMessage.getFrom());

// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
}

// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
}

private void sendNotification(String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);

Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_ic_notification)
.setContentTitle("FCM Message")
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);

NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}

데이터 메시징와 알림 메시지, 두 종류의 FCM을 처리하는 핵심적인 코드가 있다.


- Firebase Console Notification -



안드로이드 스튜디오 ADB, 가상 디바이스로 실행해보자.

이상없이 빌드되어서 앱이 실행되었다면 에뮬레이터 홈버튼을 눌러서 백그라운드로 돌린다.

파이어베이스 콘솔 알림으로 최초의 메시지를 날려보자.

타겟은 우선 패키지 네임을 이용한 세그먼트를 이용한다.

백그라운드에서 알림이 수신되면 성공이다!


- Data & Notification -



여기까지가 공식 샘플 내용이다.
하지만, 앱이 포어그라운드에서 떠 있을 경우에는 알림 수신이 되지 않는다...

분명히 sendNotification함수로 수신 관련로직을 정의했음에도 불구하고 onMessageReceived이벤트에서 실제로 호출하지 않았기 때문이다.

sendNotification이 호출되지 않음에도 백그라운드에서 알림이 표시되는 이유는?
그건 "알림 메시지"을 처리하는 주체가 안드로이드 운영체제이기 때문이다.

FCM 메시지의 분류 : https://firebase.google.com/docs/cloud-messaging/concept-options

즉, 데이터 메시지는 앱에서 전적으로 처리를 해야 하지만, 알림 메시지는 앱이 백그라운드일 경우 운영체제에서 처리함을 알 수 있다.
그리고 파이어베이스 콘솔 알림은 알림 메시지임을 유추할 수 있다.

데이터 메시지의 구성
{
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data" : {
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
},
}

알림 메시지의 구성
{
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification" : {
"body" : "great match!",
"title" : "Portugal vs. Denmark",
"icon" : "myicon"
}
}


- Foreground Process -



포어그라운드상황에서 정상적으로 알림을 처리하기 위해서는 onMessageReceived상에서 sendNotification을 호출해주어야 한다.

public void onMessageReceived(RemoteMessage remoteMessage) {
if (remoteMessage.getData().size() > 0) {
sendNotification(remoteMessage.getData().get("message"));
}
if (remoteMessage.getNotification() != null) {
sendNotification(remoteMessage.getNotification().getBody());
}
}


코드를 실행하고 에뮬레이터상에서 포어그라운드상태일때 다시 파이어베이스 콘솔에서 알림을 날려보자.
앱이 갑자기 꺼지는 사태가 발생한다면 공식 샘플에서 사용하는 아이콘 리소스가 없기 때문이다.

sendNotification 상의 아이콘 지정을 수정하자.
.setSmallIcon(R.mipmap.ic_launcher)//.setSmallIcon(R.drawable.ic_stat_ic_notification)

이제 포어그라운드 상태에서도 알림 메시지가 뜨는 것을 확인할 수 있다.


-- FCM 푸시 알림 --

FCM 푸시 알림 안드로이드 앱 가이드
FCM 푸시 알림 앱 서버 PHP 가이드
FCM 푸시 알림 iOS 앱 가이드

COMMENTS