ℹ️이 게시글에서 소개하는 예제들은 Android의 경우만 소개합니다. iOS, 등의 예제는 해당 파트에 첨부된 공식 문서 링크를 확인하세요!

바로 FCM을 실습해보고 싶다면 다음 게시글을 확인하세요!

🔥FCM 은 뭐하는 놈인가?


공식 문서

FCMFirebase Cloud Messaging의 약자로 파이어베이스에서 제공하는 교차 플랫폼 메시징 솔루션이다.

쉽게 말하면 안드로이드, iOS, 게임(Unity), 웹 등의 다양한 플랫폼을 운영하는 프로젝트의 클라이언트에게 알림 메시지(노티) 또는 데이터 메시지를 전송할 수 있는 기능이다.

🔥메시지가 전송되는 과정


FCM 아키텍처 개요

메시지를 보내는 서버가 필요하고, 메시지를 받을 클라이언트가 필요하다.

FCM_Architecture

  1. 클라이언트에게 보낼 메시지를 작성하는 부분이다. 파이어베이스 콘솔에서 GUI로 하거나 Admin SDK, HTTP/XMPP와 같은 신뢰할 수 있는 서버 환경에서 구현할 수 있다. 이 곳에서 작성된 메시지 요청을 FCM 백엔드에 전송한다.
  2. FCM 백엔드이다. FCM 백엔드에서는 여러 기능 중 메시지 요청을 수락해서 메시지 ID 같은 메시지 메타데이터를 생성해서 플랫폼별 전송 레이어로 보낸다.
  3. 플랫폼 수준 전송 레이어이다. FCM 백엔드에서 받은 메시지를 라우팅하는 역할을 한다. 메시지를 처리해서 플랫폼별 구성을 적용한다. 기기가 온라인 상태이면 기기로 전송한다.
    • Android: ATL (Android 전송 레이어)
    • Apple: APN (Apple 푸시 알림 서비스)
    • Web: 웹 앱용 웹 푸시 프로토콜
  4. 사용자 기기의 FCM SDK이다. 전송 레이어에서 보낸 메시지 또는 알림을 수신한다

이 과정에서 클라이언트를 고유하게 식별하는 등록 토큰이 필요하다.

🔥메시지의 종류


FCM 메시지 정보

FCM 메시지는 2가지 유형이 있다.

  • 알림 메시지
  • 데이터 메시지

알림 메시지

데이터 메시지에 비해 신경 쓸 부분이 적다. 정해진 형식에 따라 작성하게 된다. (사전에 정해진 key를 사용한다)

{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"메시지가 도착했습니다",
      "body":"오늘 치킨 고?"
    }
  }
}

A/B 테스팅을 제공하기 때문에 마케팅 용도로 사용하기 적합하다.

A/B 테스팅: 클라이언트에게 다른 선택지를 주고 어떤 선택지가 더 많은 선택을 받는지 통계를 내는 방법이다. 사용자가 더 선호하는 방식을 채택해 사용률을 높일 수 있다.

데이터 메시지

커스텀 key-value 쌍을 사용해 데이터 페이로드를 보낸다.

⚠️ 커스텀 key-value 쌍에 예약어를 사용하지 않아야 한다. -> from, notification, message_type, google이나 gcm으로 시작하는 단어

{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data":{
      "food" : "치킨",
      "store" : "교촌 치킨",
      "menu" : "허니 콤보"
    }
  }
}

data 키에 정보가 캡슐화 되어있고, 클라이언트가 메시지를 받으면 해석해서 사용한다.

프로그래매틱한 방법이나 파이어베이스 콘솔을 통해 노티를 보낼 수도 있다. 이때 클라이언트 앱이 백그라운드 상태인지 포그라운드 상태인지에 따라 다르게 처리된다. 특히 수신 당시에 앱이 활성 상태였는지 여부에 따라 다르다.

  • 백그라운드 상태: 노티가 앱의 노티 목록에 수신되며 사용자가 노티를 탭한 경우에만 앱이 데이터 페이로드를 처리한다.
  • 포그라운드 상태: 노티와 데이터 페이로드 둘 다 포함한 메시지 객체를 수신한다.
{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"치킨 뭐 먹을래?",
      "body":"교촌 vs BBQ"
    },
    "data" : {
      "kind" : "후라이드 vs 양념",
      "budget" : "20000"
    }
  }
}

