onDestroy: Android에서 Activity 작업 완료

저자: IT Sectr 게시일: 2026-03-04 읽는 시간: 8 분

onDestroy — Android에서 Activity와 Fragment의 생명주기 최종 메서드로, 컴포넌트가 완전히 소멸되기 전에 호출됩니다. onDestroy는 Activity 또는 Fragment가 작업을 종료하고 있음을 알립니다: 모든 리소스를 해제하고, 중첩된 프래그먼트를 소멸시키고, ViewModel을 정리해야 합니다. Google에 따르면, onDestroy는 Activity 종료의 100% 경우에 호출되지만, 프로세스 사망(process death) 시 시스템이 onDestroy 호출을 완전히 건너뛸 수 있습니다. onDestroy에 대한 Android 문서는 이 메서드가 비정상 종료 시 호출을 보장하지 않음을 강조합니다.

핵심 사항

  • onDestroy — Activity 또는 Fragment를 소멸시키기 전 마지막 호출로, 최종 리소스 정리를 위해 사용됩니다.
  • 시스템에 의한 프로세스 사망 시 onDestroy 호출이 보장되지 않습니다 — 중요 데이터 저장을 위해 이것에 의존하지 마세요.
  • onDestroy에서 백그라운드 작업을 취소하고, 소켓과 데이터베이스를 닫고, ViewModelStore를 정리해야 합니다.
  • onStop과의 차이점: onStop — 가시성 손실(Activity는 메모리에 유지), onDestroy — 완전한 소멸.
  • onDestroy의 isFinishing()은 Activity가 사용자 명령(finish()) 또는 시스템 결정으로 종료되는지 보여줍니다.

onDestroy: Android에서 무엇인가요?

onDestroy — Android가 Activity 또는 Fragment를 완전히 소멸시키기 전에 호출하는 콜백 메서드입니다. 이는 개발자가 리소스를 해제하고, 백그라운드 작업을 취소하고, 데이터 작업을 완료할 수 있는 마지막 기회입니다. onDestroy 실행 후, Activity/Fragment 인스턴스는 가비지 컬렉션(GC) 대상으로 표시되며 더 이상 사용할 수 없습니다.

onDestroy 호출 이유:

  • 명시적 finish() 호출 — 사용자가 “뒤로”를 누르거나 개발자가 finishActivity()를 호출했습니다.
  • 화면 회전 — Activity가 소멸되고 새 구성으로 다시 생성됩니다.
  • 구성 변경 — 키보드, 언어 변경, 화면 크기 변경(멀티 윈도우).
  • 시스템 결정 — Android가 리소스를 확보하기 위해 Activity를 종료합니다(그러나 onDestroy가 호출되지 않을 수 있음).

Google Android Vitals 통계(2025)에 따르면, 모든 Activity 소멸 사례의 약 12%가 화면 회전으로 인해, 65%가 finish()로 인해, 23%가 구성 변경으로 인해 발생합니다. onDestroy가 건너뛰어지는 프로세스 사망의 비율은 RAM이 적은 기기(4GB 미만)에 따라 약 5–8%입니다.

onDestroy가 호출되는 경우와 호출되지 않는 경우

onDestroy는 대부분의 표준 시나리오에서 호출됩니다, 그러나 개발자가 고려해야 할 중요한 예외가 있습니다. onDestroy 호출의 보장을 이해하는 것은 애플리케이션 아키텍처에 중요하며, 특히 데이터 저장 및 WorkManager 작업 취소에 있어서 중요합니다.

onDestroy가 호출되는 경우:

  • 사용자가 “뒤로” 버튼을 누름 — Activity.finish() → onPause → onStop → onDestroy.
  • 화면 회전 — Activity가 소멸됨(onPause → onStop → onDestroy), 그런 다음 다시 생성됨.
  • 구성 변경 — Activity 재생성이 필요한 시스템 설정.
  • finishAffinity() 호출 — 스택의 모든 Activities 종료.
  • FragmentManager에서 Fragment 제거 — Fragment가 수신: onPause → onStop → onDestroyView → onDestroy → onDetach.

