메인 콘텐츠로 건너뛰기
중복 알림은 동일한 기기가 동일한 메시지 내용을 두 번 이상 수신할 때 발생합니다. 이 가이드는 가장 일반적인 원인과 해결 방법을 다룹니다. 동일한 사용자가 여러 기기(휴대폰, 태블릿, 데스크톱)에서 알림을 받는 경우, 이는 타겟팅 구성(세그먼트, 외부 ID 등)에 기반한 예상된 동작입니다. 중복 인앱 메시지의 경우 인앱 메시지 문제 해결 가이드를 참조하세요.
Apple은 중복을 유발한 iOS 17의 버그를 인정했습니다. 이는 iOS 17.3에서 수정되었습니다. 자세히 보기.

여기서 시작하세요

상황에 가장 적합한 섹션을 선택하세요:

동일한 메시지가 여러 번 전송됨

가장 일반적인 원인은 OneSignal API를 통해 동일한 알림 페이로드를 두 번 이상 전송하는 것입니다. 일반적인 이유:
  • 서버가 첫 번째 요청이 성공했는지 확인하지 않고 요청을 재시도합니다.
  • 백엔드 파이프라인의 로직이 동일한 알림을 두 번 전송합니다.
  • OneSignal로 마이그레이션했지만 이전 제공업체에서 여전히 알림을 보내고 있습니다. 두 시스템에서 동시에 보내는 것을 피하세요.

멱등 API 요청

재시도 시 멱등 키를 재사용하여 중복 메시지를 방지합니다.

서드파티 알림 핸들러

앱의 다른 코드가 OneSignal의 푸시 페이로드를 처리하고 OneSignal 외에도 자체 알림을 표시할 때 중복이 발생할 수 있습니다. 여기에는 다음이 포함됩니다:
  • 동일한 페이로드를 수신하는 다른 원격 푸시 SDK(예: Firebase Cloud Messaging).
  • Android의 FirebaseMessagingService 또는 iOS의 UNUserNotificationCenterDelegate와 같이 수신 페이로드를 읽고 로컬 알림을 구성하는 커스텀 메시지 핸들러.
OneSignal SDK는 유일한 핸들러일 때 OneSignal 페이로드를 자동으로 필터링합니다. 서드파티 코드는 명시적으로 알려주지 않는 한 필터링하는 방법을 모릅니다.

OneSignal 알림 식별

모든 OneSignal 알림에는 OneSignal 알림 UUID를 포함하는 i 키가 있는 custom 객체가 포함됩니다:
JSON
{
  "custom": {
    "i": "the-notification-id"
  }
}
전체 페이로드 참조는 커스텀 OneSignal 페이로드 구조를 참조하세요.

서드파티 핸들러에서 OneSignal 알림 필터링

OneSignal 이외의 모든 핸들러에서 custom.i 키를 확인하고 해당 키가 있으면 조기 반환합니다. 그러면 OneSignal은 자체 페이로드를 처리하고 다른 코드는 자체 페이로드를 계속 처리합니다.
커스텀 FirebaseMessagingService 또는 다른 원격 메시지 핸들러에서:
Kotlin
override fun onMessageReceived(remoteMessage: RemoteMessage) {
    val customJson = remoteMessage.data["custom"]
    if (customJson != null) {
        try {
            val custom = JSONObject(customJson)
            if (custom.has("i")) {
                // OneSignal 알림——OneSignal SDK가 처리하도록 합니다
                return
            }
        } catch (e: JSONException) {
            // OneSignal 페이로드가 아니므로 계속 진행합니다
        }
    }

    // 여기서 자체 알림을 처리합니다
}
다른 제공업체에서 전송되는 페이로드 구조를 제어할 수 있다면, 고유한 마커 키(예: my_app_notification: true)를 추가하고 양방향으로 필터링하세요——자체 코드에서는 마커가 포함된 알림만 처리하고, custom.i 키가 포함된 알림은 OneSignal이 처리하도록 합니다.

Android 알림 핸들러

OneSignal은 알림이 표시되기 전에 가로채고 커스터마이즈할 수 있는 두 가지 Android 콜백을 제공합니다: 이러한 콜백이 OneSignal의 자동 표시 외에도 알림을 표시할 때 중복이 발생합니다.

preventDefault()display() 규칙

OneSignal은 event.preventDefault()로 억제하지 않는 한 각 알림을 자동으로 표시합니다. 두 가지 일반적인 실수가 중복을 유발합니다:
  • 먼저 event.preventDefault()를 호출하지 않고 event.getNotification().display()를 호출하면——알림이 두 번 표시됩니다.
  • OneSignal이 원본을 표시하는 동안 NotificationManagerCompat.notify()로 별도의 알림을 게시하는 경우.
하나의 표시 경로만 활성화되어야 합니다——OneSignal의 display() 또는 자체 NotificationManagerCompat.notify() 중 하나이며, 둘 다 사용하면 안 됩니다.
override fun onNotificationReceived(event: INotificationReceivedEvent) {
    event.preventDefault()

    val notification = event.notification
    notification.setExtender { builder ->
        builder.setColor(0xFF0000FF.toInt())
    }
    notification.display()
}
같은 규칙이 포그라운드 생명주기 리스너의 onWillDisplay에도 적용됩니다:
Kotlin
OneSignal.Notifications.addForegroundLifecycleListener(object : INotificationLifecycleListener {
    override fun onWillDisplay(event: INotificationWillDisplayEvent) {
        event.preventDefault()

        // 자체 UI를 렌더링하거나 event.notification.display()를 호출하여 OneSignal 알림을 표시합니다
    }
})
preventDefault()를 호출했지만 display()를 호출하지 않으면 알림이 조용히 삭제되어 사용자에게 표시되지 않습니다. 모든 코드 경로가 display()를 정확히 한 번 호출하거나 의도적으로 알림을 억제하도록 하세요.
알림 서비스 확장과 포그라운드 생명주기 리스너를 모두 사용하는 경우, 특정 앱 상태에 대해 하나의 핸들러만 알림을 표시하도록 하세요. 양쪽에서 display()를 호출하면 중복이 발생합니다.