이 외에도 모든 플랫폼을 타겟으로 메시지를 보낼 지, 특정 플랫폼에만 보낼 지 설정할 수도 있다. 자세한 내용은 여기를 참고하고, 이 글에서는 안드로이드에서 사용하는 것만 다룰 것이라 자세히 다루진 않을 것이다.

🔥전송 옵션


축소형 메시지비축소형 메시지가 있다.

축소형 메시지

메시지가 아직 기기로 전송되지 않은 경우 새 메시지로 대체될 수 있는 메시지이다. 가장 최신의 메시지만 표시한다는 의미이다.

예를 들어 스케줄 앱에서 FCM을 사용할 때 다음 일정만을 표시하기 위해 사용할 수 있다. 혹은 서버의 데이터를 동기화하라는 메시지를 가장 최신의 것만 표시하는 것도 예시가 될 수 있다.

안드로이드에선 메시지 페이로드에 collapseKey라는 매개변수를 포함하면 축소형으로 표시할 수 있다.

비축소형 메시지

축소형 메시지와 달리 최대 100개 까지 표시할 수 있다. 예를 들어 채팅 내역 등이 있을 것이다. 단, FCM에서는 전송 순서를 보장하지 않는다

별도의 매개변수를 포함하지 않으면 비축소형 메시지로 전송된다.

메시지 우선 순위

  • 보통 우선순위: 앱이 포그라운드 상태일 때 즉시 전송되고, 백그라운드 상태일 때는 전송이 지연될 수 있다. 새로운 이메일 알림과 같이 시간이 크게 중요하지 않은 메시지는 보통 우선순위를 사용한다.
  • 높은 우선순위: 기기가 슬립 모드인 경우에도 메시지를 즉시 전송하려고 시도한다. (애플 기기에서는 사용할 수 없다고 한다)
{
  "message":{
    "topic":"잡지_업데이트",
    "notification":{
      "body" : "새로운 잡지를 읽어보세요.",
      "title" : "NewsMagazine.com",
    },
    "data" : {
      "volume" : "3.21.15",
      "contents" : "http://www.news-magazine.com/world-week/21659772"
    },
    "android":{
      "priority":"normal"
    }
  }
}

Time To Live: TTL

메시지를 보내면 보통 FCM에서 바로 전송되지만 기기가 꺼져있거나 오프라인인 경우 바로 전송할 수 없을 수도 있다. 이때는 FCM이 메시지를 저장한 후 전송할 수 있는 상태가 되면 바로 전송한다.

안드로이드, 웹에서는 메시지의 최대 수명을 28일 내의 시간으로 지정할 수 있다. FCM이 메시지를 저장하고 전송하기 까지 메시지를 저장할 수 있는 기간이다. 따로 지정하지 않으면 4주가 기본 값이다.

최대 수명을 지정하는 경우는 다음의 경우들이 있다.

  • 만료 기한이 있는 초대 이벤트
  • 캘린더 일정

ttl(Time To Live) key를 사용해 지정할 수 있는데 FCM에서는 ttl을 0초로 지정할 수도 있다. 0초로 지정하게 되면 바로 전송할 수 있는 경우 전송하고, 그럴 수 없는 경우엔 전송하지 않고 삭제하는 기능을 만들 수 있다.

{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data":{
      "Nick" : "맹뱀",
      "body" : "프로젝트 회의 참가",
      "Room" : "게더 타운"
    },
    "android":{
      "ttl":"4500s" // 만료 기한
    }
  }
}

🔥여러 발신자


FCM에서는 여러 발신자가 하나의 클라이언트 앱에 메시지를 보낼 수 있다.

