본문 바로가기

다우 & Android

다수의 Notification channel 관리시 주의사항

안녕하세요!
이번에는 개발 중인 앱에서 다수의 Notificaion channel을 생성하여 관리할 때 겪었던 이슈와 원인, 해결 방법을 공유해보려 합니다.

Notificaion Channel이란?

우선 안드로이드 O부터 나타난 Notificaion Channel이란 게 무엇인지 간단하게 알아보겠습니다.

Clock앱의 환경 설정에 들어가면 알림에 대한 설정이 가능합니다. 이 앱의 경우에는 여러 가지 상황에서 Notification을 주고 있네요. 알람&타이머에 대한 Notification, 놓친 알람에 대한 Notification, 스톱워치 관련 Notification 등등 여러 가지 분류의 Notification이 존재합니다.
이 상황에서 사용자는, 자신이 원하는 분류의 알림만 받고 나머지 알림은 받지 않도록 설정할 수 있습니다. Firing alarm & timers, Missed alarms 항목에만 체크를 하면, 그 이외의 나머지 알림은 받지 않습니다.

위의 그림처럼 사용자가 직접 자신이 원하는 항목의 알림만 수신할 수 있게 해주는 것이 Channel입니다. 개발자가 코드로 Firing alarm & timers, Missed alarms, Stopwatch 등등의 채널을 만들어주면 환경설정에서 이미지와 같이 채널 목록이 뜨게됩니다.

Stopwatch라는 채널을 만드는 코드는 아래와 같습니다(여러 개를 만들려면 단순히 channel id를 다르게 하여 동일하게 수행하면 됩니다).

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        // 채널 만들기
        val displayName = "Stopwatch"
        val descriptionText = "This is Stopwatch channel"
        val importance = NotificationManager.IMPORTANCE_DEFAULT
        val stopwatchChannel = NotificationChannel("stopwatch_channel_id", displayName, importance)
        stopwatchChannel.description = descriptionText
        // 시스템에 채널 등록하기.
        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(stopwatchChannel)
    }

구글 문서에 따르면, 채널은 같은 channel id로 중복 생성할 경우 아무 일도 일어나지 않으므로 앱이 실행될 때 딱 한 번만 생성하기를 권장하고 있습니다.

### notification을 특정 channel에 매칭 시키기
notification 채널이 만들어졌으므로 notifiation을 날릴 때마다 원하는 channel에 매칭 시켜 주어야 합니다. stopwatch 관련 notification을 보내려면 꼭 stopwatch chaanel에 매칭을 해주어야만 사용자가 의도한 대로 동작할 것이기 때문입니다.
notification을 특정 channel에 매칭시켜 보내는 코드는 아래와 같습니다.

private fun sendNotification() {
	val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    val title = "스탑워치 알림!"
    val message = "시간이 다 되었어요."
    
    val notificationBuilder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    	NotificationCompat.Builder(context, "stopwatch_channel_id") // 여기에 채널 ID를 적어주어야 매칭됨
	} else {
    	NotificationCompat.Builder(context, "")
    }
    
    val notification = notificationBuilder.apply {
    	setContentTitle(title)
        setContentText(message)
        setAutoCancel(true)
    }.build()
    
    notificationManager.notify(9999, notification)
}

안드로이드 버전이 O 이상이라면, Builder에 채널 id를 적어 주어야 합니다. 위 notification은 지정한 id로 매칭 되므로 사용자가 stopwatch 채널을 비활성화시켰다면 해당 notification은 사용자에게 보이지 않습니다.

fcm과 함께 사용할 때 겪었던 문제

보통 Notification은 fcm을 수신할 때 사용하는 경우가 많습니다. FCM이 날아오면 FirebaseMessagingService의 onMessageReceived()가 동작하기 때문에 해당 메서드에서 데이터를 읽어와 적절하게 channel에 매칭 시킨 후 Notificatinon을 띄워주는 주는 방식으로 자주 사용됩니다.
그러나 앱이 백그라운드에 있을 때 fcm이 날아오면, 분명히 onMessageReceived()에 Channel id을 명시해 주었음에도 불구하고 전혀 생성한 적 없는 "기타" 채널로 매칭이 되거나, AndroidManifest.xml에 default로 지정한 채널에만 매칭 되는 현상이 있었습니다.

원인을 분석해보니, 결론적으로 Notification에 대한 이해 부족이 아니라 fcm service에 대한 이해가 부족했음을 깨달았습니다.
fcm 데이터 형식에는 notification 영역과 data영역이 있습니다(https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ko). notification에는 "title"과 "body"라는 키를 가질 수 있고, 앱이 백그라운드에 있다면 자동으로 notification의 title과 body가 알림으로 뜨게 됩니다. 한편 구글 문서는 data영역은 선택사항이라고 말하고 있습니다. 부가적인 payload인 셈이죠(notification 영역에는 딱히 선택영역이라는 말은 없습니다).

따라서 fcm 삽질을 많이 해보지 않은 저로써는 당연히 notification에 title과 body를 넣어주고, 부가적으로 앱에서 필요한 데이터를 data영역에 넣어주어야 한다고 생각했습니다. 그러나 FCM Json 메시지로 notification과 data를 같이 받게 되면 특이한 현상이 일어납니다. 앱이 백그라운드에 있을 경우, 사용자가 알림 탭을 클릭하지 않으면 절대로 onMessageReceived() 메서드가 실행되지 않습니다.

 

 

만약 기본 채널 하나만 사용하는 앱이 아니라 여러 가지 알림 채널을 사용하는 앱이라면, onMessageReceived() 메서드에서 Channel을 매칭해 줄 것이기 때문에 꼭 onMessageReceived() 메서드를 타야만 합니다. 만약 그렇지 않다면 사용자가 앱의 기본 채널을 제외한 모든 알림 채널을 비활성화해놓았다 하더라도 앱이 백그라운드 상태일 때는 기본 채널로 수신이 돼버리고 마는 것이죠. 

그렇다면 백그라운드일 때 channel 매칭을 어떻게 할까요? 방법은 있습니다. fcm json 데이터에 notification을 없애 버리고, data 영역에만 데이터를 넣으면 백그라운드에서도 onMessageReceived() 메서드가 실행됩니다. data 영역에 title과 body를 넣어서 추출해야 합니다. 이렇게 되면 "대다수 유저들이 안드로이드 O 버전 이상인 현대에서 notificatnion 영역은 왜 존재하는가?"라는 의문이 들기 시작하지만, 다중 channel을 지원한다면 현재로써는 이 방법밖에 없는 것 같습니다(여러 회사에 물어보았는데 다들 같은 이유로 data영역만 사용한다고 하시더라고요). 

결론

개발 중인 앱이 다중 알림 채널을 지원한다면, 그리고 백그라운드에서도 채널 관리가 중요한 앱이라면, fcm 데이터에 notification을 제거하고 data 영역에 모든 데이터를 넣어주어야 합니다.

혹시 더 좋은 방법이 있다면 댓글로 알려주세요.!