안드로이드

Live Activity 우리도 있다! Android Live Update Notification(NowBar) 구현하기

서재원
서재원
안드로이드 개발자2026년 7월 13일4분 소요

안녕하세요, 타다에서 Android 개발을 하고 있는 서재원입니다.

이번에 타다 Android 앱에 Live Update Notification(Samsung Now Bar 연동) 기능이 추가되었습니다.

image

이번 기능 개발은 기획 문서 작성부터 구현까지 Android 개발자인 제가 도맡아 진행했는데요, 어떤 계기로 기능을 제안드렸고 어떻게 구현하였는지 그 과정을 공유드리려 합니다.

백그라운드에서도 지속된 탑승 경험 제공하기

타다를 이용하시는 많은 이용자들은 택시를 호출하고 탑승해서 내릴 때까지 타다 앱을 수시로 확인하게 됩니다. "기사님이 배정되었는지", "언제 택시가 호출 위치에 도착하는지", "언제쯤 목적지에 도착하는지" 같은 정보를 그때그때 살펴봐야 하기 때문입니다.

그런데 이용자는 그 시간 내내 앱을 켜둔 채로 두지는 않습니다. 결국 궁금한 상태가 생길 때마다 앱을 다시 열어 확인해야 하는 번거로움이 있었습니다.

타다 iOS 앱에는 이미 Live Activity와 Dynamic Island를 활용해 사용자가 백그라운드 상태에 가더라도 현재 탑승 정보를 제공하는 기능이 들어가 있습니다. 덕분에 이용자는 굳이 앱을 다시 켜지 않아도 탑승 상태를 인지하고, 필요하면 곧바로 앱으로 이동할 수 있죠.

image
"안드로이드에도 이런 기능이 없을까?"

⇒ 그때 떠올린 것이 갤럭시 유저들이라면 대부분 익숙하실 "Now Bar"였습니다.

Now Bar? Live Update?

출처: samsung.com
출처: samsung.com

Now Bar는 삼성이 One UI 7(2025년 1월 출시)부터 도입한 잠금화면용 인터랙티브 위젯입니다. 음악 재생, 타이머, 라이브 스포츠 스코어처럼 실시간으로 바뀌는 정보를 잠금화면에서 바로 보여주는 기능이죠.

제가 기존에 가지고 있던 지식으로는 "Now Bar에 띄우려면 삼성 전용 SDK가 필요한 건 아닐까?" 싶었습니다. 그렇게 생각할 수밖에 없었던 것이, One UI 7까지는 Samsung 자체 앱, Google Maps처럼 일부 지정된 앱만 Now Bar에 표시될 수 있었습니다.

출처: android-developers.googleblog.com
출처: android-developers.googleblog.com
출처: android-developers.googleblog.com

그런데 One UI 8(Android 16 기반)부터는 서드파티 앱에서도 Android 16의 LiveUpdate Notification API(Notification.ProgressStyle)만 적용하면 자동으로 Now Bar에 함께 표시되도록 열렸습니다.

별도의 삼성 전용 SDK 없이, 표준 Android API 하나로 일반 알림 영역과 Samsung Now Bar의 라이브 UX를 동시에 대응할 수 있게 된 셈입니다.

이 부분을 확인하고 해볼 만하다는 판단이 섰고, 도입 검토에 들어갔습니다.

Live Update Notification, 어떻게 구현하는가?

출처: developer.android.com
출처: developer.android.com

Live Update Notification은 기존 Notification과 달리 시간이 지남에 따라 상태가 변하는 사용자 작업(Live Session)을 위해 설계된 알림입니다. 예를 들면 이런 시나리오들이 대상이 됩니다.

  • 음식 배달 진행 상황
  • 운동 기록 (러닝 시간, 거리)
  • 차량 호출 (배차, 도착 예정 시간)

타다의 택시 호출 → 배차 → 도착 대기 → 탑승 → 도착 흐름에 잘 맞아떨어지는 유스케이스였습니다.

Live Update Notification이 일반 알림과 가장 다른 점은, 조건을 만족하면 시스템이 알림을 승격(promote)시켜 준다는 것입니다. 승격된 알림은 다음과 같은 특징을 보입니다.

출처: developer.android.com
출처: developer.android.com
  • 알림 창 상단에 더 눈에 띄게 노출
  • 잠금화면에서 별도로 강조
  • 상태 표시줄에는 작은 상태 칩(Status Chip) 형태로 표시
  • 카드는 기본적으로 펼쳐진 상태이며, 축소할 수 없음

승격을 위해 충족해야 하는 조건

공식 문서에 따르면 알림이 LiveUpdate로 간주되려면 다음 조건을 모두 만족해야 합니다.

image

조건이 많아 보이지만, 이를 실제 코드로 반영하면 다음과 같은 형태가 됩니다.

kotlin
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.POST_PROMOTED_NOTIFICATIONS" />