이때 클라이언트에서 발신자 ID가 필요하다. 다른 발신자 ID가 추가될 때마다 클라이언트 앱이 토큰을 가져오는 메서드를 사용해 토큰을 여러 번 가져온다. 토큰을 가져오는 메서드는 다음과 같다.

Android: FirebaseMessaging.getInstance().getToken()

이후 등록 토큰을 발신자와 공유하면 발신자가 자체 인증 키를 사용해 클라이언트 앱에 메시지를 보낼 수 있다.

발신자 수는 100명으로 제한된다.

🔥메시지 수명


기기가 FCM에 연결되고, 화면이 켜져있으며 아무 제한사항이 없으면 즉시 메시지가 전송된다.

기기가 연결되어 있지만 잠자기 모드인 경우 우선순위가 낮은 메시지는 잠자기 모드가 해제될 때까지 FCM이 메시지를 보관한다. 이 시점에서 collapse_key 플래그가 동작한다. 앞서 축소형 메시지에서 메시지가 전송 대기 중일 때 이전 메시지가 삭제되고 새 메시지로 대체된다고 설명한 부분이다.

기기가 FCM에 연결되어 있지 않으면 연결될 때까지 collapse_key 규칙을 반영해 메시지가 저장된다. 이후 연결이 되면 FCM이 대기 중인 모든 메시지를 기기로 전송한다.

만약 공장 초기화 등을 수행해서 다시 연결되지 않고 TTL만큼 경과하면 메시지는 삭제된다.

마지막으로 앱이 제거된 경우에는 FCM이 메시지를 즉시 삭제하고 등록 토큰을 무효화 한다. 이후 해당 기기로 메시지를 보내려고 하면 NotRegistered오류가 발생한다.

FCM 대시보드에서 사용자가 확인한 메시지 수와 노출 수 데이터를 확인할 수 있다.

🔥제한 사항


  • 개발자가 앱에 동일한 메시지를 자주 반복해서 보내면 메시지 전송을 지연(제한)한다.
  • FCM XMPP 서버에 연결할 수 있는 속도를 프로젝트당 1분에 400개 연결로 제한하고, 프로젝트마다 FCM에 동시 연결 2,500개까지 가능하다.
  • 단일 기기에 전송할 수 있는 최대 메시지 수는 240개/분, 5,000개/시간 이다.
  • 업스트림 메시지를 프로젝트당 1,500,000/분으로 제한한다.
  • 업스트림 메시지를 기기당 1,000/분으로 제한한다.
  • 주제 구독 추가 및 삭제 속도는 프로젝트당 3,000QPS로 제한한다.
  • 프로젝트당 동시 메시지 팬아웃 수는 1,000개로 제한한다.

메시지 팬아웃: 잠재고객 및 사용자 세그먼트를 타겟팅하는 경우 여러 기기로 메시지를 전송하는 프로세스

🔥FCM 포트 및 방화벽


공식 문서 참고

🔥인증 정보


인증 정보 설명 사용
프로젝트 ID Firebase 프로젝트 고유 식별자이다. 콘솔 설정에서 확인 가능하다. FCM v1 HTTP 엔드포인트 요청에 사용된다.
등록 토큰 각 클라이언트 앱 인스턴스를 식별하는 고유한 문자열이다. 토큰은 비밀로 유지되어야 한다 메시징에 필요하다.
발신자 ID Firebase 프로젝트를 만들 때 생성되는 고유한 숫자 값이다. 콘솔 설정의 클라우드 메시징 탭에서 확인할 수 있다. 클라이언트 앱에 메시지를 보낼 수 있는 각 발신자를 식별하는데 사용한다.
액세스 토큰 HTTP v1 API에 대한 요청을 승인하는 단기 OAuth 2.0 토큰이다. Firebase 프로젝트에 속한 계정에 연결된다.
서버 키 콘솔 설정의 클라우드 메시징 탭에서 확인할 수 있다. 이전 프로토콜을 사용한 메시지 전송과 같은 Google 서비스에 엑세스할 때 인증하기 위한 Key이다.

