[안드로이드&&Firebase] Node.js 웹 서버를 사용해 FCM 푸시알람 전송 방법(5)

2020. 11. 9. 23:13개발/[Kotlin] 안드로이드 개발

반응형

안녕하세요.

전 시간에 서버를 만들어서 수동으로 fcm을 보내봤습니다.

 

오늘은 바로바로 !! 

준비물 : 노드.js다운, VS코드(있으면 좋아요)

 

이번 실습은 로컬에서의 작동을 확인하였습니다. (시스템 배포를 하지 않은 같은 와이파이 에서 작용합니다.)  

 

Notification에 대해 알아보도록 하겠습니다.

FCM Notification

 'notification': {
            'body': '',
            'title': '',
        }

Notification Message는 Notification만 보내는 메시지 입니다. 가장 높은 우선순위를 가지면 Killed 상태, background 상태, foreground 상태 때 시스템 트레이에 표시 됩니다.하지만 foreground 상태에서는 시스템트레이에 남지않습니다.그 이유는 앱 단의 onMessageReceived를 통해 데이터가 처리되기 때문입니다.앱 포그라운드에서 알림을 띄울 경우에는 head-up 속성을 만들어줘야 합니다.자 그러면 vs code의 소스와 안드로이드 소스를 보시면서 설명하겠습니다.

웹 서버 소스

const firebase = require('firebase');
// 파이어베이스 config 정보 
var firebaseConfig = {
  apiKey: "your api key",
  authDomain: "your auth domain",
  databaseURL: "your database url",
  projectId: "your project id",
  storageBucket: "your storage bucket",
  messagingSenderId: "your messaging sender id",
  appId: "your app id",
  measurementId: "your measurement id"
};
// 파이어베이스 정보 등록
firebase.initializeApp(firebaseConfig);
const request = require('request');

// 무슨 데이터를 줄지 토큰은 무엇인지
const options = {
    uri:'https://fcm.googleapis.com/fcm/send', 
    method: 'POST',
    headers: {
        "content-type": "application/json",
        "Authorization": "key = your authorization key"
    },
    json: {
        'to': "your device token",
        'notification': {
            'body': '',
            'title': '',
        }
    }
  }

// 파베 접근해서 token을 받아온다.
var key=[];
var token=[];

// 파베 접근해서 token을 받아온다.
firebase.database().ref("Token").on('value', (snapshot)=>{
  val = snapshot.val();
  console.log(val);
  
  //키값들을 받는다.
  key = Object.keys(val);
  
  // 토큰값을 받는다.
  token = Object.values(val);
  
  console.log(key);
  console.log(token);
})


User = {
  id:"cerberusTable",
  door: 0,
  door_open: 0,
  danger: 0,
  alarm:0
};

// firebase를 통해서 값 실시간 모니터링
firebase.database().ref(User.id+"/door").on('value', (snapshot)=>{
      val = snapshot.val();
      if(val == 1){//door opened
        //서버에 알려주기
        User.door = val;
        console.log("door opend!!")
        options.json.notification.title = "문열림 알림"
        options.json.notification.body = "현관문이 열렸습니다."
      }else{
        User.door = val;
        console.log("door closed");
        options.json.notification.body = "현관문이 닫혔습니다."
        options.json.notification.title = "문닫힘 알림"
      }
      token.forEach(function(element){
        options.json.to = element;
        request.post(options, function(err,httpResponse,body){ /* ... */ });
      })
})
firebase.database().ref(User.id+"/danger").on('value', (snapshot)=>{
    val = snapshot.val();
    if(val == 1){//danger!!
      User.danger = val;
      console.log("Danger!!!!!!")
      options.json.notification.title = "위험수준 : 높음"
      options.json.notification.body = "신발을 벗지 않은 내부에 침입자가 있습니다."
      token.forEach(function(element){
        options.json.to = element;
        request.post(options, function(err,httpResponse,body){ /* ... */ });
      })
    }
    else{
      User.danger = val;
      console.log("normal")
    }
})
firebase.database().ref(User.id+"/alarm").on('value', (snapshot)=>{
    val = snapshot.val();
    if(val == 1){//danger!!
      User.alarm = val;
      console.log("alarm on!!!!!!");

    }
    else{
      User.alarm = val;
      console.log("alarm off")

    }
})

* 서버 배포를 하지않아 로컬에서 테스트를 해서 모든 토큰이 최초 접속해 받아지면 firebase token 테이블을 만들어서insert하여 모든 휴대폰에 알람이 가게 구현하였습니다.* 추가로 Firebase의 값을 구독하다가 원하는 값으로 바뀔 때 푸시알람을 보내도록 하였습니다. (IOT쪽에서 값 바뀜)* 만약 값이 바뀌면 options.json.notification.title or body 로 원하는 푸시메시지를 작성하였고 저장되어 있는 휴대폰 토큰에 다 전송하게끔 하였습니다.(token.forEach 부분)주석을 달아놔서 설명은 생략하도록 할께요 ! 