콜백 내 비동기 작업

표시 전에 백그라운드 작업(네트워크 호출, 데이터베이스 조회)을 수행할 때는 콜백에서 동기적으로 preventDefault()를 호출하고 비동기 작업이 완료된 후에만 display()를 호출하세요. preventDefault() 없이 콜백에서 반환하면 OneSignal이 알림을 표시하고, 이후 display() 호출이 중복을 생성합니다.

iOS 포그라운드 핸들러

OneSignal은 SDK 초기화 중에 자신을 UNUserNotificationCenterDelegate로 설정합니다. 앱이 포그라운드 알림 처리도 구현하는 경우, 알림이 두 번 표시될 수 있습니다——OneSignal에서 한 번, 코드에서 한 번. 중복을 유발하는 일반적인 패턴:
  • OneSignal로 호출을 전달하지 않고 자체 UNUserNotificationCenterDelegate를 구현한 후, 동일한 메서드에서 completionHandler([.banner, .sound])를 호출하고 커스텀 인앱 알림을 표시하는 경우.
  • OneSignal이 표시하는 수신 푸시에 응답하여 로컬 알림(UNUserNotificationCenter.current().add(...))을 예약하는 경우.
중복을 유발하지 않고 포그라운드 표시를 제어하려면 OneSignal의 포그라운드 생명주기 리스너를 사용하고 직접 알림을 렌더링하려는 경우 event.preventDefault()를 호출하세요:
Swift
OneSignal.Notifications.addForegroundLifecycleListener(self)

func onWillDisplay(event: OSNotificationWillDisplayEvent) {
    event.preventDefault()

    // 자체 UI를 렌더링하거나 event.notification.display()를 호출하여 OneSignal 알림을 표시합니다
}

다중 앱 인스턴스

프로덕션 및 개발 버전의 앱이 모두 설치되어 있을 때 중복이 발생할 수 있습니다. 각 앱에는 고유한 패키지 이름이 있고 자체 푸시 토큰을 받습니다.알림을 길게 눌러 어느 앱 인스턴스에서 전송했는지 확인하세요.

진단 팁

중복 문제를 더 빠르게 디버그하려면 다음을 수집하여 OneSignal 지원팀에 보내주세요:
  • 중복을 수신한 기기의 OneSignal 구독 ID
  • OneSignal 메시지 ID 또는 대시보드의 메시지 링크
  • 앱의 다른 라이브러리 또는 플러그인 목록
  • 문제를 재현하는 디버그 로그
  • 자세한 재현 단계

FAQ

앱에 두 개의 알림 SDK가 있으면 어떻게 되나요?

두 SDK가 모두 동일한 알림을 독립적으로 처리하고 표시할 수 있습니다. OneSignal은 자체 알림을 자동으로 필터링하지만 다른 SDK는 그렇지 않습니다. custom.i 키를 확인하여 다른 SDK의 핸들러에서 OneSignal 페이로드를 필터링하세요. 코드 예시는 서드파티 알림 핸들러를 참조하세요.

이전 제공업체와 OneSignal에서 동시에 푸시를 보내는 방법은 무엇인가요?

점진적으로 전환할 수 있지만 두 제공업체에서 동일한 메시지를 보내는 것을 피하세요.
  • Android: OneSignal을 통합하고 앱을 출시할 때 이전 SDK 알림 처리 코드를 제거하세요. 사용자가 업데이트하면 이전 제공업체에서 푸시를 받지 않게 됩니다.
  • iOS: 사용자가 업데이트하는 동안 일시적으로 이전 제공업체에서 계속 보낼 수 있습니다. 완전히 전환되면 중복을 피하기 위해 OneSignal에서만 보내세요.

빠르게 변하는 업데이트에 대한 여러 알림을 방지하는 방법은 무엇인가요?

알림 생성 API에서 collapse_id를 사용하여 알림을 쌓는 대신 이전 알림을 교체하세요. 여러 알림이 동일한 collapse_id를 공유하면 각 새 알림이 트레이의 이전 알림을 교체합니다. 이는 주가 업데이트, 라이브 스코어, 배달 예정 시간 및 유사한 빈번한 업데이트에 유용합니다.

관련 페이지

모바일 SDK 문제 해결

iOS 및 Android에서 푸시 알림 전달의 일반적인 문제를 해결합니다.

웹 푸시 문제 해결

웹 푸시 구독, 서비스 워커 및 전달 문제를 디버그합니다.

인앱 메시지 문제 해결

인앱 메시지 표시, 트리거 및 중복 문제를 수정합니다.

모바일 서비스 확장

iOS 및 Android에서 표시 전에 푸시 알림을 가로채고 커스터마이즈합니다.

표시되지 않는 알림(웹)

전송되었지만 표시되지 않는 웹 푸시 알림 문제를 해결합니다.