본문 바로가기
앱 제작/키워드 알림 앱

[Kotlin] FCM 푸시 알림 구현하기

by Kim Juhwan 2020. 12. 31.

1. 기본개념
   1-1. Firebase
   1-2. FCM(Firebase Cloud Messaging)
   1-3. Notification과 Data
   1-4. 푸시 알림 전송 대상
2. Firebase 연동
   2-1. Firebase와 app 연결
   2-2. FCM 추가
   2-3. 메시지 처리
3. 메시지 송수신
   3-1. 테스트 메시지 수신
   3-2. 다른 스마트폰에서 송신 (작성 예정)
   3-3. 다른 프로그램에서 송신

 

 


 

1. 기본개념

1-1. Firebase

Firebase란 구글에서 인수한 'Firebase'에서 만든 개발 플랫폼이다.

푸시 알림을 보내거나, 데이터를 서버에 저장하거나, 계정을 이용해 로그인하는 등의 작업을

아주 쉽고 편리하게 사용할 수 있게 도와주는 녀석이다.

안드로이드뿐만 아니라 IOS도 사용이 가능하니 사용 방법을 익혀두면 아주 좋다.

무료로 사용 가능하지만 일정량 이상 사용하기 위해서는 요금제에 가입해야 한다.

 

 

1-2. FCM(Firebase Cloud Messaging)

파이어 베이스에서는 개발자를 위한 여러 가지 기능을 제공하는데

그중 푸시 알림을 위한 기능이 바로 FCM이다.

예전에는 GCM(Google Cloud Messaging)이라는 이름을 사용했었는데

파이어 베이스를 구글이 꿀꺽하면서 이름이 바뀌었다.

 

 

1-3. Notification과 Data

Notification
Data

 

푸시 알림으로 보낼 수 있는 메시지는 위와 같이 2가지 유형이 있다.

Notification은 앱이 포그라운드일 때 (앱이 실행 중일 때)만 푸시 알림이 오고

Data는 포그라운드/백그라운드 상관없이 푸시알림이 도착한다.

(Notification과 Data를 같이 쓰는 경우도 있지만 논외로 하겠다)

 

이 말만 들어보면 Data를 쓰지 않을 이유가 없을 것 같다.

실제로 어느 블로그를 보니 회사에서도 이유 불문하고 Data를 쓴다는 글을 보았는데

이유도 없이 Notification이 있을 것 같진 않다.

검색해도 마땅히 이유에 대해 다룬 글을 찾지 못해서 멘토님에게 여쭤볼 예정이다.

 

메시지 정보에 대한 자세한 내용은 아래 공식문서에서 확인할 수 있다.

 

FCM 메시지 정보  |  Firebase

Firebase 클라우드 메시징(FCM)은 다양한 메시징 옵션과 기능을 제공합니다. 이 페이지의 정보는 다양한 유형의 FCM 메시지에 관한 이해를 돕고 FCM으로 구현할 수 있는 기능을 소개하기 위한 내용입

firebase.google.com

 

 

1-4. 푸시알림 전송 대상

  • 1명
  • 여러 명 (그룹에 속한 사람들)
  • 여러 명 (구독한 사람들)

 

첫 번째는 1명에게 알림을 보내는 경우이다.

카카오톡으로 메시지를 주고받는 것이 이에 해당한다.

이 경우 상대방의 token값을 알아야 메시지를 보낼 수 있다.

token값은 앱을 설치하면 생성되는 난수이다. 그 기기에만 존재하는 수이기 때문에 ID와 같은 역할을 한다.

 

두 번째는 그룹에 속한 사람들에게 알림을 보내는 경우이다.

사용자들을 그룹에 가입시켜서 메시지를 보내는 방법을 사용한다.

20명까지 그룹에 속할 수 있다는 단점이 있다.

 

세 번째는 구독자들에게 알림을 보내는 경우이다.

'news'라는 주제를 구독한 사람들이 있으면 그 사람들에게 몽땅 알림을 보낸다.

1000명까지 가능하며 속도보다 처리량을 위주로 처리되는 기능이라

공식문서에서는 빠르고 안전하게 전송하고 싶다면 다른 방법을 사용하라고 적혀있다.

(이 내용은 아래 링크에서 확인할 수 있다)

 

 

Android에서 주제 메시징  |  Firebase

FCM 주제 메시징은 게시/구독 모델을 기반으로 특정 주제를 구독하는 여러 기기에 메시지를 보내 줍니다. 필요에 따라 주제 메시지를 작성하면 FCM에서 라우팅을 처리하여 올바른 기기에 정확히

firebase.google.com

 

그래서인지 당근마켓 앱에서 안정성과 속도를 높이려고 개발을 많이 하고 있는 것 같았다.