val notification = NotificationCompat.Builder(context, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_tada_live)
    .setContentTitle("기사님이 배차되었어요")        // 필수
    .setContentText("약 3분 뒤 도착 예정")
    .setStyle(
        NotificationCompat.ProgressStyle()           // 라이브 스타일
            .setProgress(60)
            .setProgressSegments(rideSegments)
            .setProgressTrackerIcon(
                IconCompat.createWithResource(context, R.drawable.ic_tada_marker)
            )
    )
    .setOngoing(true)                                // 필수
    .setOnlyAlertOnce(true)
    .setRequestPromotedOngoing(true)                 // 승격 요청
    .build()

여기서 핵심은 ProgressStyle입니다. 단순한 진행률 바가 아니라 단계별 세그먼트(segment)를 표현할 수 있어서, 타다의 "호출 → 배차 → 도착 대기 → 탑승 → 도착" 같은 다단계 흐름을 시각적으로 그대로 옮길 수 있었습니다.

(참고로 조건이 적지 않다 보니, 실제 작업에서는 알림 노출 시 사용하는 채널을 기존 알림 채널과 LiveUpdate 전용 채널을 분리하여 전용 알림 채널을 별도로 만들어 관리하였습니다)

내 알림이 실제로 승격될 수 있는지 확인하기

조건을 모두 충족했다고 해서 알림이 항상 LiveUpdate로 승격되는 건 아닙니다. 사용자가 설정에서 해당 앱의 라이브 업데이트를 꺼두었을 수도 있고, OEM(Samsung 등)이 자체 기준을 추가로 적용할 수도 있기 때문입니다.

이를 확인할 수 있도록 시스템이 몇 가지 API를 제공합니다.

API역할
Notification.FLAG_PROMOTED_ONGOING이 알림이 실제로 승격되었는지 여부
Notification.hasPromotableCharacteristics()알림 자체가 승격 가능한 형태인지

타다에서는 알림을 빌드한 직후 hasPromotableCharacteristics()로 내가 만든 알림 객체가 승격 자격을 갖췄는지 검증하는 과정을 거쳤습니다.

kotlin
if (!notification.hasPromotableCharacteristics()) {
    // 알림 승격 구성 자체에 문제 → 빌드 단계 점검 필요
}

상태 칩(Status Chip)

출처: developer.android.com
출처: developer.android.com

알림 카드가 화면에 펼쳐져 있지 않은 순간에도, Live Update Notification은 상태 표시줄에 작은 칩 형태로 라이브 세션이 계속 따라다닙니다. 사용자가 다른 앱을 쓰고 있어도 흘끗 보면 "지금 차가 어디쯤 오고 있구나"를 알 수 있죠.

칩은 다음 두 가지 API로 표현되는 정보를 표시합니다.

  • setShortCriticalText(...) — 7자 이내의 짧고 핵심적인 상태 텍스트 (예: 3분, 배차 완료)
  • setWhen(...) — 시간 기반 카운트다운 / 경과 시간 표현

칩의 텍스트 표시 규칙은 다음과 같습니다.

조건표시 방식
텍스트가 7자 미만전체 텍스트 표시
칩에 텍스트 절반 미만만 들어갈 때아이콘만 표시
칩에 텍스트 절반 이상 들어갈 때가능한 한 많은 텍스트 표시

칩의 최대 너비는 96dp로 고정되어 있어서, 텍스트가 너무 길면 시스템이 알아서 잘라내거나 아이콘으로 대체합니다. 그래서 타다는 단계별로 "배차 완료", "곧 도착", "탑승 중", "도착" 같이 7자 이내의 짧은 라벨을 별도로 정의해 칩에 노출되도록 했습니다.

기획서도 작성해보자

기술 검토가 끝나면 다음은 무엇을, 어떻게 보여줄지 정해야 합니다.

이미 존재하던 iOS Live Activity, Dynamic Island 디자인 피그마
이미 존재하던 iOS Live Activity, Dynamic Island 디자인 피그마

타다는 이미 iOS에서 Live Activity가 돌고 있던 덕분에, 단계별로 어떤 정보를 노출할지에 대한 레퍼런스 디자인이 Figma에 이미 갖춰져 있었습니다.

다만 Android의 LiveUpdate Notification은 iOS의 Live Activity와 표현 가능한 영역이 다르기 때문에, Android의 컴포넌트 제약에 맞춰 정책과 디자인을 다시 정리할 필요가 있었습니다.

기획서 작성 (w/ Claude Code)

앞 단락에서 정리한 것처럼 iOS Live Activity 디자인 레퍼런스가 이미 있었고, 안드로이드 쪽은 그 위에서 컴포넌트 제약을 다시 매핑하는 작업이 중심이었습니다. 디자인 골격이 잡혀 있다 보니, 이번에 한해서는 제가 직접 기획 제안 문서와 정책을 작성해서 팀에 제안하는 방식으로 진행해봤습니다. 이 단계에서 Claude Code를 함께 다음과 같은 작업들을 빠르게 진행했습니다.

  • iOS Live Activity Figma & iOS 코드 로직 분석 → 단계별 노출 정보 정리
  • Android LiveUpdate Notification 제약사항 매핑
  • 단계별 텍스트, 아이콘, 진행률 정의 → AC(Acceptance Criteria) 단위로 정리
  • 예외 상황 정책 (호출 취소, 결제 실패, 네트워크 단절 등)

