안드로이드 서비스(Service) 컴포넌트란?

aufcl4858·2025년 8월 24일
post-thumbnail

안드로이드 개발하다 보면 백그라운드에서 뭔가 계속 돌려야 할 일이 있다. 음악 재생이나 파일 다운로드처럼 말이다. 이런 때 사용하는 게 바로 Service다. 오늘은 안드로이드 Service에 대해 정리해봤다.

Service란?

Service는 사용자 인터페이스 없이 백그라운드에서 장시간 실행되는 작업을 처리하는 안드로이드 컴포넌트다. 쉽게 말해서 "화면에는 안 보이지만 뒤에서 열심히 일하는 친구"라고 생각하면 된다.

대표적인 예가 음악 플레이어다. 음악 앱에서 노래를 틀고 다른 앱으로 이동해도 음악이 계속 재생되지 않나? 그게 바로 Service가 하는 일이다.

Service의 종류

안드로이드 Service는 크게 세 가지로 나눌 수 있다.

1. Started Service (시작된 서비스)

startService()로 시작하면 독립적으로 실행되는 서비스다. 한 번 시작하면 작업이 끝날 때까지 계속 돌아간다.

언제 사용하나?

  • 파일 다운로드/업로드
  • 데이터 백업
  • 푸시 알림 처리
// 파일 다운로드 시작
val downloadIntent = Intent(this, DownloadService::class.java)
downloadIntent.putExtra("file_url", "https://example.com/file.zip")
startService(downloadIntent)

2. Bound Service (바인드된 서비스)

bindService()로 클라이언트가 연결해서 직접 통신할 수 있는 서비스다. 클라이언트가 없으면 자동으로 종료된다.

언제 사용하나?

  • 음악 플레이어 제어 (재생/일시정지)
  • 데이터베이스 액세스
  • 실시간 위치 추적
// 음악 서비스와 연결해서 직접 제어
private val connection = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName, service: IBinder) {
        val binder = service as MusicPlayerService.MusicBinder
        musicService = binder.getService()
        musicService?.play() // 직접 메소드 호출 가능!
    }
}

3. Foreground Service (포그라운드 서비스)

사용자에게 알림을 보여주면서 실행되는 서비스다. 시스템이 강제로 종료하기 어려워서 안정적으로 돌아간다.

언제 사용하나?

  • 음악 재생
  • GPS 추적
  • 대용량 파일 처리

Started vs Bound, 언제 뭘 써야 할까?

이 부분이 처음에 좀 헷갈렸다. 간단하게 정리하면

Started Service를 쓸 때

  • "시작하고 잊어버려도 되는" 작업
  • 클라이언트와 실시간으로 소통할 필요 없는 경우
  • 예: 파일 다운로드, 데이터 동기화

Bound Service를 쓸 때

  • 클라이언트와 실시간 상호작용이 필요한 경우
  • 서비스의 메소드를 직접 호출해야 하는 경우
  • 예: 음악 플레이어 제어, 복잡한 데이터 처리

기억하기 쉽게 "제어가 필요하면 Bound, 독립적인 작업이면 Started"라고 외우면 될 것 같다.

포그라운드 서비스 구현하기

포그라운드 서비스는 좀 특별하다. 반드시 알림을 보여줘야 하고, 5초 이내에 startForeground()를 호출해야 한다.

class MusicPlayerService : Service() {
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 🔥 핵심: 5초 이내에 이걸 호출해야 함!
        startForeground(NOTIFICATION_ID, createNotification())
        
        // 실제 작업 수행
        performBackgroundWork()
        
        return START_STICKY
    }
    
    private fun createNotification(): Notification {
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("음악 재생 중")
            .setContentText("현재 재생: 노래 제목")
            .setSmallIcon(R.drawable.ic_music_note)
            .addAction(R.drawable.ic_pause, "일시정지", pausePendingIntent)
            .setOngoing(true) // 사용자가 스와이프로 제거 못하게
            .build()
    }
}

AndroidManifest.xml에도 권한 추가하는 거 잊지 말자

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

<service 
    android:name=".MusicPlayerService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="false" />

Service 생명주기

Service도 Activity처럼 생명주기가 있다. 유형에 따라 조금씩 다르지만 기본적인 흐름은 이렇다

Started Service 생명주기

onCreate() → onStartCommand() → [실행 중] → onDestroy()

Bound Service 생명주기

onCreate() → onBind() → [연결됨] → onUnbind() → onDestroy()

중요한 건 onCreate()는 서비스가 처음 생성될 때 한 번만 호출되고, onStartCommand()startService()를 호출할 때마다 실행된다는 거다.

그리고 onDestroy()에서는 반드시 리소스 해제해야 한다! 안 그러면 메모리 누수 생긴다.

override fun onDestroy() {
    super.onDestroy()
    // 반드시 리소스 해제!
    mediaPlayer?.release()
    wakeLock?.release()
}

Service 사용할 때 주의사항

실제로 써보면서 느낀 주의사항들이다

1. 꼭 필요할 때만 사용하자

안드로이드 8.0부터 백그라운드 서비스 제한이 심해졌다. 정말 필요한 경우가 아니라면 WorkManager나 JobScheduler 같은 대안을 고려해보자.

2. 작업 완료되면 반드시 종료

// 작업 완료 후 꼭 이거 호출하자
stopSelf()

3. 포그라운드 서비스는 투명하게

사용자가 무슨 일이 일어나고 있는지 알 수 있게 명확한 알림을 제공하자. "백그라운드에서 실행 중"이라고만 하지 말고 구체적으로 설명해주자.

4. 배터리 최적화 고려

무한 루프 돌리지 말고, 적절한 간격으로 작업하자:

// ❌ 이렇게 하지 말자
while(true) { 
    checkForUpdates()
    Thread.sleep(1000) 
}

// ✅ 이렇게 하자
handler.postDelayed(periodicTask, 5 * 60 * 1000) // 5분 간격

정리

Service는 백그라운드 작업의 핵심이지만, 최신 안드로이드에서는 제약이 많아졌다. 꼭 필요한 경우에만 사용하고, 사용할 때는

  • 즉시 실행이 필요없는 작업: WorkManager 사용
  • 사용자가 인지해야 하는 장시간 작업: Foreground Service
  • 실시간 상호작용이 필요한 작업: Bound Service
  • 단발성 백그라운드 작업: Started Service

이렇게 구분해서 사용하면 될 것 같다.

Service 잘 활용하면 사용자 경험이 확실히 좋아지는데, 잘못 쓰면 배터리 킬러가 되니까 신중하게 사용하자! 😅

profile
데브누누

0개의 댓글