chacha's

โฐ Android FCM ํ‘ธ์‹œ ์•Œ๋ฆผ ๋ณธ๋ฌธ

Android/My Library

โฐ Android FCM ํ‘ธ์‹œ ์•Œ๋ฆผ

Cha_Cha 2021. 11. 8. 18:30

๋ชฉ์ฐจ

     

    ๐Ÿ“ฆ FCM ( Firebase Cloud Messaging )

     Firebase Console

    Firebase ํด๋ผ์šฐ๋“œ ๋ฉ”์‹œ์ง•(FCM)์€ ๋ฉ”์‹œ์ง€๋ฅผ ์•ˆ์ •์ ์œผ๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๋ฉ”์‹œ์ง• ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค. ์ง์ ‘ ๊ตฌํ˜„ํ•œ ์„œ๋ฒ„์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์•Œ๋ฆผ/๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์„œ๋ฒ„๊ฐ€ ์—ฐ๊ฒฐ์„ ๊ณ„์† ์œ ์ง€ํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ FCM์„ ์ด์šฉํ•˜๋ฉด, ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•˜๊ณ  ์žˆ์ง€ ์•Š์•„๋„ ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

     

    ๐ŸŽฏ Notification๊ณผ Data

     FCM ๋ฉ”์‹œ์ง€ ์ •๋ณด  
     Android FCM Data์™€ Notification - ํ•ด๋ฆฌ์˜ ์œ ๋ชฉ์ฝ”๋”ฉ

    ํ‘ธ์‹œ ์•Œ๋ฆผ์œผ๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” ๋ฉ”์‹œ์ง€๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด 2๊ฐ€์ง€ ์œ ํ˜•์œผ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค. Notification์€ ์•ฑ์ด ํฌ๊ทธ๋ผ์šด๋“œ์ผ ๋•Œ๋งŒ ํ‘ธ์‹œ ์•Œ๋ฆผ์ด ์˜ค๊ณ  Data๋Š” ์•ฑ์ด ํฌ๊ทธ๋ผ์šด๋“œ์— ์žˆ๋“  ๋ฐฑ๊ทธ๋ผ์šด๋“œ์— ์žˆ๋“  ์ƒ๊ด€์—†์ด ํ‘ธ์‹œ ์•Œ๋ฆผ์ด ์˜ต๋‹ˆ๋‹ค. ๋˜ํ•œ Notification๊ณผ Data๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

    ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
    Notification
    Data

     

    ๐Ÿ‘จ‍๐Ÿ‘จ‍๐Ÿ‘ง‍๐Ÿ‘ฆ ๋ˆ„๊ตฌ์—๊ฒŒ ๋ณด๋‚ผ ๊ฒƒ์ธ๊ฐ€? ( ํ‘ธ์‹œ ์•Œ๋ฆผ ๋Œ€์ƒ )

     Android์—์„œ ์ฃผ์ œ ๋ฉ”์‹œ์ง•(topic messagin)
     Android์˜ ๊ธฐ๊ธฐ ๊ทธ๋ฃน์— ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ

    1. ํŠน์ • ๋Œ€์ƒ 1๋ช…

    ํŠน์ • ๊ธฐ๊ธฐ์˜ Token ๊ฐ’์„ ์ด์šฉํ•˜์—ฌ ํ•ด๋‹น ๊ธฐ๊ธฐ์—๋งŒ ์•Œ๋ฆผ์„ ์ „์†กํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. 

    2. ์—ฌ๋Ÿฌ ๋ช… ( topic์„ ๊ตฌ๋…ํ•œ ์‚ฌ๋žŒ๋“ค )

    ํŠน์ • topic์„ ๊ตฌ๋…ํ•œ ์—ฌ๋Ÿฌ ๊ธฐ๊ธฐ์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋‚ ์”จ์™€ ๊ฐ™์ด ๊ณต๊ฐœ์ ์œผ๋กœ ์ œ๊ณต๋˜๋Š” ์ •๋ณด์— ์‚ฌ์šฉํ•˜๋ฉด ์ ํ•ฉํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ์ธ ์•ฑ์—์„œ ๊ธฐ์กด topic์„ ๊ตฌ๋…ํ•˜๊ฑฐ๋‚˜ ์ƒˆ topic์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Firebase ํ”„๋กœ์ ํŠธ์— ์•„์ง ์—†๋Š” ์ƒˆ topic์„ ๊ตฌ๋…ํ•˜๋ฉด FCM์—์„œ ์ด ์ด๋ฆ„์œผ๋กœ ์ƒˆ topic์ด ๋งŒ๋“ค์–ด์ง€๊ณ , ์ดํ›„์— ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ์—์„œ ๊ทธ topic์„ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Firebase Admin SDK๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„œ๋ฒ„ ์ธก์—์„œ ๊ธฐ๋ณธ์ ์ธ topic ๊ด€๋ฆฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋“ฑ๋ก ํ† ํฐ์„ ์•Œ๊ณ  ์žˆ์œผ๋ฉด ์„œ๋ฒ„ ๋กœ์ง์„ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ ์•ฑ ์ธ์Šคํ„ด์Šค๋ฅผ ์ผ๊ด„ ๊ตฌ๋…ํ•˜๊ฑฐ๋‚˜ ๊ตฌ๋… ์ทจ์†Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์†๋„๋ณด๋‹ค ์ฒ˜๋ฆฌ๋Ÿ‰์„ ์œ„์ฃผ๋กœ ์ตœ์ ํ™”๋˜์–ด ์žˆ๋Š” ๊ธฐ๋Šฅ์œผ๋กœ ๋น ๋ฅด๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†กํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ๊ถŒ๊ณ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

    3. ์—ฌ๋Ÿฌ ๋ช… ( ๊ธฐ๊ธฐ ๊ทธ๋ฃน์— ์†ํ•œ ์‚ฌ๋žŒ๋“ค )

    ๊ทธ๋ฃน์— ์†ํ•œ ๊ธฐ๊ธฐ์—๋งŒ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๊ธฐ๊ธฐ ๊ทธ๋ฃน ๋ฉ”์‹œ์ง•์€ ์•ฑ ๋‚ด์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์„œ๋ฒ„์—์„œ ๊ธฐ๊ธฐ ๊ทธ๋ฃน์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ธฐ๊ธฐ ๋ชจ๋ธ์— ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋ ค๋ฉด ์„œ๋ฒ„์—์„œ ์•Œ๋งž์€ ๊ทธ๋ฃน์— ๋“ฑ๋ก/์‚ญ์ œํ•˜์—ฌ ๊ฐ ๊ทธ๋ฃน์— ์ ์ ˆํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์•Œ๋ฆผํ‚ค ํ•˜๋‚˜ ๊ทธ๋ฃน์— ์ตœ๋Œ€ 20๋ช…๊นŒ์ง€๋งŒ ์†ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

     

     ๐Ÿš˜ FCM ์•Œ๋ฆผ์„ ์ „์†กํ•˜๋Š” ์ „์ฒด ๊ณผ์ •

    FCM ๋ฉ”์‹œ์ง€ ์ „์†ก ๊ณผ์ •

     

    ๐Ÿ“ฅ Android ์•ฑ์—์„œ ํ‘ธ์‹œ ์•Œ๋ฆผ ๋ฐ›๊ธฐ

    1. Firebase ๊ตฌ์„ฑ ํŒŒ์ผ ์ถ”๊ฐ€

     

    Android์—์„œ Firebase ํด๋ผ์šฐ๋“œ ๋ฉ”์‹œ์ง• ํด๋ผ์ด์–ธํŠธ ์•ฑ ์„ค์ •  |  Firebase Documentation

    Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register ์˜๊ฒฌ ๋ณด๋‚ด๊ธฐ Android์—์„œ Firebase ํด๋ผ์šฐ๋“œ ๋ฉ”์‹œ์ง• ํด๋ผ์ด์–ธํŠธ

    firebase.google.com

    2. FirebaseMessagingService๋ฅผ ์ƒ์†

    ์•Œ๋ฆผ์„ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๋Š” Service ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 2๊ฐœ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

    onNewToken()์€ FCM ์„œ๋ฒ„์— ์•ฑ์ด ๋“ฑ๋ก๋˜์—ˆ์„ ๋•Œ ํ˜ธ์ถœ๋˜๊ณ , ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ Token ๊ฐ’์ด ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ์ด Token ๊ฐ’์€ ๊ฐ๊ฐ์˜ ์•ฑ์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•œ ๊ณ ์œ ํ•œ ํ‚ค์ž…๋‹ˆ๋‹ค.

    onMessageReceived()๋Š” FCM ์„œ๋ฒ„์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜๋ฉด ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. 

    class ChordFirebaseMessagingService : FirebaseMessagingService() {
    
        override fun onNewToken(token: String) {
            Log.i("onNewToken", "Success save token")
            sendRegistrationToServer(token) // Token์„ ์„œ๋ฒ„๋กœ ์ „์†ก
        }
    
        // ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๋ฉ”์„œ๋“œ
        override fun onMessageReceived(remoteMessage: RemoteMessage) {
            if (remoteMessage.data.isNotEmpty()) {
                sendNotification(
                    remoteMessage.data["title"].toString(),
                    remoteMessage.data["body"].toString()
                )
            } else {
                remoteMessage.notification?.let {
                    sendNotification(
                        remoteMessage.notification!!.title.toString(),
                        remoteMessage.notification!!.body.toString()
                    )
                }
            }
        }
    
        // ์•Œ๋ฆผ์„ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ
        private fun sendNotification(title: String, body: String) {
            val notifyId = (System.currentTimeMillis() / 7).toInt()
    
            val intent = Intent(this, MainActivity::class.java)
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
            val pendingIntent =
                PendingIntent.getActivity(this, notifyId, intent, PendingIntent.FLAG_ONE_SHOT)
    
            val channelId = getString(R.string.firebase_notification_channel_id)
            val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
    
            val notificationBuilder = NotificationCompat.Builder(this, channelId)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(title)
                .setContentText(body)
                .setPriority(NotificationManagerCompat.IMPORTANCE_HIGH)
                .setAutoCancel(true)
                .setSound(soundUri)
                .setContentIntent(pendingIntent)
    
            val notificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val channel = NotificationChannel(
                    channelId,
                    channelId,
                    NotificationManager.IMPORTANCE_HIGH
                )
                notificationManager.createNotificationChannel(channel)
            }
    
            notificationManager.notify(notifyId, notificationBuilder.build())
        }
    }

    3. Manifest ํŒŒ์ผ ์ˆ˜์ •

    1) ์ธํ„ฐ๋„ท์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ธํ„ฐ๋„ท ํผ๋ฏธ์…˜์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. 

    2) ์„œ๋น„์Šค ํŒŒ์ผ์€ Manifest ํŒŒ์ผ์— ๋“ฑ๋กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ, ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•˜๊ธฐ ์œ„ํ•œ intent-filter๋ฅผ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค.

    <uses-permission android:name="android.permission.INTERNET"/>
            <service android:name=".network.ChordFirebaseMessagingService"
                android:exported="false">
                <intent-filter>
                    <action android:name="com.google.firebase.MESSAGING_EVENT" />
                </intent-filter>
            </service>

    4. Firebase Console์—์„œ Push ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ

    ์•Œ๋ฆผ ์ œ๋ชฉ๊ณผ ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ๋ฅผ ์ž…๋ ฅ ํ›„ [ํ…Œ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ์ „์†ก] ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์ค๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ…Œ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ์ „์†ก์„ ๋ˆŒ๋ €์„ ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ํ‘œ์‹œ๋œ๋‹ค๋ฉด FCM ํ† ํฐ์„ ์ž…๋ ฅํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

    Android์—์„œ ์–ป์€ FCM ํ† ํฐ ๊ฐ’ ์ž…๋ ฅํ•˜๊ธฐ

    • ํ…Œ์ŠคํŠธ ํ™”๋ฉด

    Push ํ…Œ์ŠคํŠธ

     

    ๐Ÿ“ค Node js ํ‘ธ์‹œ ์•Œ๋ฆผ ๋ณด๋‚ด๊ธฐ

     ๊ธฐ๊ธฐ ๋“ฑ๋ก ํ† ํฐ ์•ก์„ธ์Šค 
     ์•ฑ ์„œ๋ฒ„ ์ „์†ก ์š”์ฒญ ์ž‘์„ฑ
     FCM์„ ์ด์šฉํ•˜์—ฌ node js ํ‘ธ์‹œ ์•Œ๋žŒ ๊ตฌํ˜„ํ•˜๊ธฐ - Yusong ๋ธ”๋กœ๊ทธ

    1. ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ํ˜„์žฌ FCM ํ† ํฐ ๊ฐ’์„ ์•Œ์•„๋ƒ…๋‹ˆ๋‹ค. ( Android ํด๋ผ์ด์–ธํŠธ ์„ค์ • )

    FCM SDK๋Š” ์•ฑ์„ ์ฒ˜์Œ ์‹œ์ž‘ํ•  ๋•Œ ํด๋ผ์ด์–ธํŠธ ์•ฑ ์ธ์Šคํ„ด์Šค์šฉ ๋“ฑ๋ก ํ† ํฐ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ํ† ํฐ์€ ์•„๋ž˜์˜ ๊ฒฝ์šฐ์— ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • ์ƒˆ ๊ธฐ๊ธฐ์—์„œ ์•ฑ์„ ๋ณต์›
    • ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ ์‚ญ์ œ/์žฌ์„ค์น˜
    • ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐ
    FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
        if (!task.isSuccessful) {
            Log.w(TAG, "Fetching FCM registration token failed", task.exception)
            return@OnCompleteListener
        }
    
        // Get new FCM registration token
        val token = task.result
    
        // Log and toast
        val msg = getString(R.string.msg_token_fmt, token)
        Log.d(TAG, msg)
        Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
    })

    2. ํŠน์ • ๊ธฐ๊ธฐ (deviceToken์— ํ•ด๋‹นํ•˜๋Š” ๊ธฐ๊ธฐ)๋กœ ํ‘ธ์‹œ ์•Œ๋ฆผ์„ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

    /**
     * Push Alarm 
     **/
    const admin = require("firebase-admin"); // Firebase Admin SDK ์„ค์น˜
    let serviceAccount = require("./firebase_admin_key.json"); // ๋น„๊ณต๊ฐœ ์„œ๋ฒ„ํ‚ค ๋‹ค์šด๋กœ๋“œ ํ›„ ๋กœ๋“œ
    
    admin.initializeApp({
        credential: admin.credential.cert(serviceAccount)
    });
    
    // Push Test
    app.get('/pushComplete', function (req, res, next) {
        let deviceToken = "์•ฑ FCM ํ† ํฐ ๊ฐ’"
        let message = {
            notification: {
                title: "Test",
                body: "push test in server"
            },
            token: deviceToken
        }
    
        admin
            .messaging()
            .send(message)
            .then(function (response) {
                console.log('Successfully sent message: : ', response)
                return res.status(200).json({ success: true })
            })
            .catch(function (err) {
                console.log('Error Sending message!!! : ', err)
                return res.status(400).json({ success: false })
            })
    })
    • ํ…Œ์ŠคํŠธ ํ™”๋ฉด

    Server Push ํ…Œ์ŠคํŠธ

     

    ๐Ÿ“Œ ์ฐธ๊ณ 

    ๋‹น๊ทผ๋งˆ์ผ“์˜ ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๋งŽ์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์–ด๋–ป๊ฒŒ ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์œผ๋กœ ํ‘ธ์‹œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ์„œ์ˆ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

     

    ๋‹น๊ทผ๋งˆ์ผ“์˜ ํ‘ธ์‹œ์•Œ๋ฆผ์„ ์ง€ํƒฑํ•˜๊ณ  ์žˆ๋Š” Node.js ์„œ๋น„์Šค

    ํ‘ธ์‹œ์•Œ๋ฆผ์€ ๋‹น๊ทผ๋งˆ์ผ“ ์„œ๋น„์Šค์—์„œ ์ฑ„ํŒ…, ‘ํ‚ค์›Œ๋“œ ์•Œ๋ฆผ’, ‘๊ธˆ์ฃผ์˜ ์ธ๊ธฐ๋งค๋ฌผ’๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์— ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ดˆ๋‹น 1500 ์š”์ฒญ์„ ๋ˆ„๋ฝ ์—†์ด ์ง€์›ํ•˜๋Š” ํ‘ธ์‹œ ์„œ๋น„์Šค๋ฅผ Node.js, TypeScript๋กœ ๊ฐœ

    medium.com

     

    END

    Comments