아래는 실제 제안에 사용한 기획서 결과물입니다.

실제 제안 기획서
실제 제안 기획서

이렇게 정리된 정책 문서를 바탕으로 곧바로 프로토타입을 만들어 팀에 제안했습니다. 잠금화면 위에서 실제로 NowBar가 단계별로 갱신되는 모습을 직접 보여드리니, 의사결정이 훨씬 빨라졌고 그대로 PO분의 검토를 받고 정식 프로덕트 구현으로 이어졌습니다.

기획 제안에 사용한 프로토타입 피그마
기획 제안에 사용한 프로토타입 피그마

실제 구현 코드 살펴보기

앞서 언급했듯 LiveUpdate Notification은 setOngoing(true) 가 필수 조건이고, 라이드 세션이 끝날 때까지 알림이 살아 있어야 합니다. 그래서 타다에서는 이를 Foreground Service 위에서 돌리는 구조로 설계했습니다.

택시 호출이 시작되는 순간 Foreground Service를 띄우고, 서비스의 라이프사이클에 묶인 코루틴 스코프 위에서 RideStatus Flow를 구독합니다. 그리고 탑승이 끝나면 서비스가 스스로 정리되는 흐름이죠.

[탑승 세션 시작]

startForegroundService(LiveUpdateForegroundService)

서비스에서 LiveUpdateNotificationManager.start(lifecycleScope) 호출

RideStatus Flow 구독 → 상태 변경마다 알림 갱신

[탑승 세션 종료]

stopSelf() → 채널/알림 정리

알림 빌드 책임은 LiveUpdateNotificationManager 가, 라이프사이클 책임은 ForegroundService 갖는 식으로 역할을 분리했습니다.

kotlin
@AndroidEntryPoint
class LiveUpdateForegroundService : LifecycleService() {

    @Inject lateinit var manager: LiveUpdateNotificationManager

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)
        startForeground(NOTIFICATION_ID, manager.buildInitialNotification())
        manager.start(lifecycleScope)
        return START_STICKY
    }
}

이 구조 덕분에 앱 프로세스가 백그라운드에 있어도 라이브 세션이 끊기지 않고 유지되며, RideStatus 가 바뀔 때마다 알림이 자연스럽게 갱신됩니다.

상태 변화 → 알림 갱신

실제 알림을 만들고 갱신하는 LiveUpdateNotificationManager 의 핵심 로직만 간략히 보여드리면 다음과 같습니다.

kotlin
class LiveUpdateNotificationManager @Inject constructor(
    @ApplicationContext private val context: Context,
    private val notificationManager: NotificationManagerCompat,
    private val rideStatusObserver: RideStatusObserver,
) {

    fun start(scope: CoroutineScope) {
        rideStatusObserver.statusFlow
            .distinctUntilChanged()
            .onEach { status -> updateNotification(status) }
            .launchIn(scope)
    }

    private fun updateNotification(status: RideStatus) {
        val style = NotificationCompat.ProgressStyle()
            .setProgressSegments(status.toSegments())
            .setProgress(status.progress)
            .setProgressTrackerIcon(status.trackerIcon(context))

        val notification = NotificationCompat.Builder(context, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_tada_live)
            .setContentTitle(status.title)
            .setContentText(status.description)
            .setStyle(style)
            .setOngoing(true)
            .setOnlyAlertOnce(true)
            .build()

        notificationManager.notify(NOTIFICATION_ID, notification)
    }
}

RideStatus가 바뀔 때마다 ProgressStyle의 세그먼트와 진행률만 갱신해주면, 잠금화면의 NowBar와 알림 영역 모두에서 자연스럽게 단계 변화가 표현됩니다.

결과물

마치며

이번 작업으로 타다의 탑승 경험을 백그라운드까지 이어갈 수 있게 됐습니다. 그동안 앱을 열어야 보이던 정보가, 이제는 휴대폰을 쓰는 내내 잠금화면과 알림 영역에서 갱신됩니다.

개인적으로도 타다 Android 앱 이용자분들의 앱 사용 경험을 조금이나마 개선한 것 같아 뿌듯한 작업이었습니다.

혹시 Live Update Notification을 사용한 Now Bar 연동을 아직 모르셨다면, 이번 기회에 한번 적용해보시는 걸 추천드립니다.

출처

#Android#LiveUpdate#Nowbar#SamsungOneUI#Notification#Android16#타다
서재원
서재원안드로이드 개발자

사용자 경험을 중심하는 안드로이드 개발자입니다

이 글이 도움이 되었나요?