Android 앱 알림이 안 올 때: 권한·채널·배터리 제한 점검

핵심 요약
Android 앱 알림이 오지 않을 때 전체 알림 차단, Android 13 권한, 알림 채널, 방해 금지, 배터리·백그라운드 제한과 FCM 전달 상태를 순서대로 점검합니다.

검수 범위
Android 8.0 이상에서 일반 알림과 Firebase Cloud Messaging을 사용하는 앱을 대상으로 합니다. 문자·재난 알림 같은 시스템 서비스와 AlarmManager 기반의 정확한 예약 알림은 별도 정책이 있어 범위에서 제외합니다.

Android 앱 알림이 보이지 않는다고 해서 푸시 서버부터 다시 만들 필요는 없습니다. 서버가 메시지를 수락한 뒤에도 기기 전송, 앱 수신, Android 알림 권한, 채널 설정, 방해 금지와 화면 표시 단계가 남아 있습니다. 어느 단계에서 멈췄는지를 확인하지 않고 앱 데이터 삭제나 배터리 제한 해제부터 하면 잠시 나아져도 원인을 남기게 됩니다.

먼저 “기기에 메시지가 도착하지 않았다”와 “메시지는 도착했지만 알림으로 표시되지 않았다”를 구분합니다. 사용자 설정을 확인한 뒤 개발자 로그와 FCM 응답을 같은 메시지 ID로 연결하면 점검 범위를 빠르게 줄일 수 있습니다.

증상으로 첫 점검 위치 찾기

관찰한 증상 가능성이 높은 지점 먼저 확인할 항목
모든 앱 알림이 조용함 기기 전체 알림·방해 금지·음량 방해 금지, 무음과 잠금 화면 표시
한 앱의 모든 알림만 없음 앱 단위 권한·전체 차단 앱 알림 허용과 Android 13 권한
채팅은 오지만 공지만 없음 특정 알림 채널 차단 채널 ID와 시스템의 채널별 설정
소리는 없지만 알림 목록에는 있음 채널 중요도·무음·방해 금지 전달 실패가 아니라 표시 방식 문제인지
앱을 열면 밀린 알림이 옴 우선순위·Doze·백그라운드 처리 FCM priority와 수신 후 처리 시간
서버 전송은 성공인데 특정 기기만 안 옴 토큰·권한·채널·기기 상태 해당 설치의 최신 FCM token과 수신 로그

1단계: 사용자 설정에서 앱 전체 알림부터 확인

설정에서 해당 앱의 알림 허용 여부를 확인합니다. 기기 제조사와 Android 버전에 따라 메뉴 이름은 다르지만 일반적으로 설정 → 알림 → 앱 알림 또는 설정 → 앱 → 해당 앱 → 알림 경로에 있습니다. 최근 알림을 길게 눌러 앱의 알림 설정으로 이동할 수도 있습니다.

Android 알림 기록 기능이 켜져 있다면 알림이 실제로 도착했지만 사용자가 지웠는지, 무음 영역에 표시됐는지도 확인합니다. 기록에도 없고 앱 전체 알림이 차단돼 있다면 네트워크나 FCM token을 먼저 바꿀 단계가 아닙니다.

  • 앱 전체 알림 허용 여부
  • 잠금 화면에서 내용 또는 알림 자체를 숨겼는지
  • 무음 알림 영역에 들어갔는지
  • 방해 금지 모드와 예외 앱 설정
  • 알림 기록에 수신 흔적이 있는지

2단계: 채널별 차단과 중요도를 확인

Android 8.0(API 26)부터 모든 알림은 채널에 속해야 합니다. 사용자는 앱 전체를 허용하면서도 “채팅”, “주문”, “공지” 같은 채널 일부만 끄거나 무음으로 바꿀 수 있습니다. 시스템 설정 화면에서는 채널이 “알림 카테고리”처럼 표시되기도 합니다.

