[Android] FCM 적용기 (1) FCM 이란?

빙티·2025년 7월 3일
2

안드로이드

목록 보기
3/3
post-thumbnail

이 글은 Firebase Cloud Messaging 공식 문서를 기반으로 합니다.
궁금한 내용, 틀린 내용이 있다면 언제든지 댓글을 달아주세요!


FCM 알림 도입 배경

스타카토에 공동 카테고리 기능이 생기면서 드디어 다른 사용자와 소통할 수 있게 되었습니다.
배경 이해를 돕기 위해 서비스 용어를 간단히 설명드리자면,

💡 카테고리는 기록을 모아 볼 수 있는 폴더의 개념입니다.
- 기본 카테고리 : 당사자만 기록 가능 (Private)
- 공동 카테고리 : 친구를 초대해 함께 기록 가능 (Shared)

공동 카테고리는 초기부터 구상한 핵심 기능이자, 많은 사용자들이 요청해주셨던 기능입니다.
그래서인지 배포하고 감회가 남달랐는데요.

막상 사용해보니 숙원을 이룬 것 치고 어딘가 밋밋한 기분이 들었습니다.
마치 닭갈비를 다 먹고 볶음밥을 추가하지 않은 것처럼 말이죠.
이 허전함의 원인은 바로…

실시간 알림의 부재였습니다.
공동 카테고리란 말이 무색하게도, 알림 기능이 없어 다른 사용자의 업데이트를 빠르게 알아챌 수 없었죠.


이대로라면 기껏 개발한 신기능을 아무도 사용하지 않을 것 같아 재빨리 디스커션을 남겼고
알림의 트리거가 다른 사용자의 활동인 만큼, 기기에서 직접 알림을 생성하는 로컬 알림보다는 FCM이 적합하다는 의견을 나눴습니다.
일 벌리기 좋아하는 팀원들 덕분에 빠르게 담당자가 정해져 개발을 시작할 수 있었습니다 !






FCM(Firebase Cloud Messaging)

FCM은 Google에서 💰무료💰로 제공하는 크로스 플랫폼 푸시 알림 서비스로,
서버에서 클라이언트로 실시간 메시지를 전송할 수 있도록 다양한 기능을 지원합니다.

FCM 메시지는 서버Firebase클라이언트 흐름으로 이동합니다.
Firebase는 서버와 클라이언트 사이에서 메시지를 라우팅하는 중개자 역할을 하죠.

  • 서버 : 메시지를 작성, 타겟팅, 전송하는 역할
  • 클라이언트 : 메시지를 받아 처리하는 역할 (알림 설정, 화면 이동 등)

혹자는 이런 의문을 가질 수도 있습니다.

"그냥 서버에서 클라이언트로 직접 보내면 안되나?" 🤔

그러나 서버가 수많은 클라이언트와 연결을 유지하면서 안정적으로 메시지를 보내는 것은 굉장히 어려운 일입니다.
또한 클라이언트의 한정된 자원과 OS의 백그라운드 제한으로, 앱이 실행 중이 아닐 때에도 실시간 알림을 수신하도록 직접 구현하기는 매우 까다롭습니다.

반면 FCM은 구글의 글로벌 분산 메시징 인프라를 기반으로 수억대의 기기에 안정적으로 메시지를 전송합니다.
또한 클라이언트에서는 각 플랫폼의 시스템 수준 전송 계층을 거치기 때문에 앱이 꺼져 있어도 메시지 수신이 가능합니다.

" 오빠 구글이야. 다 해 줄게. "




FCM 알림 전송 과정

서버에서 FCM 메시지가 만들어져 클라이언트 기기에 도착하기까지의 과정을 좀 더 자세히 살펴보겠습니다. (이미지 출처 - Firebase 공식 문서)

  1. 앱 서버
    앱 서버에서 메시지를 생성하고, FCM 백엔드에 전송 요청을 보냅니다.
  2. FCM 백엔드
    전송 요청을 받아 메시지 ID 같은 메타데이터를 생성한 뒤 플랫폼별 전송 레이어로 보냅니다.
  3. 플랫폼별 전송 레이어
    플랫폼(안드로이드, iOS, 웹)마다 상이한 메시징 정책을 고려하기 위한 계층입니다.
  4. 사용자 기기의 FCM SDK
    FCM 메시지를 수신합니다. 이 때 앱의 상태(포그라운드/백그라운드)와 메시지 유형(notification/data)에 따라 처리 방식이 달라집니다.

플랫폼별 전송 레이어

