본문 바로가기

안드로이드

[안드로이드] Firebase Cloud Messaging

Firebase Cloud Messaging

Firebase Cloud Messaging 이하 FCM은 메시지를 빌드, 전송, 수신하는 크로스 플랫폼 메시징 솔루션이다. 안드로이드 앱에서 흔하게 볼 수 있는 Push 메시지가 대표적인 사용 예시이다. 동작 방식은 아래 도표와 같다. Firebase 콘솔 GUI 혹은 써드 파티 서버(node, php, ...) 등에서 메시지를 전송하도록 FCM 서버에 요청하면, FCM 서버에서 이를 각 플랫폼에 맞게 적절히 메시지를 전달한다. 여기서 말하는 메시지란, 단순히 유저로에게 어떠한 알림을 보내는 것 이외에도 디테일한 데이터 전달 또한 가능하다. 이러한 데이터는 보통 json, xml로 전송된다.

 

안드로이드 Client 측에서의 세팅

1. Firebase SDK 추가

아주 기본적인 Firebase 구성요소 세팅은 콘솔에서 프로젝트를 생성할 때 다 안내되므로 생략한다. 먼저 app단위의 build:gradle에서 다음 구문을 추가하여 FCM SDK를 설치한다.

dependencies {
  // ...
  // Add the dependencies for the Firebase Cloud Messaging and Analytics libraries
  implementation 'com.google.firebase:firebase-messaging:20.2.4'
  implementation 'com.google.firebase:firebase-analytics:17.5.0'
}

 

2. manifest.xml 수정

FirebaseMessagingService를 상속받은 서비스 컴포넌트를 활용하기 위해 아래처럼 추가해준다. 이러한 과정은 클라이언트단에서 메시지를 받아 notification을 유저에게 발생시키거나 데이터를 처리하기 위해서 꼭 필요하다.

<service
    android:name=".java.MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

 

3. Token 정보 획득

초기에 앱이 시작될 때 FCM SDK에서는 registeration token을 생성한다. 이 토큰은 디바이스를 식별하기 위해 사용된다. 그리고 앱이 설치/재설치/앱 데이터 삭제 시에 token 정보는 바뀔 수 있다. 그렇기에 항상 token 정보를 최신의 상태로 유지하여야 한다.

 

-token 획득 방법

FirebaseInstanceId.getInstance().instanceId
        .addOnCompleteListener(OnCompleteListener { task ->
            if (!task.isSuccessful) {
                Log.w(TAG, "getInstanceId failed", task.exception)
                return@OnCompleteListener
            }

            // Get new Instance ID token
            val token = task.result?.token

            // Log and toast
            val msg = getString(R.string.msg_token_fmt, token)
            Log.d(TAG, msg)
            Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
        })

 

- token의 변화를 감지하는 방법

FirebaseMessagingService를 상속받으면 아래 onNewToken 메서드를 오버라이딩 해야된다. 여기서 토큰 정보가 새로 업데이트 되거나 추가되면 토큰 정보를 얻어올 수 있다. 이렇게 얻어온 정보는 firestore나 자체적으로 구현한 서버에 전송할 수 있다.

override fun onNewToken(token: String) {
    Log.d(TAG, "Refreshed token: $token")

    //토큰 정보를 백엔드 서버로 전송하기
    sendRegistrationToServer(token)
}

 

4. 메시지 수신

아래는 Firebase 기본 샘플 예시이다. 추가적으로 notification을 생성하는 함수가 구현되어있다. 메시지를 수신하면 onMessageReceived 함수가 호출되어 전달받은 데이터를 얻어올 수 있다.

package com.google.firebase.quickstart.fcm.kotlin

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.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.google.firebase.quickstart.fcm.R

class MyFirebaseMessagingService : FirebaseMessagingService() {

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        Log.d(TAG, "From: ${remoteMessage.from}")

        if (remoteMessage.data.isNotEmpty()) {
            Log.d(TAG, "Message data payload: ${remoteMessage.data}")

 
        }

    }

    override fun onNewToken(token: String) {
        Log.d(TAG, "Refreshed token: $token")
        sendRegistrationToServer(token)
    }

    
    private fun sendRegistrationToServer(token: String?) {
        // TODO: Implement this method to send token to your app server.
        Log.d(TAG, "sendRegistrationTokenToServer($token)")
    }

    /**
     * Create and show a simple notification containing the received FCM message.
     *
     * @param messageBody FCM message body received.
     */
    private fun sendNotification(messageBody: String) {
        val intent = Intent(this, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        val pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
                PendingIntent.FLAG_ONE_SHOT)

        val channelId = getString(R.string.default_notification_channel_id)
        val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
        val notificationBuilder = NotificationCompat.Builder(this, channelId)
                .setSmallIcon(R.drawable.ic_stat_ic_notification)
                .setContentTitle(getString(R.string.fcm_message))
                .setContentText(messageBody)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent)

        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        // Since android Oreo notification channel is needed.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(channelId,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT)
            notificationManager.createNotificationChannel(channel)
        }

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

    companion object {
        private const val TAG = "MyFirebaseMsgService"
    }
}