LODY/정리

iOS 개발 필수 기본기 / 푸시 알림 생명주기

푸시 알림 생명주기

🟣 푸시가 기기까지 오는 길

"FCM을 쓰면 APNs가 필요 없다"는 오해가 흔하다. iOS 푸시는 어떤 경우에도 APNs를 거친다. FCM은 서버에서 APNs까지의 구간을 감싸 주는 편의 레이어이고, 기기에 메시지를 꽂는 건 언제나 APNs다.

앱 서버Firebase CloudMessagingApple PushNotification ServiceiOS 기기REST APIHTTP/2TLS

"푸시가 안 왔다"를 서버 → FCM / FCM → APNs / APNs → 기기로 나누면, FCM 대시보드만 성공인데 기기 수신이 없을 때 앱 코드부터 의심하지 않아도 된다.


🟣 앱 상태별 처리

같은 푸시라도 프로세스 상태에 따라 호출되는 API가 다르다.

앱 상태수신 시점클릭 시점
ForegroundwillPresent로 표시 여부 결정didReceive
Background시스템이 알림만 표시 (앱 코드 없음, content-available은 예외)didReceive
Terminated시스템만 표시. 앱은 꺼져 있음ColdStart + launchOptions에 payload

클릭 이후는 어느 상태에서 왔든 didReceive로 모이는 경우가 많다. Terminated만, 앱이 죽어 있는 동안의 payload를 launchOptions에서 따로 꺼내야 한다.

ForegroundwillPresentBackground시스템만 표시Terminated클릭 → ColdStartlaunchOptions사용자 클릭didReceive(_:)파싱 · 측정 · 라우팅어느 상태든앱 상태

수신·클릭 콜백이 AppDelegate에 흩어지면 상태 분기가 복제된다. 한 핸들러 타입으로 모으고, 화면 이동은 라우터에 위임하는 편이 유지비가 낮다.


🟣 Rich Push와 Notification Service Extension

이미지가 붙은 알림은 메인 앱이 아니라 Notification Service Extension이 표시 직전에 payload를 가로채 만든다. Extension은 메인 앱과 별도 샌드박스다. UserDefaults·URLCache·Keychain(기본 그룹)이 그대로 보이지 않는다.

APNsNotificationService독립 샌드박스~30초 / ~24MBpayload 수정Main App자체 샌드박스App Group(명시할 때만 공유)mutable-content: 1알림 탭 후
  • payload에 "mutable-content": 1이 없으면 Extension이 에러 없이 호출되지 않는다
  • 시간·메모리 한도가 있어 이미지 다운로드는 짧게, 실패 시 serviceExtensionTimeWillExpire에서 원본이라도 표시한다
  • 메인 앱과 값을 공유하려면 App Group을 명시한다

🟣 ColdStart와 payload 보류

앱이 완전히 종료된 뒤 푸시를 누르면 didReceive만으로는 부족하다. application(_:didFinishLaunchingWithOptions:)launchOptions?[.remoteNotification]에 payload가 실려 온다.

이 시점에 내비게이션·인증이 아직 없으면 바로 라우팅하면 실패한다. URL은 일단 저장만 하고, 탭바·루트가 준비된 뒤(예: 첫 viewDidAppear)에 소비한다.

didFinishLaunchinglaunchOptionspending URL저장루트 화면 준비라우팅 소비

로그인 뒤에야 열 수 있는 화면이면, 로그인 완료 시점에도 같은 큐를 한 번 더 소비한다.


🟣 수신률을 믿기 어려운 이유

마케팅이 "기기 도달률"을 물을 때, Android와 iOS의 관측 범위가 다르다.

상태AndroidiOS
Foreground집계 가능willPresent로 집계 가능
BackgroundOS가 앱을 깨워 처리 가능content-available도 스로틀링으로 누락
TerminatedOS가 프로세스를 깨워 수신 집계 가능탭 전까지 앱·FCM이 수신을 모름

iOS FCM "Delivered"는 기기 실수신이 아니라 APNs 수락에 가까운 상한에 가깝다. 두 OS 숫자를 나란히 비교하면 캠페인 실패처럼 보이기 쉽다. 실무에서는 클릭률과 클릭 이후 전환처럼 양쪽에서 같은 구간만 지표로 쓰는 편이 안전하다.


🟣 용어

  • APNs: Apple 푸시 게이트웨이. 기기는 APNs에 TLS로 붙어 메시지를 받는다.
  • FCM: 전송 SDK. iOS 경로는 FCM → APNs → 기기.
  • UNUserNotificationCenter: 권한·수신·표시·탭의 단일 진입점.
  • Notification Service Extension: 표시 직전 payload 수정(Rich Push).
  • ColdStart: Terminated 상태에서 푸시 탭으로 앱이 처음부터 뜨는 경우.

🟣 정리

  • iOS 푸시의 마지막 구간은 항상 APNs다. FCM은 그 앞단 편의 레이어다
  • Foreground / Background / Terminated마다 콜백이 다르다. Terminated는 launchOptions를 빠뜨리면 캠페인 클릭이 증발한다
  • Rich Push는 Extension 샌드박스·시간 한도·mutable-content를 같이 설계한다
  • ColdStart는 "저장 후 소비"로 초기화 순서와 맞춘다
  • iOS 수신률은 구조적으로 불완전하다. 클릭·전환 중심으로 지표를 잡는다

푸시로 앱이 열렸으면, 그다음 문제는 어느 화면으로 보낼지다. 다음 장은 딥링크와 앱 라우팅이다.