안드로이드 프레임워크·시스템 학습 노트 목차

알림·푸시 — Notification·채널·FCM

알림은 "사용자의 주의"를 빌리는 일

앱이 백그라운드에 있어도 사용자에게 닿는 유일한 통로가 **알림(notification)**이고, 서버가 잠든 앱을 깨워 알림을 보내는 것이 **푸시(FCM)**다. 그런데 알림은 사용자의 주의를 빌리는 일이라 — 남용하면 곧바로 차단·삭제로 이어진다. 그래서 현대 안드로이드는 통제권을 사용자에게 넘긴다 — 알림 채널로 종류별로 끄게 하고, Android 13+부터는 알림 자체에 런타임 권한을 건다. 핵심은 *"기술적으로 어떻게 보내나"보다 "맥락 있고 절제된 알림만 보내나"*다. (출처: Android — Notifications / Firebase Cloud Messaging.)

알림 채널 — 통제권을 사용자에게

Android 8+부터 모든 알림은 채널(channel)에 속해야 한다. 왜 채널을 강제할까? — 옛날엔 앱이 알림을 통째로 보내, 사용자는 "마케팅은 싫고 주문 알림만 받고 싶어도" 앱 알림을 전부 꺼야 했다. 채널은 알림을 "주문 알림"·"마케팅"·"채팅"처럼 종류로 묶어, 사용자가 채널 단위로 끄거나 소리·중요도를 조절하게 한다 — 앱이 아니라 사용자가 세밀한 통제권을 쥔다.

// 채널 생성 (앱 시작 시 1회) — 종류별로 만든다
val channel = NotificationChannel("orders", "주문 알림", NotificationManager.IMPORTANCE_HIGH)
notificationManager.createNotificationChannel(channel)

// 알림 표시
val notif = NotificationCompat.Builder(context, "orders")     // 어느 채널인지 지정
    .setSmallIcon(R.drawable.ic_noti)
    .setContentTitle("배송 시작").setContentText("주문 #42가 출발했어요")
    .setContentIntent(pendingIntent)                          // 탭하면 열릴 화면(딥링크)
    .addAction(R.drawable.ic_track, "추적", trackIntent)
    .build()
notificationManager.notify(42, notif)
  • 중요도(importance) — 채널의 소리·헤드업(화면 위로 튀어나옴) 수준. 한 번 정하면 사용자가 조절할 수 있어, 처음 값을 신중히 정한다(채팅은 높게, 마케팅은 낮게).
  • PendingIntent탭 시 동작(특정 화면 열기). immutable 플래그를 권장한다(보안).
  • 스타일BigTextStyle(긴 글)·BigPictureStyle(이미지)·MessagingStyle(채팅 대화).

POST_NOTIFICATIONS — 런타임 권한 (Android 13+)

Android 13+부터 알림을 띄우는 것 자체가 런타임 권한(POST_NOTIFICATIONS)이 됐다 — 사용자가 "허용"을 눌러야 알림이 뜬다([10]의 권한 흐름). 그래서 왜 알림이 필요한지 맥락을 설명하고 적절한 시점에 요청해야 한다(앱 첫 실행에 다짜고짜 묻기보다, 알림이 가치 있는 순간에).

FCM — 서버가 앱을 깨운다

앱이 꺼져 있어도 서버가 새 소식을 보내려면 — 직접 연결을 유지할 수 없다(배터리). 대신 구글의 푸시 인프라인 **Firebase Cloud Messaging(FCM)**을 거친다. 흐름은 토큰을 중심으로 돈다.

다이어그램 로딩 중…
class MyFcmService : FirebaseMessagingService() {
    override fun onNewToken(token: String) { sendTokenToServer(token) }  // ★ 토큰 등록/갱신
    override fun onMessageReceived(msg: RemoteMessage) {
        // 알림 표시 또는 데이터 동기화(길면 WorkManager로 넘김)
    }
}
  • 토큰(token)기기별 주소다. 앱이 발급받아 서버에 등록하고, 토큰이 갱신(onNewToken)되면 서버를 업데이트해야 한다(안 하면 푸시가 안 닿는다).
  • 메시지 종류 — *알림 메시지(notification)*는 앱이 백그라운드일 때 시스템이 자동으로 알림을 표시하고, *데이터 메시지(data)*는 onMessageReceived에서 직접 처리한다. 표시 방식·로직을 내가 제어하려면 데이터 메시지를 쓴다.
  • 토픽(topic) — *"news를 구독한 모두"*에게 일괄 발송(개별 토큰 없이).

절제·정확성

  • 과한 알림은 차단·삭제로 직결된다 — 사용자에게 진짜 가치 있는 것만 보낸다.
  • onMessageReceived백그라운드 처리는 짧게 — 길면 WorkManager([07])로 넘긴다(여기서 오래 끌면 제약·ANR).
  • 딥링크([08])로 알림을 탭하면 정확히 그 콘텐츠 화면으로 보낸다(목록이 아니라 해당 주문 상세로).

정리 — 채널로 통제권을, FCM으로 서버 푸시를

알림의 핵심 — 채널로 종류별 사용자 통제(중요도·끄기), NotificationCompat+PendingIntent로 표시·탭 동작·스타일, Android 13+는 POST_NOTIFICATIONS 런타임 권한(맥락 있게 요청), FCM으로 서버 푸시(토큰 등록·갱신·알림 vs 데이터 메시지·토픽), 긴 처리는 WorkManager·탭은 딥링크·무엇보다 절제. 사용자가 통제권을 쥔 환경에서 가치 있는 알림만 보내는 게 차단을 피하는 길이다.

한 줄 요약 — 알림=사용자 주의를 빌리는 일(남용→차단). 채널(Android 8+·종류별 사용자 통제·중요도)+NotificationCompat+PendingIntent(탭=딥링크)+스타일. Android 13+ POST_NOTIFICATIONS 런타임 권한. 푸시는 FCM: 토큰 발급·서버 등록·갱신(onNewTokenonMessageReceived·알림 vs 데이터 메시지(제어는 데이터)·토픽(일괄). 긴 처리는 WorkManager·절제 필수.

(출처: Android — Notifications overview / Notification runtime permission / Firebase Cloud Messaging / FCM docs.)

DataStore — 설정·키값 저장(SharedPreferences 대체)이미지·미디어 — Coil·Media3·CameraX