안드로이드 소스

package com.cookandroid.invasion.fcm

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.provider.Settings
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.cookandroid.invasion.MainActivity
import com.cookandroid.invasion.R
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage

class MyFirebaseMessagingService : FirebaseMessagingService() {

    private val TAG = "FirebaseService"
    private lateinit var database: FirebaseDatabase
    private lateinit var databaseReference: DatabaseReference

    // 파이어베이스 서비스의 토큰을 가져온다
    override fun onNewToken(token: String?) {
        Log.d(TAG, "new Token: $token")

        //파베 연결해서 토큰 날려줘야함. 단 한번만 날린다.
        database = FirebaseDatabase.getInstance()
        databaseReference = database.getReference("Token")
        val idByANDROID: String = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID)
        databaseReference.child(idByANDROID).setValue(token);
    }

    // 새로운 FCM 메시지가 있을 때 메세지를 받는다
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        // 앱이 포어그라운드 상태에서 Notificiation을 받는 경우
        if(remoteMessage.notification != null) {
            sendNotification(remoteMessage.notification?.body, remoteMessage.notification?.title)
        }
        else {
            sendNotification(remoteMessage.notification?.body, remoteMessage.notification?.title)
        }
    }

    // FCM 메시지를 보내는 메시지
    private fun sendNotification(body: String?, title: String?) {
        val intent = Intent(this, MainActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
            putExtra("Notification", body)
            putExtra("Notification",title)
        }

        var pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
        val notificationSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)

        // head up 알림 생성하기
        val notificationId = 1001
        createNotificationChannel(this, NotificationManagerCompat.IMPORTANCE_HIGH, false,
            getString(R.string.app_name), "App notification channel") 

        val channelId = "$packageName-${getString(R.string.app_name)}"

        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        val fullScreenPendingIntent = PendingIntent.getActivity(baseContext, 0,
            intent, PendingIntent.FLAG_UPDATE_CURRENT)  

        // 푸시알람 부가설정
        var notificationBuilder = NotificationCompat.Builder(this,channelId)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle(title)
            .setContentText(body)
            .setAutoCancel(true)
            .setSound(notificationSound)
            .setContentIntent(pendingIntent)
            .setFullScreenIntent(fullScreenPendingIntent, true)
            .setTimeoutAfter(1500)

        notificationBuilder.priority = NotificationCompat.PRIORITY_HIGH
        var notificationManager: NotificationManager = this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(notificationId, notificationBuilder.build())
    }

    // NotificationChannel 만드는 메서드
    private fun createNotificationChannel(context: Context, importance: Int, showBadge: Boolean,
                                          name: String, description: String) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channelId = "${context.packageName}-$name"
            val channel = NotificationChannel(channelId, name, importance)
            channel.description = description
            channel.setShowBadge(showBadge)

            val notificationManager = context.getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(channel)
        }
    }
}

1. 토큰을 얻어와서 최초 접속 하는 휴대폰의 토큰을 파이어베이스의 realtime database에 추가한다.

2. 웹 서버로부터 notifiation을 받으면 sendNotification 메서드를 실행한다. (killed : 시스템 트레이, background : 시스템 트레이, foreground :head up알람)

 

3. 시스템 버전을 비교해서 NotificationChannel을 만드는 메서드를 실행한다.

 

4. FCM 메시지를 보내는 메서드 foreground일 때 이에 해당하며 백그라운드나 killed상태일 때는 onmessageReceived를 거치지 않고 바로 기계에 푸시를 날려주기 때문에 head-up이 뜨지 않는다.

5. intent를 통해 vs code의 body와 title을 가져와 적용 시켜준다.

6. 노티는 가장 높은 우선순위로 줘야한다. 

7. pendingintent를 사용해 다른 앱을 사용 중이라도 앱을 띄울 수 있게한다.

8. 채널은 꼭 맞춰줘야한다.

9. fullscreenpendingintent를 사용한다.

10. notificationBuilder의 기본속성들을 사용한다.

 

+) 추가로 manifests 추가할 것 들 !

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>

<application 
  <service
            android:name=".fcm.MyFirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>
</application>
application 사이에 추가해준다.
       
      

이렇게 하면 최종적으로 값이 바뀜에 따라 푸시알람이 오는 것을 확인할 수 있다.

 

시연 동영상

이렇게 DB의 값을 subscribe해 값에 따라 fcm을 보내는 방법을 알아보았습니다 ! 

글을 잘 쓰는 편이 아니라서 왔다리갔다 한 점 죄송합니다 ㅠㅠ 더 추가할 점도 많고 부족하지만 

앞으로 더 자세하게 정확하게 공부해서 포스팅 해볼께요 !

감사합니다 !

 

+) 이 포스팅은 학부생이 공부하여 작성한 것으로 정확하지 않을 수 있습니다.

반응형