본문 바로가기

Android

Firebase Cloud Messaging (FCM) - Android

FCM은?

무료로 메시지 전송할 수 있는 메시징 솔루션

  • 알림 메시지 또는 데이터 메시지 전송
  • 단일기기, 기기그룹, 특정 주제를 구독한 기기등 3가지 방식으로 메시지 배포 가능
  • 클라이언트 앱에서 채널을 통해 다시 서버로 메시지를 보낼 수 있다.

  1. 메시지 작성, 타겟킹 - gui 기반 콘솔에서 가능, sdk 등이용해서 서버환경에서 메시지 생성 가능
  2. 메시지 메타 데이터 생성
  3. 각다른 플렛폼별 기기로 메시지 라우팅, 필요한 경우 플랫폼별 처리 등
  4. 사용자 기기의 sdk 로 로직에 따라 메시지 처리
  • 일반적인 상용앱 플로우

앱에서 토큰 취득 → 앱서버로 토큰 전송 → 앱서버에서 FCM 토큰을 이용하여 메시지 전송 요청 → FCM에서 메세지 전송 → 앱에서 메시지 수신

메세지

메시지 유형 2가지

  • 알림메시지
    • FCM이 기기에 자동으로 메시지 표시
    • 앱이 백그라운드에있으면 알림보내고 끝 전처리가 힘들 수 있다 대신 구현쉬워
    • 앱이 백그라운드에 있으면 onMessageReceived 호출 하지 않음
    • 기본 예약 키모음, 커스텀 키-값 포함
// 예시
{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"Portugal vs. Denmark",
      "body":"great match!"
    }
  }
}

// 커스텀 데이터 포함된 알림 메시지
{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"Portugal vs. Denmark",
      "body":"great match!"
    },
    "data" : {
      "Nick" : "Mario",
      "Room" : "PortugalVSDenmark"
    }
  }
}
  • 알림 및 데이터 페이로드가 둘 다 포함된 메시지를 수신한 경우의 앱 동작은 앱이 백그라운드 상태인지 아니면 포그라운드 상태인지에 따라 다르다. 수신당시 앱이 활성 상태였는지 여부가 특히 영향을 준다
    • 백그라운드 상태인 경우 알림 페이로드가 앱의 알림 목록에 수신되며 사용자가 알림을 탭한 경우에만 앱이 데이터 페이로드를 처리합니다.
    • 포그라운드 상태인 경우 앱에서 페이로드가 둘 다 제공되는 메시지 객체를 수신합니다.
  • 데이터 메시지
    • 클라이언트 앱에서 데이터메시지 처리
    • 앱이 백그라운드던 포그라운드던 상관없이 앱에서 자체처리 → 구현어협지만 , 전처리 가능 장점
    • 앱이 백그라운드던 포그라운드던 onMessageReceived 호출 함
    • 예약키 없이 커스텀만 있다.
// 예시
{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data":{
      "Nick" : "Mario",
      "body" : "great match!",
      "Room" : "PortugalVSDenmark"
    }
  }
}

참고 - 여러 플래폼별 메시지 맞춤 가능

  • 모든 플랫폼 상관없이 사용가능한 공통 필드가 있고
  • 플랫폼별 필드가 있다. (특젇 플랫폼에만 필드 전송해야하는 경우 등)

메시지 우선순위 설정

오래오(8) 이하는 메시지에 따로 우선순위 설정 8 이상은 메시지 채널이 필수이고, 채널에 우선순위 설정

  • 보통 우선순위
    • default 우선순위
    • 앱이 포그라운드 상태면 즉시전송
    • 기기가 잠자기 상태면 배터리 절약 위해 전송지연될 수 있음
    • 새로운 이메일 알림, UI동기화 유지, 백그라운드 앱 데이터 동기화 같이 시간이 중요하지 않은메시지 인경우 사용
    • WorkManager에 네트워크 연결시 처리하도록 작업 예약도 가능
  • 높은 우선순위
    • 높은 우선순위 메시지인경우 즉시전송 시도 ,
    • 필요시 FCM에서 기기의 절전모드를 해제하고, 앱서버로 연결되는 네트워크 열수 있다.
    • 메신저, 채팅, 음성통화 알림기능 즉각적인 상호작용이 필요한경우
    • 배터리더 많이 소모됨
    "priority" : "normal",
    

메시지 수명설정

보통 메시지 보내면 바로전송인데 메세지 지연 가능

메시지 최대수명 지정가능 (최대 4주)

방해금지 모드

방해 금지 모드에서는 다음 세 가지 수준을 사용할 수 있습니다.

  • 모두 차단: 알람, 음악, 동영상, 게임의 소리를 비롯해 모든 알림음과 진동을 차단합니다.
  • 알람만: 알람을 제외한 모든 알림음과 진동을 차단합니다.
  • 우선순위만: 사용자가 시스템 수준에서 허용할 카테고리를 구성할 수 있습니다(예: 알람, 알림, 일정, 전화, 메시지만 허용 등). 메시지와 전화의 경우 발신자에 따라 필터링할 수도 있습니다(그림 13).