앞서 설명했듯, 플랫폼마다 메시지를 수신하고 처리하는 방식은 제각각입니다.
이를 고려해 Firebase는 메시지를 클라이언트 디바이스에 직접 전달하지 않고, 각 플랫폼의 전송 계층을 거쳐 전달합니다.
이러한 구조 덕분에 다양한 플랫폼을 아우르며 메시지를 전송할 수 있는 것이죠.

  • 안드로이드 : Google Play Services(구글 플레이 서비스)
  • iOS : APNs(애플 푸시 알림 서비스)
    FCM 서버가 iOS 기기의 APNs 토큰을 받아 APNs에 메시지를 전달해주는 중계 역할을 한다.
    FirebaseAPNsiOS 기기
  • 웹 앱 : Web Push Protocol
    FCM 서버가 브라우저로 메시지를 보내고 브라우저에서 이를 수신하여 앱에 전달한다.

Google Play Services(구글 플레이 서비스)

Google Mobile Services(이하 GMS)에 포함된 구글 플레이 서비스는 안드로이드에서 FCM 메시지를 수신하기 위해 사용하는 핵심 계층입니다.

FCM 서버에서 GMS가 탑재된 디바이스에 메시지를 전송하면, 구글 플레이 서비스에 내장된 FCM 클라이언트가 메시지를 수신합니다.

이렇게 수신된 메시지는 앱의 상태(포그라운드, 백그라운드)에 따라 다르게 처리됩니다.
더 자세한 처리 과정은 아래 FCM 메시지 처리 방식에서 다루겠습니다.

+) 흥미로운 사실 😲

중국에서는 구글 규제 정책으로 대부분의 안드로이드 기기에 GMS가 탑재되지 않습니다.
그래서 GMS에 포함된 구글 플레이 서비스를 사용할 수 없고, 당연히 FCM도 지원하지 않습니다.
대신 제조사가 개발한 자체 푸시 시스템을 사용한다고 합니다.
(샤오미 - Mi Push, 화웨이 - Huawei Push Kit)
따라서 중국 제조사의 안드로이드 기기를 위한 푸시 알림은 별도로 고려해야 합니다.




FCM 등록 토큰

서버가 FCM 메시지를 전송하려면 클라이언트의 FCM 등록 토큰을 알고 있어야 합니다.
이 토큰은 디바이스를 식별하고 메시지를 라우팅하기 위해 사용하는 고유한 문자열 입니다.
FCM 토큰은 특정 조건에서 새롭게 생성되므로, 이때마다 서버에 알려 항상 최신 토큰을 유지하는 것이 중요합니다.

FCM 토큰 갱신 시점

  • FID가 변경되었을 때
  • FCM 서버에서 기존 토큰이 만료되었을 때
  • FirebaseMessaging.deleteToken() 호출로 명시적으로 토큰이 삭제되었을 때

FID(Firebase Installation ID)

FID는 앱 인스턴스를 구별하기 위해 Firebase SDK가 생성하는 ID로, FCM을 포함한 Firebase의 여러 기능에서 공통 식별자로 사용됩니다.
따라서 FID가 바뀌면 이에 종속된 FCM 토큰도 함께 갱신됩니다.

FID 갱신 시점

  • 앱이 설치 및 삭제되었을 때
  • 사용자가 앱 데이터를 삭제했을 때
  • 기기 변경 후 앱이 복원됐을 때
  • FirebaseInstallations.delete()가 호출되었을 때
  • Firebase 설치 서비스가 내부적으로 FID를 무효화한 경우

토큰 관리 전략 세우기

파이어베이스 공식 문서를 참고하여, 서버 및 iOS와 함께 수립한 스타카토의 토큰 관리 전략은 아래와 같습니다.

  • 앱 시작 시 현재 FCM 토큰을 가져와 서버와 동기화한다.
  • 새 토큰이 발급될 때마다 서버로 보내 저장된 토큰을 갱신한다.
  • 사용자가 여러 기기를 사용하는 경우를 고려해, 디바이스별로 토큰을 관리하기 위해 플랫폼 타입(Android/iOS)디바이스 ID(FID)를 함께 전송한다.
  • 서버에서 메시지를 전송할 때 토큰 오류가 발생하면 해당 토큰을 삭제한다.



FCM 메시지 필드

FCM 메시지에는 알림(notification) 필드와 데이터(data) 필드가 있습니다.
각 필드의 존재 여부에 따라 알림, 데이터, 알림+데이터 총 세 개의 유형으로 구분됩니다.

1. Notification (알림)