onDestroy가 호출되지 않는 경우:

  • 시스템에 의한 프로세스 사망 — 메모리가 부족할 때 Android가 전체 애플리케이션 프로세스를 종료합니다. 프로세스가 Linux 커널 수준에서 종료되므로 Activity는 onDestroy를 받지 못합니다.
  • 비정상 종료 — 메인 스레드에서 처리되지 않은 예외가 onDestroy 호출 없이 애플리케이션을 종료합니다.
  • 강제 중지 — 사용자가 설정에서 애플리케이션을 강제로 중지합니다.

onDestroy 호출 보장 부족으로 인해 Google은 다음을 권장합니다: 중요 데이터 저장을 위해 onDestroy에 절대 의존하지 마십시오. onSaveInstanceState(), WorkManager 또는 자동 저장 기능이 있는 Room을 사용하세요. onDestroy는 리소스 해제를 위한 것이며, 지속성(persistence)을 위한 것이 아닙니다.

Activity와 Fragment의 onDestroy: 공통점과 차이점

onDestroy는 Activity와 Fragment 모두에 존재합니다, 그러나 다른 계약을 가지고 있습니다. Fragment의 생명주기는 더 상세합니다: onDestroy 외에도 onDestroyView(View 계층 구조 소멸)와 onDetach(Activity에서 분리)가 있습니다.

컴포넌트소멸 메서드순서ViewModel 생존
ActivityonDestroyonPause → onStop → onDestroy아니요(ViewModelStore가 저장되지 않은 경우에만)
FragmentonDestroyView, onDestroy, onDetachonPause → onStop → onDestroyView → onDestroy → onDetach예(Fragment가 제거되지 않은 경우)

주요 차이점: Fragment의 View는 Fragment 자체보다 더 자주 다시 생성됩니다. 화면 회전 중에 Fragment는 onDestroyView(View 소멸)를 거치지만, Fragment 자체와 ViewModel은 살아 있습니다. onDestroyView는 메모리 누수를 방지하기 위해 View 참조를 정리하는 올바른 장소입니다. Fragment의 onDestroy는 Activity의 onDestroy와 유사하며, Fragment가 완전히 제거될 때 호출됩니다.

하위 프래그먼트는 상위 Fragment의 onDestroy 전에 소멸됩니다. Activity에서 하위 프래그먼트는 상위 Activity의 onDestroy가 호출될 때 onDestroy를 받습니다. 순서가 보장됩니다: 프래그먼트는 포함하는 Activity보다 먼저 종료됩니다.

onDestroy에서 해야 할 일: 정리 체크리스트

onDestroy는 Activity 또는 Fragment보다 오래 살아서는 안 되는 모든 리소스를 해제하기 위한 것입니다. 돌아올 때까지 리소스를 해제하는 onStop과 달리, onDestroy는 최종 정리를 수행합니다.

onDestroy에서 필수 작업 체크리스트:

  • 코루틴 및 Flow 취소 — viewModelScope에 바인딩되지 않은 작업을 취소합니다. viewModelScope는 자동으로 취소되지만, lifecycleScope는 Activity 생명주기에 바인딩됩니다.
  • 소켓 및 채널 닫기 — WebSocket(OkHttp), BluetoothSocket, ServerSocket. 소멸 후 열어두면 시스템 리소스 누수입니다.
  • 파일 및 스트림 닫기 — FileInputStream, FileOutputStream, Cursor. Cursor는 닫지 않으면 ContentProvider에서 ANR을 일으킬 수 있습니다.
  • ContentObserver 구독 취소 — Activity가 콘텐츠 변경(연락처, 미디어 라이브러리)을 감시하는 경우.
  • BroadcastReceiver 등록 취소 — 동적으로 등록된 리시버는 취소되어야 합니다.
  • 데이터베이스 닫기 — Room은 Application이 소멸될 때 자동으로 연결을 닫지만, 직접 SQLiteDatabase는 수동 close()가 필요합니다.

onDestroy에서 하지 말아야 할 일: onDestroy에서 데이터를 저장하지 마세요 — onPause 또는 onSaveInstanceState를 사용하세요. 새 Service 또는 WorkManager 작업을 시작하지 마세요 — Activity가 소멸되고 결과를 추적할 수 없습니다. UI 업데이트를 시도하지 마세요 — View 계층 구조가 이미 소멸되었거나 소멸 과정에 있습니다. findViewById()를 호출하면 null이 반환됩니다.