특정 종류의 알림만 빠진다면 앱을 재설치하기 전에 해당 채널이 꺼져 있는지 확인합니다. 알림 목록에는 있지만 소리나 팝업만 없다면 전달 자체보다 채널 중요도와 방해 금지 정책을 봅니다. 개발자는 사용자가 구분할 수 있는 이름과 설명으로 채널을 만들고, 긴급하지 않은 콘텐츠를 높은 중요도 채널에 섞지 않아야 합니다.

3단계: 배터리와 백그라운드 제한은 증거가 있을 때만 조정

Doze와 App Standby는 기기를 오래 사용하지 않을 때 백그라운드 CPU와 네트워크 작업을 미룹니다. 특히 일반 priority의 데이터 동기화는 절전 중 늦어질 수 있습니다. 앱을 열자마자 알림이 몰려온다면 전송 시각, 기기 수신 시각과 알림 표시 시각을 따로 기록해 지연 구간을 확인합니다.

모든 사용자에게 배터리 최적화를 끄도록 요구하는 것은 첫 해결책이 아닙니다. Android 공식 지침에 맞게 FCM을 사용하고, 긴급하고 사용자에게 실제로 보이는 알림만 high priority로 보냅니다. 단순 데이터 최신화를 위해 high priority를 남용하면 배터리 비용이 커지고 메시지 동작이 조정될 수 있습니다.

사용자 점검에서는 앱의 배터리 사용 설정이 “제한됨”으로 강하게 설정됐는지와 백그라운드 데이터가 차단됐는지만 확인합니다. 설정을 바꾼다면 같은 메시지를 화면 켜짐, 화면 꺼짐과 장시간 유휴 상태에서 각각 시험해 결과가 달라지는지 기록합니다.

4단계: Android 13 알림 권한을 코드와 설정에서 함께 확인

Android 13(API 33) 이상에서는 예외에 해당하지 않는 알림을 보내려면 POST_NOTIFICATIONS 런타임 권한이 필요합니다. 새로 설치한 앱의 알림은 기본적으로 꺼져 있으므로 manifest 선언만으로 충분하지 않고 사용자가 허용해야 합니다.

<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <application ...>
        ...
    </application>
</manifest>

앱은 사용자가 알림의 가치를 이해할 수 있는 시점에 권한을 요청하고, 전송 전에는 areNotificationsEnabled()로 현재 상태를 확인합니다. 사용자가 거절한 뒤 시스템 대화상자를 계속 띄우기보다 앱 안에서 필요한 이유와 설정 이동 경로를 설명합니다. 권한 허용 여부와 채널 허용 여부는 별도이므로 둘 다 확인해야 합니다.

5단계: 채널 ID와 생성 시점을 검증

Android 8.0 이상을 대상으로 하면서 존재하지 않는 채널 ID로 알림을 게시하면 알림이 나타나지 않고 시스템 로그에 오류가 남습니다. 앱 시작 시 필요한 채널을 안전하게 생성하고 알림을 만들 때 동일한 ID를 사용합니다.

const val CHANNEL_ID = "order_updates"

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel = NotificationChannel(
        CHANNEL_ID,
        "주문 상태",
        NotificationManager.IMPORTANCE_DEFAULT
    ).apply {
        description = "결제와 배송 상태 변경 알림"
    }

    getSystemService(NotificationManager::class.java)
        .createNotificationChannel(channel)
}

채널을 한 번 만든 뒤 앱이 중요도, 소리 같은 동작을 임의로 바꿀 수 없고 사용자가 최종 제어권을 가집니다. 코드를 IMPORTANCE_HIGH로 바꿨는데 기존 설치에서 계속 조용하다면 정상적인 동작일 수 있습니다. 단순히 사용자 선택을 우회하려고 새 채널 ID를 계속 만드는 것은 피하고, 알림의 의미가 실제로 달라진 경우에만 명확한 이전 정책을 둡니다.

6단계: 앱 내부 테스트 알림으로 표시 계층을 분리

FCM을 거치지 않고 앱 안의 버튼으로 로컬 테스트 알림을 하나 게시합니다. 이 알림도 보이지 않으면 서버 token보다 권한, 채널, 아이콘과 notification 생성 코드를 먼저 확인합니다. 로컬 알림은 보이는데 원격 알림만 빠지면 FCM token, payload와 백그라운드 수신 흐름으로 이동합니다.

