판단의 출발점
캠페인 대시보드에는 발송이 끝났다고 나오는데, 테스트 휴대폰에는 아무것도 뜨지 않습니다. 개발팀은 서버 응답이 정상이었다고 하고 운영팀은 고객이 알림을 받지 못했다고 합니다. 이때 먼저 확인할 것은 발송 건수가 아니라 그 화면의 ‘성공’이 어느 단계까지를 뜻하는가입니다.
OneSignal의 Delivered는 FCM·APNs 같은 푸시 사업자가 메시지를 접수했다는 뜻입니다. FCM이 메시지 ID를 반환하는 것도 전달 요청을 받아들였다는 의미이지, 휴대폰에 표시했다는 증거는 아닙니다. 따라서 서버의 성공 응답과 고객의 미수신 신고는 동시에 성립할 수 있습니다.[1][3]
원인을 좁히려면 요청 수락 → 대상자 결정·큐 → 푸시 사업자 접수 → 기기 수신 → 화면 표시 → 열람·상호작용을 분리해야 합니다. 앞 단계의 성공으로 뒤 단계를 추정하지 말고, 마지막으로 확인한 증거에서 다음 단계로 이동하는 방식입니다. 이 글의 진단 순서와 기록 양식은 이 원칙을 적용한 운영 설계안입니다.
‘성공’을 여섯 단계로 나누면 확인할 담당자도 달라집니다
첫 단계는 발송 API의 요청 수락입니다. 요청한 프로젝트나 앱이 맞는지, 응답 본문에 오류가 없는지, 추적할 메시지 ID가 생겼는지를 봅니다. 그다음은 대상자 결정과 큐입니다. 예약 시각이 아직 오지 않았거나, 유효한 수신 대상이 없거나, 발송 제한으로 제외됐다면 기기 설정부터 살펴볼 이유가 없습니다. OneSignal은 이러한 상태를 Scheduled, Queued, No Recipients 등으로 구분하고 메시지 설정에서 대상·예약·제한 조건을 보여 줍니다.[1]
세 번째인 푸시 사업자 접수부터는 FCM·APNs의 응답과 오류가 중요합니다. 네 번째 기기 수신은 SDK 수신 기록이나 지원되는 전달 확인 기능으로 확인합니다. 다섯 번째 화면 표시는 알림 센터·잠금 화면·배너 등 어디에 보였는지를 따로 확인해야 합니다. 마지막 열람·상호작용도 알림 클릭과 앱 실행을 구분해 기록합니다. 클릭하지 않고 배너를 읽은 사람의 이해 여부까지 클릭 지표로 증명할 수는 없습니다.[1][2]
이 여섯 단계는 진단을 위한 논리 모델입니다. 모든 서비스가 여섯 이벤트를 제공하거나, 각 단계의 ID가 같거나, 모든 메시지에 종단 간 기록이 남는다는 뜻은 아닙니다. Apple도 Push Notifications Console에서 APNs 전달 로그를 살펴보는 방법을 제공하지만 이를 사용자 화면 표시나 열람 확인과 동일시해서는 안 됩니다.[14]
실무에서는 단계별 상태를 확인됨 / 아직 확인 못 함 / 이 환경에서는 관측 불가로 나누는 편이 좋습니다. 세 상태를 모두 성공·실패 두 칸으로 압축하면, 계측이 없는 구간을 장애로 오인하기 쉽습니다.
채널 연결에서 첫 캠페인과 인계까지 — SDK와 채널을 설정하고 핵심 캠페인을 검증한 뒤 내부 담당자가 이어갈 수 있도록 인계합니다.
OneSignal의 Delivered와 Confirmed receipt는 다른 증거입니다
| 대시보드 지표 | 확인하는 내용 | 이 지표만으로 결론 내릴 수 없는 내용 |
|---|---|---|
Sent | 푸시 사업자 전송 성공과 실패를 포함하는 발송 수 | 성공한 전송 수, 기기 수신 수 |
Delivered | 푸시 사업자까지의 전달·접수 | 기기 도착, 화면 표시 |
Confirmed receipt | OneSignal SDK의 기기 수신 확인 | 배너 표시, 사람이 읽었는지 여부 |
Clicked | 알림을 클릭한 Subscription 수 | 클릭하지 않은 기기의 미수신 여부 |
위 정의는 OneSignal 푸시 보고서에 적용됩니다. 다른 제품에서 이름이 비슷한 지표를 같은 뜻으로 합치지 않습니다.[1][2]
공식 문서가 현재 사용하는 이름은 Confirmed receipt이며 Confirmed Delivery로도 알려져 있습니다. 유료 플랜과 기기의 OneSignal SDK가 필요하고 API로만 만든 Subscription은 지원하지 않습니다. iOS에서는 Notification Service Extension과 App Group 구성이 필요하며 푸시에 mutable-content: 1이 포함돼야 합니다. Safari는 이 수신 확인 기능을 지원하지 않습니다.[2]
따라서 수신 확인이 비어 있을 때는 실제 전달뿐 아니라 확인을 돌려보내는 경로가 준비됐는지도 살펴야 합니다. iOS 확장이나 App Group 설정이 잘못되면 기기가 푸시를 받아도 확인이 돌아오지 않을 수 있습니다. 반대로 수신 확인이 있어도 앱 코드나 표시 정책 때문에 알림이 눈에 보이지 않을 수 있습니다.[2]
증상별로 첫 확인 지점부터 좁힙니다
“전체가 못 받는다”는 표현도 점검 범위를 붙여야 합니다. 내부 테스트 기기 두 대에서 못 받은 것인지, 특정 캠페인의 대상 전체를 확인한 것인지부터 구분합니다. 아래 표는 공식 문제 해결 문서와 뒤에서 설명할 플랫폼별 조건을 연결한 진단 설계안입니다. 첫 확인 지점은 원인의 확정이 아니라 조사 순서입니다.[13]
| 증상 | 첫 확인 지점 | 필요한 증거 | 다음 조치 | 관측 한계 |
|---|---|---|---|---|
| 확인한 대상 모두에서 안 보임 | 올바른 앱·프로젝트인지, 실제 대상과 예약·큐 상태 | 요청 ID, 메시지 ID, 대상 조건, 발송 시각, 사업자 오류 | 대상이 없다면 조건을 수정하고 접수 오류라면 인증·앱 환경을 점검 | 캠페인 합계만으로 개별 기기 상태를 알 수 없음 |
| iOS에서만 안 보임 | APNs 접수 결과와 앱·환경 식별 일치 | iOS 대상 기록, APNs 오류, 앱 빌드, 권한·Focus 설정 | 접수 성공이면 기기 수신·전경 표시 처리를 확인. 수신 확인만 빠졌다면 NSE·App Group 점검 | Android 성공이 iOS 연동 정상의 증거는 아님 |
| Android에서만 안 보임 | 알림 권한과 실제 사용하는 채널 | OS·target SDK, 권한 상태, 채널 ID·중요도, 발송 payload | 표시 설정 다음에 전경·배경 처리와 수신 핸들러를 확인 | FCM 수신과 알림 표시가 같은 이벤트는 아님 |
| 특정 사용자·기기만 안 보임 | 해당 설치본이 실제 발송 대상이었는지 | 회원과 Subscription의 연결, 등록 갱신 시각, 해당 기기 권한 | 다른 기기·이전 설치본을 가리키는지 확인하고 등록 상태를 정상 경로로 동기화 | 현재 구독 상태가 발송 당시 상태를 그대로 증명하지 않음 |
| 늦게 오거나 간헐적으로 빠짐 | 발송 대기와 사업자 접수 이후 지연을 구분 | 예약·접수·수신 시각, 네트워크 상태, TTL, 우선순위, 병합 설정 | 큐 대기·오프라인·만료·앱 상태 중 하나씩 조건을 바꿔 비교 | 서로 다른 시계와 집계 지연을 곧바로 전송 지연으로 계산하면 안 됨 |
| 수신 증거는 있는데 화면에 안 보임 | 표시 위치와 앱의 표시 처리 | 기기 수신 기록, 배너·알림 센터 관찰, 전경 상태, 표시 억제 코드 | 전달 연동을 다시 만들기 전에 표시 정책과 앱 코드를 점검 | 화면에 한 번 보였다는 증거만으로는 실제 열람을 확인할 수 없음 |
특정 기기라면 회원 ID보다 수신 주소부터 확인합니다
OneSignal의 User는 사용자이고 Subscription은 기기·브라우저 등 채널의 수신 단위입니다. 한 사람이 여러 Subscription을 가질 수 있습니다. 계정이 존재한다는 사실만으로 지금 손에 든 휴대폰이 발송 대상이었다고 볼 수는 없습니다. 대상 Subscription과 현재 설치본을 맞춰 보고, 마지막 동기화와 구독 상태를 확인해야 합니다.[4]
FCM을 직접 연동했다면 현재 사용하는 등록 방식에 맞춰 클라이언트의 등록 식별자, 서버 저장값, 갱신 시각을 대조합니다. 등록 토큰을 사용하는 구현도 같은 원칙입니다. UNREGISTERED처럼 등록 무효를 알리는 응답은 처리해야 하지만 INVALID_ARGUMENT를 받았다고 곧바로 토큰을 삭제하면 안 됩니다. 이 오류는 payload 문제에도 발생하므로, payload가 유효한지를 먼저 확인해야 합니다.[5]
회원·사용자 프로필·수신 대상의 연결을 정리할 양식이 필요하다면 OneSignal 사용자 식별·이벤트 명세 키트를 참고합니다. 여기서는 데이터 모델 전체를 다시 설계하기보다, 문제 메시지가 어느 설치본을 대상으로 했는지 확인하는 데 사용합니다.
Android와 iOS는 표시를 확인하는 순서가 다릅니다
Android: 권한, 채널, payload와 앱 상태를 함께 봅니다
Android 13(API 33) 이상에서는 예외 대상이 아닌 알림에 런타임 알림 권한이 적용됩니다. 특히 새로 설치한 앱은 사용자가 권한을 허용하기 전까지 기본적으로 알림이 꺼져 있습니다. 업그레이드한 앱까지 모두 같은 상태라고 가정하지 말고, 실제 권한 상태를 확인합니다.[6]
Android 8.0(API 26) 이상에서 알림 채널을 사용하는 앱은 앱 전체 허용 여부와 개별 채널 설정을 구분해야 합니다. target SDK 26 이상인 앱이 채널 없이 알림을 게시하면 표시되지 않습니다. 채널이 있어도 사용자가 그 채널을 끄거나 중요도를 바꿨을 수 있으므로, 발송에 지정한 채널과 기기의 채널을 맞춰 봅니다.[7]
그다음은 메시지 유형입니다. FCM의 notification 메시지는 앱이 배경에 있을 때 시스템 알림 영역으로 전달되지만 전경에서는 onMessageReceived로 전달됩니다. data 메시지는 앱의 처리 코드가 중요합니다. 두 payload가 함께 있는 메시지도 배경에서는 알림이 표시되고 데이터는 사용자가 알림을 열 때 전달되는 경로를 갖습니다. 따라서 배경 notification 메시지에 onMessageReceived 기록이 없다는 이유만으로 미수신이라고 판정하면 안 됩니다.[8]
iOS: APNs 경로와 화면 표시 정책을 구분합니다
FCM으로 iOS에 보내는 메시지도 APNs를 거칩니다. FCM 성공 화면만 보지 말고 APNs 관련 설정과 오류를 확인한 뒤, 기기의 알림 허용 여부와 잠금 화면·알림 센터·배너 설정을 살펴야 합니다. Focus는 알림을 허용할 앱과 시점을 조정합니다. Apple Watch를 함께 쓰는 경우에는 어느 기기에 알림이 나타났는지도 확인할 필요가 있습니다.[9][10]
전경 표시는 UNUserNotificationCenterDelegate 처리와 presentation options를 확인합니다. 화면 알림을 기대하면서 백그라운드 데이터 갱신용 푸시만 보내고 있지 않은지도 구분합니다. Apple 플랫폼의 백그라운드 알림은 전달되지 않을 수도 있으므로, 이를 화면 알림의 확정적인 대체 경로로 설계해서는 안 됩니다.[9]
OneSignal을 사용한다면 앱의 전경 리스너나 확장 코드가 표시를 억제하는지도 살펴봅니다. 이때 다른 수신 핸들러와의 관계를 확인하지 않고 SDK나 서비스를 제거하는 식으로 고치지 않습니다.[13]
웹 푸시라면 모바일 앱의 점검표를 그대로 적용하지 않습니다. 브라우저 권한·구독과 함께 OneSignal Service Worker의 등록·동작, 기존 PWA Service Worker와의 구성을 확인합니다. 수신 확인 기능의 지원 여부와 웹 푸시 자체의 지원 여부도 구분해야 합니다.[15]
관측에서 복구와 개선까지 이어지는 운영 — 서비스 지표와 경보를 기준으로 대응하고 변경 이력과 사후 보고를 다음 개선에 연결합니다.
지연을 없애려고 우선순위와 TTL부터 바꾸지 않습니다
먼저 예약·발송 제한·큐에서 기다린 시간과, 사업자 접수 이후 기다린 시간을 나눕니다. 기기가 오프라인이면 FCM이 메시지를 보관하다 연결 후 전달할 수 있지만 유효기간인 TTL이 끝나면 전달되지 않습니다. 같은 등록 토큰과 collapse_key에 해당하는 대기 메시지는 새 메시지로 대체될 수도 있습니다. 이는 모든 메시지가 순서대로 쌓여 전달된다는 가정과 다릅니다.[3]
FCM의 Android 일반 우선순위는 Doze 상태에서 지연될 수 있습니다. 높은 우선순위는 즉시 전달을 시도하지만 모든 푸시에 붙이는 해결책은 아닙니다. 사용자에게 보이는 알림으로 이어지지 않는 높은 우선순위 메시지는 우선순위가 낮아지는 등의 처리가 적용될 수 있습니다. 전송 우선순위와 알림 채널 중요도는 서로 다른 설정입니다.[7][11]
TTL을 0으로 설정하는 것도 지연 해결과 다릅니다. 즉시 전달하지 못하면 버리는 동작이므로, 뒤늦게라도 도착해야 하는 메시지에는 맞지 않을 수 있습니다. Android·웹의 TTL과 APNs의 만료 설정을 같은 필드처럼 복사하지 말고, 실제 발송 경로의 유효기간을 기록합니다.[3]
보고서의 지연도 구분해야 합니다. Firebase 보고서의 Received는 Android FCM SDK 18.0.1 이상에 제공되는 지표이고 Impressions는 Android의 배경 notification 메시지 표시를 대상으로 합니다. 이를 iOS까지 동일한 표시 확인으로 확대하면 안 됩니다. 보고서에는 Analytics 설정 등 수집 조건이 있고, 여러 통계는 집계로 인해 최대 24시간 늦게 반영될 수 있습니다. 별도의 집계형 FCM Data API도 전체 메시지를 빠짐없이 설명하는 장부는 아닙니다.[12]
따라서 실시간 재현은 개별 메시지와 기기의 증거로 확인하고 집계 보고서는 OS·버전·기간별 패턴을 비교하는 데 사용합니다. 집계에서 숫자가 바로 늘지 않았다는 이유만으로 같은 고객에게 재발송하지 않습니다.
재현 기록은 ‘안 왔다’가 아니라 조건과 관찰을 남깁니다
먼저 소유하거나 명시적으로 허가받은 테스트 앱·기기와 가상 사용자를 고정합니다. 전체 사용자 세그먼트나 실제 고객을 시험 대상으로 삼지 않습니다. 원본 토큰·인증정보·개인정보는 공개하지 않고, 제한된 내부 기록에서만 필요한 식별자를 연결합니다.
최소 기록에는 앱·프로젝트, 메시지와 수신 대상의 식별자, 시각과 시간대, OS·앱·SDK 버전, 앱 상태, 권한·채널, payload 유형, TTL·우선순위, 각 단계의 관찰 결과가 들어가야 합니다. 메시지 플랫폼 ID와 FCM·APNs ID가 있다면 각각 보존하되 같은 값이라고 가정하지 않습니다. 이 기록 항목은 앞선 문서들을 연결한 설계 제안입니다.
채운 기록 예시: 전경에서는 안 보이고 배경에서는 보이는 경우
다음은 실제 발송·측정 결과가 아닌 설계 예시입니다. 버전과 시각, 사용자·메시지 식별자는 기록 방법을 보여 주기 위한 예시이며 특정 SDK 버전의 신규 도입을 권장하는 뜻이 아닙니다.
| 공통 항목 | 예시 값 |
|---|---|
| 테스트 범위 | 소유한 개발용 앱과 테스트 기기 1대. 실제 고객 제외 |
| 경로 | 테스트 서버 → FCM HTTP v1 → Android. OneSignal 미사용 |
| 프로젝트 / 가상 사용자 / 기기 별칭 | example-push-lab / example-user-01 / example-device-A |
| 앱 / OS / SDK | 앱 1.0.0 (100) / Android 13(API 33) / firebase-messaging:24.0.0 |
| 빌드 조건 | compile SDK 34, target SDK 33. SDK 번호는 고정된 기록 예시 |
| 수신 주소 기록 | 실제 등록 토큰은 제한된 내부 저장소에 보관. 공유 기록에는 example-registration-A 별칭만 사용 |
| 권한 / 채널 | 알림 허용, 앱이 만든 diag 채널 활성화, 중요도 HIGH |
| 네트워크 / 기기 상태 | 같은 Wi-Fi, 화면 켜짐, 방해 금지 꺼짐, 강제 종료하지 않음 |
| payload / 전달 설정 | notification + data, 같은 내용, normal priority, TTL 600초, 별도 collapse key 미지정 |
| 앱 코드 | 전경 수신 콜백은 기록하지만 전경 알림을 직접 게시하는 코드는 없음 |
| 관찰 방법 | 각 발송 뒤 30초간 콜백과 시스템 알림 영역을 따로 관찰. 30초는 관찰 구간이지 전달 보장 시간 아님 |
SDK 24.0.0의 compile SDK 요구사항은 공식 릴리스 노트에 명시돼 있습니다. 실제 재현 기록에는 예시 번호를 복사하지 말고, 해당 빌드에 해석·포함된 정확한 버전을 기록합니다.[16]
| 관찰 항목 | A: 앱 전경 | B: 앱 배경 |
|---|---|---|
| 요청 시각 | 2026-09-26 10:00:00 +09:00 | 2026-09-26 10:02:00 +09:00 |
| 메시지 ID의 공유용 별칭 | example-msg-A | example-msg-B |
| API 관찰값 | HTTP 200과 메시지 ID 반환 | HTTP 200과 메시지 ID 반환 |
| 앱 수신 콜백 | 10:00:01에 관찰 | 관찰 구간에서 호출 없음 |
| 시스템 알림 영역 | 표시 없음 | 10:02:01에 표시 관찰 |
| 클릭 / 실제 열람 | 클릭하지 않음 / 판단하지 않음 | 클릭하지 않음 / 판단하지 않음 |
두 발송에서 의도적으로 바꾼 조건은 전경·배경 상태입니다. 요청 시각과 메시지 ID는 각 발송을 구분하기 위해 다릅니다. 이 가상의 관찰 조합은 FCM의 notification 메시지 처리 경로로 설명할 수 있습니다. A에서는 기기 수신 이후의 전경 표시 처리를 살펴야 하며 B에서는 수신 콜백이 없어도 시스템 알림이 표시될 수 있습니다.[8]
이 상황의 다음 조치는 인증키를 전부 재발급하는 것이 아닙니다. 제품이 전경에서도 알림을 표시해야 하는지 먼저 정하고 필요하다면 전경 표시 처리를 구현한 별도 빌드로 다시 비교합니다. 반대로 수신 기록도 표시도 없다면 그 구간은 미확인으로 남기고, 대상·접수·네트워크·만료 조건으로 돌아갑니다. 두 번의 테스트를 전체 사용자 전달 보장으로 확대하지 않습니다.
마지막으로 확인한 단계가 다음 조치를 정합니다
앱 권한이나 채널 설정 하나로 원인이 재현되고 해결된다면 새 메시징 플랫폼이나 별도 관측 도구부터 도입할 필요는 없습니다. 직접 확인할 수 있는 기기 상태와 기존 로그로 가설을 좁힌 뒤, 여러 앱·SDK·푸시 사업자 사이의 설정과 책임이 나뉘어 있을 때 연동 검토와 운영 지원의 범위를 정하는 편이 좋습니다.
SDK 연동과 채널 설정의 지원 범위는 IXC 앱 푸시 도입·온보딩에 설명돼 있습니다.
장애 기록의 마지막 문장을 “푸시가 안 왔다”로 끝내지 마세요. 어느 대상에 보냈고, 어디까지 확인했으며, 다음에 무엇을 확인할 것인지를 남기면 서버·앱·운영 담당자가 같은 문제를 조사할 수 있습니다.