FCM에 대해 공부하다가 읽어봤는데 흥미로워서 링크를 달았다. 

이 내용은 아래 당근마켓 기술 블로그에서 확인할 수 있다.

 

당근마켓의 푸시알림을 지탱하고 있는 Node.js 서비스

푸시알림은 당근마켓 서비스에서 채팅, ‘키워드 알림’, ‘금주의 인기매물’과 같은 여러 기능에 사용되고 있습니다. 초당 1500 요청을 누락 없이 지원하는 푸시 서비스를 Node.js, TypeScript로 개

medium.com

 

 

 

 

2. Firebase 연동

2-1. Firebase와 app 연결

안드로이드 스튜디오 - Tools - Firebase

 

위 사진처럼 경로를 따라가면 파이어 베이스에서 제공하는 여러 기능들이 있다.

라떼는 말야~ 어? 파이어베이스 사용하려면~ 복잡했는데 말야~

세상이 좋아져서 이제 그냥 하라는 대로 클릭하면서 넘어가면 알아서 다 해준다.

 

 

 

Cloud Messaging - Set up Firebase Cloud Messaging

 

이 중 Cloud Messaging을 찾아서 클릭하고 파란 글씨를 클릭해준다.

 

 

 

순서대로 나와있는 사용 방법

 

그러면 이렇게 친절하게 해야 하는 순서가 번호로 매겨져 있고

심지어 버튼을 누르면 gradle에 추가하고 하는 작업들을 자동으로 완성해준다.

우선 1번의 Connect to Firebase를 눌러주자.

 

 

구글 계정 로그인 필요

 

그러면 파이어 베이스와 앱을 연결하기 위해 구글 로그인 창으로 넘어간다. 

계정 선택하고 다시 안드로이드 스튜디오로 돌아오면 된다.

 

 

 

이런 창이 떠있을 텐데, 현재 앱의 프로젝트 이름이 맞는지 확인하고

Connect to Firebase를 눌러주면 된다.

 

 

1단계 완료

 

Connected가 뜨면 1단계 완료!

 

 

2-2. FCM 추가

Add FCM to your app을 누르면 자동으로 gradle이 추가된다.

시간이 약간 걸리므로 Connected 뜰 때까지 좀 기다리면 된다.

2단계 완료!

 

 

2-3. 메시지 처리

AndroidManifest.xml

 

3단계에서는 친절하게 어떤 클래스를 만들어야 하고

매니페스트에 어떤 것을 선언해야 하는지 소스가 다 적혀있다.

그래서 복사 붙여 넣기만 하면 된다.

단, 자바 기준으로 적혀있으므로 코틀린에 맞게 살짝 바꿔야 한다.

 

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

이름 부분에 있는 '.java'만 지운 것이다.

뒤에 적혀있는 MyFirebase... 이 부분은 클래스 이름이므로 원하는 걸로 바꾸어도 상관없다.

 

 

class MyFirebaseMessagingService : FirebaseMessagingService() {

    private val TAG = "FirebaseService"

    // FirebaseInstanceIdService는 이제 사라짐. 이제 이걸 사용함
    override fun onNewToken(token: String?) {
        Log.d(TAG, "new Token: $token")

        // 토큰 값을 따로 저장해둔다.
        val pref = this.getSharedPreferences("token", Context.MODE_PRIVATE)
        val editor = pref.edit()
        editor.putString("token", token).apply()
        editor.commit()

        Log.i("로그: ", "성공적으로 토큰을 저장함")
    }

    override fun onMessageReceived(remoteMessage: RemoteMessage?) {
        Log.d(TAG, "From: " + remoteMessage!!.from)

        // Notification 메시지를 수신할 경우는
        // remoteMessage.notification?.body!! 여기에 내용이 저장되어있다.
        // Log.d(TAG, "Notification Message Body: " + remoteMessage.notification?.body!!)

        if(remoteMessage.data.isNotEmpty()){
            Log.i("바디: ", remoteMessage.data["body"].toString())
            Log.i("타이틀: ", remoteMessage.data["title"].toString())
            sendNotification(remoteMessage)
        }

        else {
            Log.i("수신에러: ", "data가 비어있습니다. 메시지를 수신하지 못했습니다.")
            Log.i("data값: ", remoteMessage.data.toString())
        }
    }

    private fun sendNotification(remoteMessage: RemoteMessage) {
        // RequestCode, Id를 고유값으로 지정하여 알림이 개별 표시되도록 함
        val uniId: Int = (System.currentTimeMillis() / 7).toInt()

        // 일회용 PendingIntent
        // PendingIntent : Intent 의 실행 권한을 외부의 어플리케이션에게 위임한다.
        val intent = Intent(this, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) // Activity Stack 을 경로만 남긴다. A-B-C-D-B => A-B
        val pendingIntent = PendingIntent.getActivity(this, uniId, intent, PendingIntent.FLAG_ONE_SHOT)

        // 알림 채널 이름
        val channelId = getString(R.string.firebase_notification_channel_id)

        // 알림 소리
        val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)

