TIL

fcm 알림 흐름 정리

하얀잔디 2026. 3. 11. 15:27

FCM 알림 기능 붙일 때 처음엔 단순히 API에서 바로 Firebase 호출하면 되지 않나 생각했었음.
근데 실제 서비스에서는 생각보다 고려할 게 많았음.

특히 아래 같은 문제들이 있었음.

  • 요청이 몰릴 때 API 응답이 느려질 수 있음
  • FCM 전송 실패를 API 요청 흐름 안에서 바로 처리하면 사용자 응답까지 지연될 수 있음
  • 재시도 로직, 실패 로그 추적, 토큰 만료 처리 등을 한 군데서 관리하기 어려움
  • 대량 발송이나 비동기 처리 시 안정성이 떨어질 수 있음

그래서 BullMQ + Redis + Firebase Admin SDK 조합으로 알림 구조를 분리해서 처리했음.

 

이번 글에서는
FCM 알림이 생성되고, 큐에 적재되고, 워커가 처리하고, 실패 토큰을 정리하는 전체 흐름을 정리해보겠음.

 

 

 

왜 API에서 바로 FCM을 보내지 않았음?

 

처음에는 서버 API 안에서 바로 아래처럼 FCM 호출하는 구조를 생각할 수 있음.

 
await admin.messaging().send(message);
 

이 방식은 구현은 간단함.
근데 운영 환경에서는 몇 가지 단점이 있음.

 

1) API 응답 시간 증가

알림 전송은 외부 서비스(Firebase)에 대한 네트워크 호출임.
이걸 사용자 요청 흐름 안에서 바로 처리하면, 알림 전송 속도에 따라 API 응답도 같이 느려질 수 있음.

 

2) 실패 처리 복잡

FCM 전송 실패 원인은 다양함.

  • 토큰 만료
  • 등록 해제된 토큰
  • 일시적 네트워크 오류
  • 메시지 포맷 오류

이걸 API 안에서 전부 분기 처리하면 서비스 로직이 지저분해지기 쉬움.

 

3) 재시도/로깅/추적이 어려움

실패했을 때 몇 번 재시도할지,
어떤 요청이 실패했는지,
어떤 토큰에서 반복적으로 문제가 나는지 추적하기가 어려움.

그래서 알림 생성알림 발송을 분리하는 게 좋다고 판단했음.

 

 

 

전체 구조

내가 정리한 구조는 대략 아래 흐름임.

 
[사용자 이벤트 발생]

[API 서버]
알림 발송 데이터 생성

[BullMQ Queue에 Job 추가]

[Redis]
Job 저장

[Worker]
Queue에서 Job 가져옴

[Firebase Admin SDK]
FCM 전송

[성공/실패 로그 기록]

[실패 시 토큰 비활성화 또는 재시도]
 

 

조금 더 현실적으로 풀면 아래와 같음.

  1. 채팅, 시스템 이벤트, 공지 등 알림 발생
  2. API 서버는 알림 payload를 만들고 바로 큐에 넣음
  3. 사용자에게는 알림 발송 완료가 아니라 발송 요청이 등록됨
  4. 별도 Worker 프로세스가 Redis Queue에서 job을 가져감
  5. Worker가 Firebase Admin SDK로 실제 FCM 전송 수행
  6. 성공하면 로그 남기고 종료
  7. 실패하면 에러 코드에 따라 재시도하거나 토큰 비활성화 처리

'TIL' 카테고리의 다른 글

Ansible 쓰는이유  (0) 2026.03.16
트래픽 몰려도 멀쩡히 웹서버 rollout을 해보자  (0) 2026.03.13
Docker - ContainerD 차이  (0) 2026.03.07
리눅스 파일 찾을때 명령어 저장  (0) 2026.03.06
Kubernetes 구성요소  (0) 2026.03.04