🔥등록 토큰 관리


오래된 등록 토큰이 존재한다는 것은 비활성 기기에 메시지를 전송하기 위해 리소스를 낭비하고 있다는 뜻이다. 이러한 오래된 등록 토큰을 적절히 처리해야 한다.

  • 서버에 등록 토큰 저장: 서버는 각 클라이언트의 토큰을 추적하고 활성 토큰 목록을 유지해야 한다. 코드와 서버에 토큰 타임스탬프를 구현하고 이 타임스탬프를 정기적으로 업데이트하는 것이 좋다.
  • 오래된 저장된 토큰 제거: 유효하지 않은 토큰 응답이 오는 경우 토큰을 제거하는 것이 좋다.

토큰 검색 및 저장

앱을 처음 실행할 때 FCM SDK는 클라이언트 앱 인스턴스의 등록 토큰을 생성한다. 이 토큰은 API에서 요청을 보내거나 토픽을 구독할 때 필요하다.

처음 실행할 때 토큰을 검색하고 타임스탬프와 함께 앱 서버에 저장해야 하는데 이 타임스탬프는 FCM SDK에서 제공하지 않기 때문에 코드와 서버에서 구현해야 한다.

다음과 같이 토큰이 변경될 때마다 토큰을 서버에 저장하고 타임스탬프를 업데이트하는 것이 중요하다.

  • 앱이 새 기기에서 복원될 때
  • 사용자가 앱을 삭제 및 재설치 할 때
  • 사용자가 앱 데이터를 지울 때

잘못된 토큰 응답

FCM에서 잘못된 토큰 응답이 감지되면 등록 토큰을 시스템에서 삭제해야 한다.

  • UNREGISTERED (HTTP 404)
  • INVALID_ARGUMENT (HTTP 400)
  • 그 외

위 응답 중 하나를 받은 경우 확실히 토큰이 유효하지 않은 것이라 삭제하는 것이 좋지만 위 응답이 없다고 해서 무조건 토큰이 유효하다고 할 수는 없다. 예를 들어 FCM 서버는 기기가 영구적으로 오프라인 상태인지 알 수 없다.

🔥등록 토큰 최신으로 유지하기


위 사례처럼 응답이 없는 경우에 토큰이 항상 유효하다고 할 수는 없기에 임계값을 정해야 한다. 파이어베이스에서는 임계값을 두 달로 잡고 있다. 두 달이 지난 토큰은 비활성 장치라고 판단하는 것이다.

토큰 정기 업데이트

서버에서 등록 토큰을 주기적으로 검색 및 업데이트하는 것이 좋다. 그러기 위해서 다음 과정이 필요하다.

  • getToken()을 사용해 현재 토큰을 검색해서 앱 서버에 (타임스탬프를 포함해)보낸다.
  • 토큰 변경 여부에 관계 없이 정기적으로 토큰의 타임스탬프를 업데이트하는 서버 로직을 추가한다.

보통 한 달에 한 번 업데이트 하는 정도면 적당하다.

토픽의 오래된 토큰 구독 취소

토픽을 구독하는 등록 토큰을 관리하는 두 단계는 다음과 같다.

  • 앱은 한 달에 한 번이나 등록 토큰이 변경될 때마다 토픽을 다시 구독해야 한다.
  • 앱 인스턴스가 두 달동안 유휴 상태인 경우 Firebase Admin SDK를 사용해 토픽에서 구독을 취소하여 FCM 서버에서 토큰-토픽 매핑을 삭제해야 한다.

🔥마무리


이번 게시글에서는 FCM이 무엇인지, 메시지가 전송되는 과정과 메시지의 종류, 메시지 전송 옵션, 메시지의 수명, 등록 토큰 관리 등 개념적인 부분에 대해 다뤘습니다. FCM의 역할에 대해 알았다면 다음 게시글에서 안드로이드에서 FCM을 사용하기 위한 준비 과정을 확인해보세요!