if (NotificationManagerCompat.from(this).areNotificationsEnabled()) {
    val notification = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_stat_notice)
        .setContentTitle("알림 점검")
        .setContentText("권한과 채널을 통과한 로컬 테스트입니다.")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .build()

    NotificationManagerCompat.from(this).notify(1001, notification)
}

Android 13 이상에서는 호출 전에 런타임 권한을 확인해야 합니다. 테스트 성공 여부, 채널 ID와 notification ID를 로그로 남기되 사용자에게 표시한 민감한 본문은 운영 로그에 복사하지 않습니다.

7단계: FCM token의 설치 단위 최신 상태 확인

FCM registration token은 앱 설치 단위를 식별하며 갱신될 수 있습니다. 앱의 onNewToken에서 새 token을 서버로 보내고 사용자·기기 레코드와 갱신 시각을 관리합니다. 로그나 분석 도구에는 전체 token을 평문으로 남기지 말고 내부 설치 ID 또는 해시로 연결합니다.

  • 현재 앱에서 얻은 token과 서버 대상 레코드가 같은 설치인지
  • 로그아웃한 계정의 token이 이전 사용자에게 남지 않았는지
  • 오래 사용하지 않은 token의 갱신 시각을 기록하는지
  • FCM이 UNREGISTERED를 반환한 token을 대상에서 제거하는지
  • 한 사용자의 여러 기기를 별도 설치 레코드로 관리하는지

서버가 메시지 ID를 반환했다는 사실은 FCM이 요청을 수락했다는 증거이지, Android 화면에 알림이 표시됐다는 최종 증거는 아닙니다. 서버 응답과 기기 수신·표시 로그를 나눠 봅니다.

8단계: foreground와 background 처리 차이 확인

FCM의 notification message와 data message는 앱 상태에 따라 처리 위치가 달라질 수 있습니다. foreground에서 onMessageReceived가 호출됐다고 해서 시스템 알림이 자동으로 보인다고 가정하지 말고 앱이 직접 표시할 정책인지 확인합니다. notification과 data가 함께 있는 메시지는 background에서 알림을 누른 뒤 data가 실행 intent로 전달되는 흐름도 구분해야 합니다.

onMessageReceived에서는 짧은 시간 안에 payload를 검증하고 알림을 표시합니다. 원격 이미지를 내려받거나 추가 API를 오래 호출하면 프로세스 수명이 끝나 작업이 중단될 수 있습니다. 시간이 더 필요한 처리는 메시지 priority와 사용자 가치를 확인한 뒤 WorkManager 같은 적절한 백그라운드 작업으로 넘깁니다.

9단계: FCM priority와 채널 importance를 혼동하지 않기

FCM priority는 메시지가 기기로 전달되는 시점에 영향을 주고, Android 알림 채널 importance는 도착한 알림의 소리·팝업 같은 표시 강도에 영향을 줍니다. 둘은 다른 설정입니다.

설정 결정하는 것 잘못 판단한 예
FCM normal/high priority 특히 유휴 기기에서의 전달 시점 팝업을 띄우려고 모든 메시지를 high로 전송
채널 importance 소리, heads-up과 알림 목록 표시 전송 지연을 채널 중요도로 해결
Android 13 권한 앱이 알림을 표시할 수 있는지 서버 성공 응답을 권한 허용으로 간주

긴급한 채팅, 계정 보안이나 배송 상태처럼 즉시 보여야 하는 사용자 가치는 high priority를 검토할 수 있습니다. 백그라운드 동기화나 추천 갱신은 normal priority가 적합합니다. high priority 메시지가 사용자에게 보이는 알림으로 이어지지 않는 패턴은 FCM에서 우선순위가 낮아질 수 있으므로 수신 후 실제 표시 여부를 함께 관찰합니다.

10단계: 서버 오류 코드에 맞춰 token과 재시도 처리

FCM HTTP v1 응답을 성공·영구 실패·일시 실패로 나눕니다. 인증과 프로젝트 설정 오류를 같은 payload로 무한 재시도하지 않습니다.