알림 메시지는 Firebase에서 정의한 키를 활용해 구성할 수 있습니다.

{
  "to": "<FCM token>",
  "notification": {
    "title": "스타카토가 추가됐어요",
    "body": "OOO님이 OOO에 남긴 스타카토를 확인해 보세요"
  }
}

2. Data (데이터)

데이터 메시지는 사용자가 정의한 커스텀 키-값 쌍으로 구성됩니다.

{
  "to": "<FCM token>",
  "data": {
    "title": "스타카토가 추가됐어요",
    "body": "OOO님이 OOO에 남긴 스타카토를 확인해 보세요",
    "type": "STACCATO_CREATED",
    "staccatoId": "123",
  }
}

3. Notification + Data (알림 + 데이터)

알림 필드와 데이터 필드가 모두 존재하는 메시지 유형입니다.

{
  "to": "<FCM token>",
  "notification": {
    "title": "스타카토가 추가됐어요",
    "body": "OOO님이 OOO에 남긴 스타카토를 확인해 보세요"
  },
  "data": {
    "type": "STACCATO_CREATED",
    "staccatoId": "123",
  }
}

메시지 유형 결정하기

아래에서 자세히 다루겠지만, FCM 메시지 유형은 메시지 처리 방식을 결정하는 핵심 요인입니다.
따라서 본격적으로 기능을 개발하기에 앞서, 알림 기능의 목적과 플랫폼을 고려해 우리 서비스에는 어떤 유형이 적절한지 서버–클라이언트가 사전에 협의할 것을 추천드립니다.
(스타카토는 Android, iOS를 지원하고 화면 이동을 구현하기 위해 알림+데이터를 사용하고 있습니다.)





FCM 메시지 처리 방식

FCM 메시지 처리 방식은 크게 세 요인에 의해 결정됩니다.
해당 시리즈에서는 첫번째 요인이 '안드로이드 플랫폼'인 경우만 다룰 예정입니다.

  1. 클라이언트 플랫폼 : Android, iOS, Web
  2. 앱의 상태 : 포그라운드, 백그라운드
  3. FCM 메시지 유형 : 알림, 데이터, 알림+데이터

앱의 상태에 따른 처리 방식

앞서 FCM 메시지는 앱의 포그라운드/백그라운드 여부에 따라 다르게 처리된다고 설명했습니다.

앱은 사용자 행동에 따라 포그라운드와 백그라운드를 자유롭게 오갈 수 있으므로 일반적으로는 두 상태를 모두 고려해야 합니다.

  1. 앱이 포그라운드에 있는 경우
    메시지가 으로 전달되며 개발자가 직접 작성한 코드를 통해 메시지를 처리할 수 있습니다.
    따라서 더 복잡하고 플랫폼 종속적인 기능(화면 이동, DB 저장 등)을 자유롭게 구현할 수 있습니다.

  2. 앱이 백그라운드에 있는 경우
    2-1. notification 필드를 포함하지 않는다면
    앱이 포그라운드에 있을 때처럼 메시지가 앱으로 전달됩니다.
    2-2. notification 필드를 포함한다면
    시스템에서 알림 UI를 표시합니다. 이후 사용자가 알림을 클릭하면 시스템은 Intent로 앱을 깨웁니다.


처리 방식 요약

  1. Notification 필드
  • 포그라운드 상태 : 앱에서 직접 처리 가능
  • 백그라운드 상태 : 시스템이 자동으로 알림 UI 표시
  1. Data 필드
  • 포그라운드/백그라운드 상태 : 모두 앱에서 직접 처리 가능
  1. Notification + Data 필드
  • 포그라운드 상태 : 알림과 데이터 모두 앱으로 전달되어 직접 처리 가능
  • 백그라운드 상태 : 시스템에 의해 알림만 자동으로 처리됨

의문점
그렇다면 백그라운드에서도 알림을 수신하기 위해 항상 Data 필드만 사용해야 하는 걸까?
Notification + Data 조합을 사용 중이라면 백그라운드에서 어떻게 알림을 처리할 수 있을까?
해답은 다음 글에서 공개합니다!





다음 편 예고 ...

지금까지 FCM을 활용하기 위해 필요한 정보를 알아보았습니다.
위 내용을 바탕으로, 다음 편에서는 본격적으로 안드로이드에서 FCM 알림을 수신하는 방법에 대해 알아보겠습니다.

To Be Continued

2개의 댓글

comment-user-thumbnail
2025년 7월 7일

이거 개발 블로그 아니고 개그 블로그 아니에요?

1개의 답글