본문 바로가기

안드로이드

FCM (Firebase Cloud Messaging) 푸시 알림 실습 (push notification)

지금까지 FCM과 안드로이드 연동, FCM을 이용한 어플에서 구글 계정 로그인, 페이스북 계정 로그인 (아직 완료버전까지 포스팅은 못했지만....) 까지 실습해 보았다. 이번 포스팅에서는 FCM의 기능중 하는 cloud messaging 인 push notification에 대하여 실습해보겠다. 한마디로 FCM 콘솔에 들어가서 관리자가 날리는 push를 설치한 모든 유저에게 noti를 해주는 방식이다.



일단 실습을 하기전에 자신의 계정 FCM과 안드로이드가 연동된 상태여야 한다. 방법은 아래의 링크를 참고해서 연동만 시켜두고 본 포스팅을 따라하도록 하자. 아마 내가 올려놓을 코드를 그대로 다운받아서 자신의 FCM 계정에 연동시키려 한다면 패키지명도 변경하고 이것저것 에러가 나올 수 있다...그럴바에는 얼마 안걸리니까 새로 프로젝트를 하나 파버리고 연동시켜버리고 실습을 따라하는게 속편할것이다.


참고: https://neosla.tistory.com/24?category=811318


이제 준비가 다 되었다면 FCM의 push notification이 무엇이고 어떠한 경우에 사용을 하는것지 알아보도록 하자!!!



위의 그림을 보면 오늘 하려는 실습의 전체적인 설명은 정말 간단하다. fcm 콘솔에 들어가서 좌측 하단에 성장->cloud messaging에 들어가서 관리자가 보내고 싶은 내용을 입력후 send를 누르면 fcm 플랫폼에서 자동으로 어플이 설치된 디바이스로 push가 날라가게 된다. 오늘은 이 실습을 진행한다. 어플을 사용하다보면 업데이트의 내용 혹은은 특정 상황에 대한 내용을 디바이스에 전체적으로 알림을 알려주어야 하는 상황이 생긴다. 그럴때? 사용하는 기능으로 보면 될거 같다. 그럼 실습을 진행해보도록 하겠다.



1. firebase SDK 추가


firebase SDK를 추가하기 위해서는 gradle의 추가가 필요하다. 앞서 말한것처럼 이미 fcm 기본적인 연동을 한 상태라면 app 수준의 gradle에 아래와 같이 추가해주면 된다.


//push implementation
implementation 'com.google.firebase:firebase-messaging:17.3.4'
implementation 'com.google.android.gms:play-services-auth:16.0.1'


이렇게만 추가해주면 gradle 설정은 끝이다. 아마 gradle 에서 문제가 생겼다면 어플리케이션과 fcm 연동을 안한 상태에서 했기 때문일거다. sync를 해주고 다음 단계로 넘어가자.


2. 메니페스트 추가


push 기능을 구현하기위해 우리는 두개의 클래스를 생성해 줄것이다. fcm과 push를 위해서 고유한 토큰을 등록해주기 위한 FirbaseInstanceIDService 클래스와 실질적으로 push의 기능구현을 위한 FirebaseMessagingService 두개의 클래스이다. 그러므로 먼저 매니페스트에 아래와 같이 추가를 해주도록 하자. 아직 빨간색으로 표시되는 경우는 우리가 클래스 생성을 하지 않았기 때문이다. 걱정하지 말고 다음스텝으로 넘어가자!!


<service
android:name=".FireBaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>

<service
android:name=".FirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>


2. FirebaseInstanceIDService.java


import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;


public class FirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseInstanceIDService";

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

private void sendRegistrationToServer(String token) {
}
}


디바이스 사용자별 token을 조회 후 서버에 전송하는 클래스 이다. push 알림을 특정 타겟에게 보낼때 사용되는 고유 키 값으로 보면된다. 또한 토큰이 새로 생성될 때마다 onTokenRefresh 콜백이 실행되므로 이 콜백의 컨텍스트에서 getToken을 호출하면 사용 가능한 현재 등록 토큰에 항상 액세스 하게된다. 토큰이 새로 생성되는 상황은 다음과 같다.


등록 토큰이 변경되는 경우

1. 앱에서 인스턴스 ID 삭제

2. 새 기기에서 앱 복원

3. 사용자가 앱 삭제 혹은 재설치를 할 경우

사용자가 앱 데이터 소거


3. FireBaseMessagingService.java


import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Vibrator;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class FireBaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";

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

if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());

if (true) {
} else {
handleNow();
}
}
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
sendNotification(remoteMessage.getNotification().getBody());
}
}
private void handleNow() {
Log.d(TAG, "Short lived task is done.");
}

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, intent,
PendingIntent.FLAG_ONE_SHOT);

