(Android) FCM을 이용하여 사용자 경험 개선하기

FCM 도입 시 고려할 점들

Jaesung Lee
jaesung dev
9 min readSep 14, 2023

--

Photo by Jonas Leupe on Unsplash

최근 동아리에서 진행한 프로젝트에서 FCM을 도입하여 헤드업 푸시 알림을 적용하였습니다. 이번 글에서는 프로젝트에서 FCM을 적용하면서 발생했었던 문제점들과 해결 방법을 정리합니다.

FCM 도입 배경

제가 진행했던 프로젝트는 대화하고 싶은 상대를 쉽고 빠르게 연결해주는 키워드 기반의 쪽지 어플리케이션입니다. 프로젝트의 특성 상 유저들 간의 소통이 핵심 기능이였고, 원활한 소통을 할 수 있는 방법들에 대해 기획 단계에서부터 디자이너, 개발자들과 많은 이야기를 주고 받았습니다.

1. WebSocket

WebSocket은 일반적으로 클라이언트-서버 통신 방식에서 사용되던 HTTP 프로토콜과는 사뭇 다른 방식입니다. 기존의 HTTP는 클라이언트가 서버에 연결을 요청하여 통신하는 단방향 방식이였다면, WebSocket은 클라이언트와 서버가 양방향 통신형태로 연결을 유지하게 됩니다. 즉, 클라이언트가 서버에 별도의 Request를 요청하지 않아도 Response를 받을 수 있게됩니다. 이와 같은 방식은 채팅 서비스에서 많이 사용됩니다. 실시간 데이터 업데이트가 핵심적이기 때문입니다.

저희 프로젝트는 원하는 관심사에 대한 소통이 유저가 피로감을 느끼거나 부담감을 갖지 않도록 하자는 기획을 갖고 있었고, 다른 유저가 보낸 메시지를 즉각적으로 받게 된다면 기획에 어긋날 것이라는 판단을 하게 되었습니다.

2. FCM Push Notification

저희가 생각했던 쪽지는 채팅에 비해 다른 사용자가 보낸 메시지를 열람하는 것에 자유도가 높다고 생각했습니다. 메시지를 즉각적으로 수신하여 열람하는 채팅에 비해, 즉각적으로 수신하더라도 그 메시지를 열람하고 답장하는 시점은 오롯이 유저가 결정할 수 있다고 판단했습니다. 다만, 사용자에게 쪽지가 도착했다는 것을 알릴 필요는 있었고, 아무런 장치 없이 사용자가 알아서 앱에 진입하는것을 기대하는 것은 어려울 것이라고 결론을 내렸습니다. 그렇게 푸시 알림을 적용하게 되었습니다.

일단 만들어서 띄워 보자

프로젝트에서는 구독 중인 키워드에 대한 쪽지 알림, 연결된 유저가 보낸 쪽지 알림, 총 두가지 알림을 표시하고 있습니다.

제모옥은..

프로젝트에서는 알림을 띄우기 위해 FCM을 프로젝트에 연동하고, 발급받은 FCM token을 로그인 시 서버에 전달합니다. 서버 단에서는 전달받은 token을 DB에 저장하고있다가, 필요 시 조회하여 FCM에 알림 전송을 트리거하는 방식으로 진행됩니다.

1. FCM 토큰 관리

개발자 문서에는 FCM token의 경우 일반적으로는 정해진 만료 시간이 없지만 특정한 이벤트에 따라 만료될 수 있는 4가지 경우를 안내하고 있습니다. 아래 경우에 따라 갱신된 token을 서버에 새롭게 저장해야 합니다.

  • 앱이 인스턴스 ID를 삭제한 경우
  • 앱이 새 기기에서 복원되는 경우
  • 사용자가 앱을 제거 / 재설치 하는 경우
  • 사용자가 앱 데이터를 지운 경우

추가로, FCM token을 주기적으로 갱신해야할 필요도 있습니다. 오래된 token이 있는 비활성화된 기기에 메시지를 전송하여 리소스가 낭비될 수도 있기 때문입니다. 개발자 문서에서는 적절한 임계기간과 타임스탬프를 활용하는 방식을 안내하고 있습니다.

2. Service 구현

FCM에서 전송하는 메시지를 수신하여 Notification을 띄우기 위해서는 FirebaseMessagingService를 상속하는 Service를 구현해야 합니다. Service에는 대표적으로 아래 두 가지 메서드를 오버라이드하여 재정의합니다.

  • onNewToken(token: String)
  • onMessageReceived(remoteMessage: RemoteMessage)

onNewToken의 경우 앞서 설명한 상황에 따라 FCM token이 만료되어 재발급 되는 경우에 해당 메서드를 통해 새로운 token을 받게 됩니다. 여기서는 필요에 따라 서버에 갱신된 토큰을 저장하는 로직이 추가되면 될 것입니다.

onMessageReceived는 실제 수신된 RemoteMessage를 기준으로 데이터를 파싱할 수 있게 됩니다.

프로젝트에서는 onMessageReceived 메서드에 RemoteMessage 파싱, 채널 생성, 빌더 설정 등 다양한 책임을 갖는 로직이 몰리는 것을 피하기 위해 UseCase를 도입하였고, 이러한 로직들을 적절하게 분리하여 관리하고 있습니다.

발생했던 문제점

개발자 문서나 여러 블로그 글에도 알림을 표시하는 방식은 잘 나와있기 때문에 구현에 있어 크게 어려운 점은 없었지만 자체 QA를 진행하면서 발생한 몇가지 문제점들은 있었습니다.