응답 분류 판단 처리
UNREGISTERED 해당 앱 설치의 token이 더 이상 유효하지 않음 서버 대상 목록에서 제거
INVALID_ARGUMENT payload 또는 유효한 token 형식 문제 요청 필드 검증 후 수정
SENDER_ID_MISMATCH token과 발신 프로젝트 불일치 앱·서버 Firebase 프로젝트 확인
HTTP 429 전송률 또는 기기별 할당량 초과 Retry-After와 지수형 backoff 적용
HTTP 500·503 일시적인 서버 문제 가능 제한된 횟수로 지수형 backoff 재시도

재시도 큐에는 내부 메시지 ID, 대상 설치 ID, 시도 횟수와 다음 시각을 저장합니다. token과 알림 본문 전체를 오류 로그에 남기지 않습니다. 같은 사용자 이벤트를 재시도하면서 중복 알림이 생기지 않도록 업무 이벤트 ID도 함께 관리합니다.

수신부터 표시까지 관측 지점 만들기

“발송 성공” 한 줄 대신 다음 시각과 결과를 구분하면 누락 구간을 찾을 수 있습니다.

  1. 업무 이벤트 생성 시각과 내부 message ID
  2. FCM 요청 시각, HTTP 상태와 반환된 message name
  3. 기기의 onMessageReceived 시각과 app state
  4. 권한·채널 확인 결과와 notification 게시 시각
  5. 사용자가 알림을 눌러 앱으로 들어온 시각

개인정보 보호를 위해 token은 해시된 식별자로, payload는 유형과 크기처럼 진단에 필요한 최소 정보로 기록합니다. 수신 로그가 없으면 전송·token·기기 네트워크를, 수신 로그는 있는데 게시 로그가 없으면 앱 처리 코드를, 게시 로그까지 있는데 화면에 없으면 권한·채널·기기 표시 설정을 확인합니다.

수정 후 재현 테스트

테스트 기대 결과
Android 13 이상 새 설치·권한 허용 설명 뒤 권한 요청, 허용 후 테스트 알림 표시
권한 거절 반복 대화상자 없이 설정 경로 안내
특정 채널만 끔 해당 유형만 조용하고 다른 채널은 정상
foreground에서 FCM 수신 수신 로그와 앱의 표시 정책이 일치
background·화면 꺼짐 정한 priority 정책에 맞춰 전달
Doze 상태 normal 지연과 긴급 high 알림을 구분해 관찰
만료 token 전송 UNREGISTERED 처리 후 대상에서 제거
429·503 응답 즉시 반복 없이 backoff와 최대 횟수 적용
여러 기기에 로그인 각 설치 token과 사용자 연결이 정확함

가장 짧은 진단 순서

  1. 한 앱만 문제인지 모든 앱이 문제인지 구분합니다.
  2. 앱 전체 알림, Android 13 권한과 해당 채널을 확인합니다.
  3. 앱 내부 로컬 테스트 알림으로 표시 계층을 검증합니다.
  4. 로컬 알림이 보이면 최신 FCM token과 서버 응답을 확인합니다.
  5. 기기 수신 로그와 notification 게시 로그를 나눠 확인합니다.
  6. 유휴 상태에서만 늦다면 priority, Doze와 처리 시간을 확인합니다.
  7. 권한·foreground·background·Doze·만료 token 테스트를 다시 실행합니다.

이 순서를 따르면 사용자 설정, 앱 표시 코드, FCM 전송과 Android 절전 정책을 한꺼번에 바꾸지 않고 알림이 사라진 정확한 단계를 찾을 수 있습니다.


검증 기준과 참고 자료

이 글은 2026-09-14에 Android Developers, Android 고객센터와 Firebase Cloud Messaging 공식 문서를 기준으로 Android 13 알림 권한, 채널, Doze, 메시지 처리·우선순위, token 관리와 오류 코드를 검수했습니다. 기기 제조사와 Android 버전에 따라 설정 화면 이름은 달라질 수 있습니다.

관련 글