[Android] 무슨 일이 있어도 BLE 통신이 살아있게 해주세요

유진·2025년 8월 8일

Android

목록 보기
7/17
post-thumbnail

안드로이드의 4대 컴포넌트는 다음과 같다.

  • Activity
  • Service
  • BroadcastReceiver
  • ContentProvider

이 중 Service는 화면이 없는 컴포넌트다.
사용자와 직접 상호작용하지 않지만, 앱의 “뒤에서 돌아가야 하는 작업”을 담당한다.

이 글에서는 “무슨 일이 있어도 BLE 통신이 살아 있어야 한다”라는 실제 개발 요구사항을 통해
Service, 특히 Foreground Service가 왜 필요했는지를 정리해본다.


개발 요구사항: 무슨 일이 있어도 BLE 통신이 살아있게 해주세요

Service를 처음 제대로 다뤄본 계기는 스타트업 인턴 시절 받은 첫 번째 과제였다.

요구사항은 대략 다음과 같았다.

해외 전시회에서 회사의 전기자전거용 속도계를 시연해야 한다.
실제 전기자전거를 해외로 가져갈 수 없으니,
안드로이드 앱에서 전기자전거 신호를 흉내 내어
BLE 통신을 보내는 앱을 만들어라.

단, 부스 운영 중에는 앱이 백그라운드로 가도
절대 꺼지면 안 된다.

처음 들었을 때는 다소 추상적인 요구처럼 들렸지만, 이를 개발 용어로 정리하면 훨씬 명확해진다.


요구사항을 개발 용어로 풀어보면

위 요구사항을 정리하면 다음과 같다.

기기를 잠그거나 앱이 포커싱되지 않은 상태에서도
BLE subscriber에게 notification이 계속 전달되어야 한다.

여기서 중요한 포인트는 두 가지다.

1. BLE notification

  • 앱이 임베디드 장비(자전거 속도계 프로토타입)에게
    주기적으로 신호를 보내는 행위
  • BLE GATT 통신에서의 Notify

2. 포커싱이 없는 상태

  • Activity 기준으로 onStop() 상태
  • 즉, 화면에 보이지 않는 상태
  • onDestroy()만 아니라면 계속 BLE 통신이 유지되어야 함

이 시점에서 Activity만으로는 요구사항을 만족할 수 없다는 것이 분명해진다.


왜 Activity로는 안 되는가

Activity는 사용자 인터페이스를 담당하는 컴포넌트다.

  • 화면이 가려지면 onStop()
  • 시스템 상황에 따라 언제든 종료될 수 있음
  • 백그라운드 장시간 작업에 부적합

즉, “좀비처럼 살아 있어야 하는 작업”을 Activity에 두는 것은 구조적으로 잘못된 선택이다.

이때 등장하는 컴포넌트가 바로 Service다.


Service란 무엇인가

Service는 다음과 같은 특징을 가진다.

  • 화면이 없음
  • 백그라운드에서 작업 수행 가능
  • Activity와 독립적인 생명주기

하지만 여기서 한 가지 더 중요한 사실이 있다.

일반 Service는 백그라운드에서 오래 살아남지 못한다.

안드로이드는 배터리와 리소스를 보호하기 위해
백그라운드에서 오래 실행되는 작업을 적극적으로 종료한다.

그래서 BLE처럼 “지속적이고 중요한 작업”에는 Foreground Service가 필요하다.


좀비처럼 살아있게 하기: Foreground Service

Foreground Service는 사용자에게 명시적으로 알리고, 시스템에게도 중요하다고 선언하는 Service다.

  • 알림(Notification)을 반드시 표시해야 함
  • 시스템이 쉽게 종료하지 않음
  • BLE, 음악 재생, 위치 추적 등에 사용됨

이를 위해 사용되는 핵심 API가 startForeground()다.


Foreground Service 구현

BLE 통신을 담당하는 Service는 다음과 같이 구현했다.

class BluetoothService(
    context: Context,
) : Service() {

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification = createNotification()
        startForeground(1, notification)

        return START_STICKY
    }
}

핵심 포인트 정리

  • startForeground()
    → 이 Service를 Foreground Service로 승격
  • Notification 필수
    → 사용자에게 “이 작업이 실행 중”임을 알림
  • START_STICKY
    → 시스템이 Service를 종료하더라도
    가능한 한 다시 재시작

이 설정 덕분에,

  • 화면이 꺼져도
  • 앱이 백그라운드에 있어도
  • 포커싱이 없어도

BLE 통신은 계속 유지될 수 있었다.


Manifest 등록도 필수

Foreground Service는 Manifest에도 명시해야 한다.

<service
    android:name=".bluetooth.BluetoothService"
    android:foregroundServiceType="dataSync" />
  • foregroundServiceType
    → 서비스의 목적을 시스템에 명시
  • BLE 통신은 dataSync 유형에 해당

이 설정이 없으면 Android 9(API 28) 이상에서 정상 동작하지 않는다.


정리하며

이 경험을 통해 Service를 이렇게 정리하게 되었다.

  • Activity는 화면을 위한 컴포넌트다.
  • 백그라운드에서 “계속 살아 있어야 하는 작업”은 Service의 책임이다.
  • 단순 Service로는 부족하고, 사용자와 시스템 모두에게 중요함을 알리는 Foreground Service가 필요할 때가 있다.

Service는 단순히 “백그라운드 작업용” 컴포넌트가 아니라, 안드로이드 시스템과 협상하는 방식에 가깝다.

BLE 통신 요구사항 하나를 만족시키기 위해
Service, Foreground Service, 생명주기를 함께 고민했던 경험은
이후 안드로이드 구조를 이해하는 데 큰 도움이 되었다.


작성한 코드

BluetoothService 전체 코드

profile
안드로이드... 좋아하세요?

0개의 댓글