onDestroy와 ViewModel: 함께 작업하기

ViewModel은 화면 회전 중에 Activity의 onDestroy에서 살아남도록 설계되었습니다, 그러나 finish() 시에는 Activity와 함께 소멸됩니다. 이 비대칭적 동작은 개발자들 사이에서 혼란의 주요 원인입니다.

화면 회전 중:

  • Activity: onPause → onStop → onDestroy(Activity 소멸).
  • ViewModel: 소멸되지 않음 — ViewModelStore가 저장되고 새 Activity로 전달됩니다.
  • 새 Activity: onCreate → onStart → onResume, 동일한 ViewModel을 받습니다.

finish() 중(사용자가 “뒤로”를 누름):

  • Activity: onPause → onStop → onDestroy.
  • ViewModel: onCleared() — Activity의 onDestroy 후에 호출됩니다.
  • 모든 viewModelScope 코루틴이 자동으로 취소됩니다.

따라서 onDestroy에서 viewModelScope를 취소할 필요가 없습니다 — ViewModel이 자체적으로 수행합니다. lifecycleScope(ViewModel이 아닌 Activity에 바인딩됨)를 사용하는 경우, onDestroy에서 lifecycleScope.cancel()을 통해 취소하거나 Job을 수동으로 관리하세요.

Kotlin에서 onDestroy를 사용한 코드 예제

예제 1: lifecycleScope 코루틴 취소가 있는 onDestroy Activity

Activity에서 올바른 lifecycleScope 관리를 보여줍니다: 네트워크 상태를 모니터링하기 위해 코루틴이 시작되고 onDestroy에서 취소됩니다.

kotlin
class NetworkMonitorActivity : AppCompatActivity() {
    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            Log.d("NetworkMonitor", "네트워크 사용 가능")
        }
        override fun onLost(network: Network) {
            Log.d("NetworkMonitor", "네트워크 연결 끊김")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_network)
        val connectivityManager = getSystemService(ConnectivityManager::class.java)
        connectivityManager.registerDefaultNetworkCallback(networkCallback)
        lifecycleScope.launch {
            Log.d("NetworkMonitor", "네트워크 모니터링 시작됨")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        val connectivityManager = getSystemService(ConnectivityManager::class.java)
        connectivityManager.unregisterNetworkCallback(networkCallback)
        Log.d("NetworkMonitor", "onDestroy: 콜백이 취소됨")
    }
}

onDestroy에서 네트워크 콜백 등록이 취소됩니다. lifecycleScope는 생명주기가 소멸될 때 자동으로 취소됩니다 — 별도의 코루틴 취소가 필요하지 않습니다. 네트워크 콜백은 반드시 등록 취소해야 합니다. 그렇지 않으면 Activity가 소멸된 후에도 시스템에 남아 있습니다.

예제 2: View 참조 정리가 있는 onDestroy Fragment

Fragment는 onDestroyView에서 View 참조를 올바르게 정리하여 클로저로 인한 메모리 누수를 방지합니다.