1. 알림이 온 것 같긴한데.. 이거 왜 안보이나요?

앱을 사용하고 있는 도중에는 정상적으로 알림을 확인할 수 있었습니다. 하지만, 다른 앱을 사용하는 도중에 알림이 도착할 경우에는 확인할 수 없었습니다.

우선 FCM의 기본 흐름부터 다시 확인해보게 되었습니다. 서버에서 FCM 서버에 알림 트리거를 발생시키면 FCM 서버에서는 메시지를 전송하게 됩니다. 전달된 메시지는 onMessageReceived의 파라미터인 RemoteMessage를 통해 확인할 수 있습니다. 여기까지는 별 문제가 없었습니다.

추가로, 알림 수신 테스트를 여러 번 진행해봤습니다. 저의 경우, 메시지 타입별로 미리 생성한 알림 채널에 매칭시켜 알림을 표시하는 방식으로 구현하였습니다. 하지만, 앱이 포그라운드에 있을 경우에는 정상적으로 알림이 표시되었지만, 백그라운드에 있을 경우에는 전혀 관계없는 Miscellaneous라는 채널로 연결되었습니다. 심지어 이 채널의 알림 표시는 비활성화 된 상태였습니다. 이 채널을 통해 알림이 오는 것은 확인했었지만 직접 연결하지 않았던 채널이고, 매 번 활성화를 하는 것은 너무 비효율적일 것입니다.

문제의 해결법은 개발자 문서에 있었습니다.

처음 작성했던 코드는 아래와 같습니다.

개발자 문서에서도 나와있듯이 notification을 파싱하게 된다면 포그라운드에서는 원하는대로 onMessageReceived에서 메시지를 처리하여 알림을 표시할 수 있지만, 백그라운드에서는 onMessageReceived에서 처리하는 것이 아닌 FCM SDK 자체적으로 처리해 상태 표시줄에만 표시되게 됩니다.

발생중인 현상을 서버 개발자들과 공유하였고, 최종적으로는 data 메시지를 파싱하도록 수정하게 되었습니다. 개발자 문서에서도 설명하듯 data 메시지를 통해 파싱된 메시지는 포그라운드와 백그라운드 모두 onMessageReceived에서 처리할 수 있기 때문입니다.

일정이 타이트하더라도 언제나 답은 개발자 문서에 있음을 다시 한번 깨닫게 되었습니다.

포그라운드, 백그라운드 모두 확인할 수 있습니다.

2. 이전 알림들도 보고싶은데.. 이거 쌓이게할 수는 없을까요?

푸시 알림 기능이 적용된 다양한 서비스들만 보더라도 이전에 알림이 온 상태에서 새로운 알림이 도착하면 계속 누적해서 보여줍니다. 기존의 알림을 확인하지 못한 상태더라도 알림을 계속 노출시켜 사용자에게 보여주는 것이 비즈니스적으로도 더 좋다고 생각합니다.

프로젝트에서는 이러한 점이 고려되지 않은 상태였습니다. 아래는 수정하기 전 코드입니다.

NotificationConfigs에서는 NotificationManager 초기화, 채널 생성, 알림 표시를 하고있습니다. 문제 상황이 FCM에서 메시지가 수신되어 알림이 띄워지기는 하지만 쌓이지 않는 것이기 때문에 NotificationManager 초기화, 채널 생성까지는 문제가 없는 것으로 판단하였습니다.

생각보다 문제는 간단하게 해결할 수 있었습니다.

NotificationManager#notify의 javadoc에서 설명하는 파라미터 중 id에 해답이 있었습니다. id는 앱에서 알림을 식별하는 고유 식별자로 설명하고 있습니다. 즉, 각각의 알림은 고유한 식별자를 가져야 한다는 의미입니다.

구현한 코드에서는 SIGNAL_NOTIFICATION_ID라는 상수값으로 알림을 식별하고 있었습니다. 그렇기 때문에 새로운 알림이 도착하더라도 모두 이 상수값으로 식별되기 때문에 알림이 누적되지 않고 대체되는 것이였습니다.

알림이 도착하는 구체적인 시간은 파악할 수 없기 때문에 현재 시간을 기준으로 고유 식별자를 지정하는 식으로 수정하였습니다. 이외에도 식별자를 지정하는 방식은 다양할 것 같습니다. 최종적으로는 원하는 방식대로 알림을 누적시켜 사용자에게 계속 보여줄 수 있었습니다.

더 개선할 점은?

프로젝트에서 알림을 표시하는 경우는 앞서 설명한 구독중인 키워드에 대한 쪽지가 도착할 경우, 유저와 연결된 상태에서 수신한 쪽지 총 두 가지 경우입니다. 두 경우 모두 정시에 알림을 수신하거나, 수신 횟수가 적지 않기 때문에 알림이 무분별하게 쌓일 수 있게 됩니다.

이미 Android 7.0 이상부터는 4개 이상의 알림이 도착할 경우 자동으로 그룹화를 진행해주고 있습니다. 하지만 채널 구분 없이 하나의 그룹으로 그룹화를 해주기 때문에 채널 별로 별도의 그룹을 만들어 주는 것이 사용자 경험 측면에서 더 좋을 것 같습니다.

이 부분에 대한 개선은 다음 버전에 포함이 되어있는 상태이고, 또 다른 공유할 부분이 생기면 다시 글을 작성해보겠습니다. 해당 서비스는 링크를 통해 다운받아 사용해보실 수 있습니다.

--

--