        // 알림에 대한 UI 정보와 작업을 지정한다.
        val notificationBuilder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.mipmap.ic_launcher) // 아이콘 설정
            .setContentTitle(remoteMessage.data["body"].toString()) // 제목
            .setContentText(remoteMessage.data["title"].toString()) // 메시지 내용
            .setAutoCancel(true)
            .setSound(soundUri) // 알림 소리
            .setContentIntent(pendingIntent) // 알림 실행 시 Intent

        val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        // 오레오 버전 이후에는 채널이 필요하다.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(channelId, "Notice", NotificationManager.IMPORTANCE_DEFAULT)
            notificationManager.createNotificationChannel(channel)
        }

        // 알림 생성
        notificationManager.notify(uniId, notificationBuilder.build())
    }
}

(3단계 4단계 소스를 코틀린 버전으로 합쳤다)

 

 

클래스 파일도 설명서에는 자바 기준으로 작성되어 있다.

물론 복사 붙여 넣기 하면 자동변환해주긴 하지만..

위는 코틀린 버전 소스이다.

 

onNewToken토큰을 생성하는 메서드이다.

17.0.0 버전 이전에는 무슨 getInstance? refreshToken? 이런 메서드가 있어서

생성하고 토큰 업데이트하고 하는 로직을 작성해야 했던 것 같다.

근데 이제는 이 메서드를 쓰면 알아서 내부에서 관리해주도록 바뀐 모양이다.

현재 기기의 토큰 값을 가져오는 메서드가 분명히 있을 것 같은데

찾다가 화딱지 나서 그냥 토큰이 생성될 때 따로 저장을 하도록 하였다.

 

onMessageReceived메시지를 수신하는 메서드이다.

메시지에 제목이나 내용이 들어있는지 검사하고

문제가 없다면 sendNotification을 호출한다.

 

sendNotification알림을 생성하는 메서드이다.

아이콘은 어떻게 할 건지, 알림 소리는 어떻게 할건지 등

이런 세세한 옵션을 설정하고 알림을 생성하면 비로소 푸시 알림이 뜨게 되는 것이다.

 

 

 

 

3. 메시지 송수신

3-1. 테스트 메시지 수신

https://console.firebase.google.com/u/0/?hl=ko

 

이제 FCM 연동이 잘 되었는지 확인하기 위해 파이어 베이스 콘솔 홈페이지로 접속한다.

이 중 자신이 생성한 프로젝트를 클릭한다.

 

 

 

Cloud Messaging - Send your firest message

 

위 메뉴를 찾아서 메시지 전송 버튼을 클릭해준다.

UI 업데이트가 잦아서 메뉴가 이리저리 도망가기 때문에 이리저리 뒤져봐야 한다..

2020-12-31 기준으로 좌측 메뉴 중 '참여' 탭을 누르면 있다.

 

 

 

푸시알림 내용 설정

 

알림 제목과 알림 텍스트를 적고 다음을 눌러준다.

 

 

 

 

 

 

앱 선택을 눌러서 앱을 선택하고 다음을 눌러준다.

그러면 푸시 알림이 스마트폰에 도착해있는 것을 확인할 수 있다.

 

 

 

3-2. 다른 스마트폰에서 송신 (작성 예정)

카카오톡으로 대화할 때 푸시알림이 오듯이

앱을 설치한 사용자끼리 푸시 알림을 주고받는 것이 가능하다.

이 부분에 대해서는 추후에 작성할 예정이다.

 

 

 

3-3. 다른 프로그램에서 송신

꼭 앱에서 앱으로만 메시지를 보낼 수 있는 게 아니라

서버에서 앱으로 메시지를 보내는 것도 가능하다.

마치 배달의 민족 앱에서 쿠폰을 뿌릴 때 사용자 전체에게 푸시 알림을 보내는 것처럼 말이다.

 

아래 게시물에 자세히 작성해두었다.

 

[파이썬] FCM을 통해 앱으로 푸시알림 보내기

1. 준비물  1-1. pyfcm 설치  1-2. Server Key  1-3. Token 2. 푸시 알림 보내는 방법  1-1. 1명에게 보내기  1-2. 여러 명에게 보내기 1. 준비물 1-1. pyfcm 설치 커맨드에서 "pip install pyfcm"을 입력..

todaycode.tistory.com

 

 

 

 

 


Android Studio Version : 3.6.1
Firebase Messaging : 17.3.4

 

반응형

댓글