kotlin
class ProfileFragment : Fragment() {
    private var avatarView: ImageView? = null
    private var progressBar: ProgressBar? = null
    private val imageLoader = ImageLoader()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        avatarView = view.findViewById(R.id.avatar)
        progressBar = view.findViewById(R.id.progress)
        loadProfile()
    }

    private fun loadProfile() {
        viewLifecycleOwner.lifecycleScope.launch {
            try {
                progressBar?.visibility = View.VISIBLE
                val bitmap = imageLoader.load("https://example.com/avatar.png")
                avatarView?.setImageBitmap(bitmap)
            } finally {
                progressBar?.visibility = View.GONE
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        avatarView = null
        progressBar = null
        imageLoader.cancel()
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("ProfileFragment", "onDestroy: Fragment가 완전히 소멸됨")
    }
}

onDestroyView에서 View 참조가 null로 설정됩니다 — 이는 imageLoader의 클로저가 avatarView에 대한 참조를 보유하는 경우 메모리 누수를 방지합니다. Fragment 자체와 ViewModel은 onDestroy까지 살아 있습니다. imageLoader.cancel()은 Fragment가 화면을 떠날 때 로딩을 취소합니다.

예제 3: onDestroy에서 isFinishing 확인

isFinishing()을 사용하면 Activity가 사용자 명령으로 종료되는지 아니면 재생성을 위해 종료되는지 구별할 수 있습니다.

kotlin
class AnalyticsActivity : AppCompatActivity() {
    private val analytics = Analytics()

    override fun onDestroy() {
        if (isFinishing) {
            Log.d("AnalyticsActivity", "finish()로 Activity 종료 — 분석 전송 중")
            analytics.sendSessionEnd()
        } else {
            Log.d("AnalyticsActivity", "Activity 재생성 중(회전/구성) — 분석을 전송하지 않음")
        }
        super.onDestroy()
    }
}

isFinishing() 확인은 분석, 로깅 및 세션 데이터 정리를 위한 중요한 패턴입니다. 회전 중에는 세션 종료 이벤트를 보내면 안 됩니다 — 사용자가 여전히 애플리케이션으로 작업하고 있습니다. Google Analytics에 따르면, 잘못된 isFinishing() 확인은 40%의 잘못된 세션 이벤트 원인입니다.

자주 묻는 질문

onDestroy가 호출되지 않을 수 있나요?

네, 그럴 수 있습니다 — 시스템에 의한 프로세스 사망, 사용자에 의한 강제 중지 또는 비정상 종료 시. Google에 따르면, 약 5–8%의 Activity 종료가 onDestroy 호출 없이 발생합니다. 개발자는 중요 데이터 저장을 위해 onDestroy에 의존해서는 안 됩니다 — onPause 또는 onSaveInstanceState를 사용하세요.

onDestroy와 finish()의 차이점은 무엇인가요?

finish() — Activity 소멸을 시작하는 호출입니다. onDestroy — finish() 실행 중에 호출되는 콜백입니다. 정상 종료 시 onDestroy가 호출되려면 finish()가 필요합니다. finish()는 시스템이나 개발자에 의해 호출될 수 있으며, onDestroy는 시스템 콜백일 뿐입니다.

Fragment에서 super.onDestroy()를 호출해야 하나요?

네, 반드시 Activity와 Fragment 모두에서. super.onDestroy()는 ChildFragmentManager, LoaderManager 및 기타 시스템 컴포넌트의 적절한 정리를 보장합니다. super.onDestroy()를 건너뛰면 메모리 누수 및 프래그먼트 복원 버그가 발생합니다.

onDestroy에 상대적인 ViewModel의 onCleared()는 언제 호출되나요?

onCleared()는 Activity 또는 Fragment의 onDestroy 후에 호출됩니다, ViewModel이 더 이상 필요하지 않을 때입니다. 화면 회전 중에는 onCleared()가 호출되지 않습니다 — ViewModel이 onDestroy에서 살아남습니다. 순서: Activity/Fragment의 onDestroy → (ViewModelStore 정리) → onCleared().

onDestroy에서 Service를 시작할 수 있나요?

기술적으로는 가능하지만 권장되지 않습니다. Activity는 onDestroy 직후에 소멸되며, 시작된 Service는 통제 불능 상태가 됩니다. 백그라운드 작업에는 지연이 있는 WorkManager를 사용하세요: WorkManager는 Activity 종료 후에도 실행을 보장하고 프로세스 사망에서 살아남습니다.

요약

  • onDestroy — Activity와 Fragment의 최종 생명주기 콜백으로, 컴포넌트의 완전한 소멸 전에 호출됩니다.
  • onDestroy 호출은 프로세스 사망 시 보장되지 않습니다 — 약 5–8%의 종료가 그것 없이 발생합니다.
  • onDestroy에서 해제해야 할 것: 네트워크 콜백, 소켓, 파일 스트림, BroadcastReceiver, ContentObserver.
  • ViewModel.onCleared()는 Activity의 onDestroy 후에 호출됩니다 — viewModelScope가 자동으로 취소됩니다.
  • Fragment의 onDestroyView(onDestroy와 별개) — View 참조를 null로 설정하는 올바른 장소입니다.
  • onDestroy에서 isFinishing() 확인은 finish() 종료와 구성 변경으로 인한 재생성을 구별할 수 있게 합니다.
  • 데이터 저장을 위해 onDestroy에 의존하지 마세요 — onPause 또는 onSaveInstanceState를 사용하세요.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기