//String channelId = getString(R.string.default_notification_channel_id);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("FCM Message")
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);

NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
/*
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelName = getString(R.string.default_notification_channel_name);
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
}*/

notificationManager.notify(0, notificationBuilder.build());
}
}


푸시 메시지가 들어왔을 경우 실제 디바이스를 가진 사용자에게 푸시 알림을 만들어서 띄어주는 클래스이다. API를 통해 푸시 알림을 전송하면 입력한 내용이 메시지에 담겨서 오게 된다.


크게 onMessageReceived, handleNow, sendNotification 세개의 함수가 존재한다. 자세하게 알아보도록 하자.


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

if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());

if (true) {
} else {
handleNow();
}
}
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
sendNotification(remoteMessage.getNotification().getBody());
}
}


일반적으로 이 부분에 메시지의 두가지 타입 1. data payload / 2. notification payload에 따라 다른 처리를 해주면 된다. 왜냐? 푸시를 보내고 푸시 알림 안에는 입력하는 데이터의 내용의 태스크 처리를 해주기 위함이다.


if (remoteMessage.getData().size() > 0) {}

if (remoteMessage.getNotification() != NULL) {}


로 처리를 해주었다. if (remoteMessage.getData().size() > 0) 는 데이터의 내용 태스크 처리를 위함이라고 했다. 그러면 현재 문제 없이 시간안에 태스크를 실행했을 경우 handleNow로 넘어가게 된다. 또한 푸시메시지가 정상적으로 실행되었을 경우 sendNotification 함수가 호출된다.

sendNotification 함수의 경우 실제 push noti의 형태를 구성하기 위한 기능을 기술할 수 있다.


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, intent,
PendingIntent.FLAG_ONE_SHOT);

//String channelId = getString(R.string.default_notification_channel_id);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("FCM Message")
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);

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

notificationManager.notify(0, notificationBuilder.build());
}


푸시 알림의 아이콘 설정, 알림, 사운드 진동설정, 터시 반응 후 삭제 등을 기술한 것이다. 푸시 알림이 날라왔을 경우 타이틀을 어떻게 할지 또한 푸시 알림의 아이콘을 어떻게 할지를 정해주면 된다.


.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("FCM Message")
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);


4. MainActivity.java

메인 부분 onCreate 안에 아래와 같이 적어주면 된다. 다음 스텝에서 알 수 있겠지만 FCM console에서 푸시를 할때 주제라는 것을 선택할 수 있다 그때 news라는 토픽을 작성해줌으로써 news 토픽을 보고있는 디바이스들은 푸시에 반응을 하게된다.

//push message
FirebaseMessaging.getInstance().subscribeToTopic("news");
FirebaseInstanceId.getInstance().getToken();


5. 실습

이제 어플리케이션 설치를 함과 동시에 fcm console로 들어가보자 우측에 보면 성장 이라는 부분이 있을 것이다 성장을 클릭릭후 Cloud Messaging을 클릭해주면 다음과 같이 뜬다. 나는 테스트를 여러번 했기때문에 테스트 이력들이 있다. 이거는 무시하고 우측에 있는 새 알림을 클릭해주자.


이제 어플리케이션 설치를 함과 동시에 fcm console로 들어가보자 우측에 보면 성장 이라는 부분이 있을 것이다 성장을 클릭릭후 Cloud Messaging을 클릭해주면 다음과 같이 뜬다. 나는 테스트를 여러번 했기때문에 테스트 이력들이 있다. 이거는 무시하고 우측에 있는 새 알림을 클릭해주자.

알림 제목과 알림 텍스트는 개인이 아무거나 지정해도 된다. 이 내용이 디바이스로 푸시로 날라가게 된다. 입력을 했다면 다음을 클릭하자.



news 좀전에 얘기했던 MainActivity에서 설정했던 내용을 적어준 것이다. MainActivity에서 news라는 토픽을 구독하고 있기때문에 news라고 주제를 주면 해당 디바이스가 반응한다 이제 다른 예약, 전환 이벤트, 추가옵션은 무시하고 바로 검토를 눌러준다.



자!!! 마지막 단계이다. 여기서 게시를 눌러주면 어플리케이션이 설치된 디바이스 들에게 푸시 알림이 날라갈 것이다!!! 이제 끝이다..


만약에 푸시가 안날라간다면 깃허브에 코드를 올려놨으니 틀린부분이 없는지 확인해보면된다. 그래도 안된면 댓글을 남겨주세용~~


https://github.com/songtaegeon/android_FCMpush


    }