Android 8.0(API 수준 26) 이상에서는 사용자가 채널별로 방해 금지 모드를 재정의하여 앱별 카테고리(채널이라고도 함)에 알림을 추가로 허용합니다.

결제 앱에서는 출금 및 입금과 관련된 알림 채널을 보유할 수도 있습니다. 사용자는 우선순위 모드에서 출금 알림, 입금 알림 또는 둘 다 허용하도록 선택할 수 있습니다.

→ 방해금지모드에서는 메시지 채널이나 메시지 우선순위가 높음이여도 사용자가 추가 허용을 해두지 않으면 알림이 오지 않는다 해제하는순간 다시온다!


 

개발하기

1. Android 프로젝트에 Firebase 추가하기

https://firebase.google.com/docs/android/setup?hl=ko

 

Android 프로젝트에 Firebase 추가  |  Firebase Documentation

의견 보내기 Android 프로젝트에 Firebase 추가 기본 요건 Android 프로젝트가 준비되지 않았다면 빠른 시작 샘플 중 하나를 다운로드하여 Firebase 제품을 사용해 볼 수 있습니다. 다음 옵션 중 하나를

firebase.google.com

- Firebase Console 에서 프로젝트 추가l

또 하라는대로 한다 친절하게 잘해놨다.

Json 파일넣고, sdk build.gradle에 추가

2. 매니페스트 수정

  • FirebaseMessagingService 상속하여 구현한 클래스를 서비스로 등록
<!-- FirebaseMessagingService 상속하여 구현한 class 등록-->
        <service
            android:name=".MyFirebaseMessagindService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
  • 알림 아이콘 색상등 메타데이터 추가 가능 (선택)

3. 토큰 엑세스

현재 토큰 가져오기

private fun getFCMToken() {
        FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
            if (!task.isSuccessful) {
                return@OnCompleteListener
            }
            // Get new FCM registration token
            tokenTextView.text = task.result
        })
    }

새토큰 발급시

  • 토큰이 변경되는 경우는
    • 새기기에서 앱복원
    • 사용자가 앱 삭제 재설치
    • 사용자가 앱 데이터 날릴때
class MyFirebaseMessagindService: FirebaseMessagingService() {
    override fun onNewToken(p0: String) {
        super.onNewToken(p0)
        // [토큰 액세스]
        // 새로운 토큰이 발급되면 해당 함수가 호출됨
        // 앱서버로 토큰 전송
    }
}

 

📍 토큰 받았으면 원래는 토큰을 가지고 서버가 메세지 요청을 해야하는데 지금 테스트 할때 없으니 임의로 토큰을 가지고 웹 콘솔에서 알림 메세지를 날려보겠당

https://console.firebase.google.com/project/fcmstudy-2198f/notification?hl=ko

잘간다..!

 

4. FirebaseMessagingService 구현하기

  • 채널 생성
private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val channel = NotificationChannel(
                CHNNEL_ID,
                CHNNEL_NAME,
                NotificationManager.IMPORTANCE_DEFAULT
            )
            channel.description = CHNNEL_DES
            (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
                .createNotificationChannel(channel)
        }
    }
  • 메시지 생성
override fun onMessageReceived(message: RemoteMessage) {
        super.onMessageReceived(message)
        createNotificationChannel()

        // 메시지 생성
        val notificationBuild = NotificationCompat.Builder(this, CHNNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle(message.data["title"])
            .setContentText(message.data["message"])
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)

        // 메시지 보내기
        NotificationManagerCompat.from(this)
            .notify(1, notificationBuild.build())

    }
  • full code
class MyFirebaseMessagindService: FirebaseMessagingService() {
    override fun onNewToken(p0: String) {
        super.onNewToken(p0)
        // [토큰 액세스]
        // 새로운 토큰이 발급되면 해당 함수가 호출됨
        // 앱서버로 토큰 전송
    }

    override fun onMessageReceived(message: RemoteMessage) {
        super.onMessageReceived(message)
        createNotificationChannel()

        // 메시지 생
        val notificationBuild = NotificationCompat.Builder(this, CHNNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle(message.data["title"])
            .setContentText(message.data["message"])
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)

        // 메시지 보내기
        NotificationManagerCompat.from(this)
            .notify(1, notificationBuild.build())

    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val channel = NotificationChannel(
                CHNNEL_ID,
                CHNNEL_NAME,
                NotificationManager.IMPORTANCE_DEFAULT
            )
            channel.description = CHNNEL_DES
            (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
                .createNotificationChannel(channel)
        }
    }

    companion object {
        private const val CHNNEL_ID = "FCMStudyChennel"
        private const val CHNNEL_NAME = "Transfer Info"
        private const val CHNNEL_DES = "이체 정보를 알려줘"
    }
}

 

5. 실제 앱서버에서 메시지 보내는것과 같이 테스트해보기

https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send?hl=ko-

  • parent : projects/{google-services.json의 project_id}
  • request body : 보낼 데이터들 정의 보내야 하는 포맷은 문서 참고

  • EXECUTE 누르면 FirebaseMessageingService의 onMessageReceived로 메시지 들어온다.

- 채널 생성이 잘됐고 (Transfer Info) 메시지도 포그라운드, 백그라운드 상관없이 잘온다.

 

 

끝~!~!