Activity와 Fragment의 생명주기

aufcl4858·2025년 8월 24일
post-thumbnail

안드로이드 개발을 하다 보면 가장 자주 부딪히는 주제가 바로 Activity와 Fragment의 생명주기(Lifecycle)였다.
처음에는 “onCreate → onResume → onPause → onDestroy” 정도만 대충 외우고 넘어갔지만, 실제 프로젝트에서 메모리 누수, 잘못된 UI 참조, 화면 전환 시 뷰가 초기화되지 않는 문제 같은 걸 겪으면서 다시 깊게 공부하게 됐다.

이번 글에서는 Activity와 Fragment의 생명주기를 정리하면서, 공부 과정에서 정리했던 Q&A들을 함께 풀어보겠다.


Activity 생명주기

기본 흐름

Activity는 크게 아래 단계로 움직인다.

  • onCreate() : 최초 생성. 레이아웃 inflate, 데이터 초기화
  • onStart() : 화면이 보이기 시작
  • onResume() : 화면이 포그라운드에 올라와 사용자와 상호작용 가능한 상태
  • onPause() : 다른 액티비티가 올라오거나 전화가 오는 등, 포커스를 잃은 상태
  • onStop() : 더 이상 화면에 보이지 않음
  • onDestroy() : 완전히 종료

onPause() vs onStart()의 차이

  • onStart()는 화면이 사용자에게 보이기 시작하는 시점이었다. UI는 보이지만 아직 포커스를 가지지 않을 수 있다.
  • onPause()는 화면이 보일 수도 있지만 포커스를 잃은 상태였다. 따라서 동영상, 센서, GPS 같은 리소스를 멈추는 데 적합하다.

👉 정리하면

  • onStart() → 리소스 준비, UI 갱신
  • onPause() → 리소스 중단

Activity 전환 시 생명주기 변화

예시: Activity A → Activity B → 다시 A 복귀

[A 시작]
A: onCreate → onStart → onResume

[B 실행]
A: onPause → onStop (B가 전체화면일 경우)
B: onCreate → onStart → onResume

[B 종료 → A 복귀]
B: onPause → onStop → onDestroy
A: onRestart → onStart → onResume

👉 핵심은 A가 다시 돌아올 때 onRestart()가 호출된다는 점이다. A는 이미 메모리에 살아 있기 때문에 다시 onCreate부터 시작하지 않는다.


Fragment 생명주기

Fragment는 Activity와 유사하지만, 뷰(View) 관련 단계가 추가되었다.

  • onAttach() : Activity에 붙을 때
  • onCreate() : 인스턴스 초기화 (UI 없음)
  • onCreateView() : 레이아웃 inflate (UI 생성)
  • onViewCreated() : 뷰 초기화 (ViewBinding, RecyclerView 등)
  • onStart() / onResume() : 보이고 상호작용 가능
  • onPause() / onStop() : 포커스를 잃거나 화면에서 사라짐
  • onDestroyView() : 뷰가 제거될 때. 뷰 관련 리소스 해제
  • onDestroy() : Fragment 인스턴스 자체 종료
  • onDetach() : Activity에서 완전히 분리

onCreateView와 onDestroyView

  • onCreateView()는 Fragment의 UI를 만드는 곳이었다. LayoutInflater를 이용해 XML을 inflate해야 한다.
  • onDestroyView()는 Fragment의 UI가 사라질 때 호출된다. 이 시점에서 ViewBinding 등 뷰 참조를 반드시 해제해야 한다.

👉 만약 뷰 해제를 onDestroy에서 한다면, Fragment는 살아있는데 뷰는 이미 사라진 상황이 발생해 메모리 누수가 생긴다. 따라서 뷰 관련 리소스는 onDestroyView에서 해제해야 했다.

코드 예시 (ViewBinding)

class SampleFragment : Fragment() {
    private var _binding: FragmentSampleBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentSampleBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null // 메모리 누수 방지
    }
}

onCreate에서 inflate하면 안 되는 이유

Activity는 onCreate에서 setContentView()로 UI를 붙이는 게 정상이다.
하지만 Fragment의 onCreate는 아직 container 정보가 없기 때문에, inflate를 하면 문제가 생긴다.

또한 Fragment는 View가 여러 번 생성/파괴될 수 있는데, onCreate는 한 번만 호출된다. 따라서 UI 재생성 과정이 꼬이게 된다.

👉 결론: Fragment UI는 반드시 onCreateView에서 inflate해야 한다.


FragmentManager vs ChildFragmentManager

  • fragmentManager / parentFragmentManager : Activity(또는 부모 Fragment)에 직접 붙은 Fragment 관리
  • childFragmentManager : Fragment 내부에 또 다른 Fragment를 넣을 때 사용

즉, Activity → ParentFragment → ChildFragment 구조라면,

  • ParentFragment는 parentFragmentManager로 Activity에 붙는다.
  • ChildFragment는 childFragmentManager로 ParentFragment 안에 붙는다.

viewLifecycleOwner

Fragment는 인스턴스와 View 생명주기가 다르다. 따라서 LiveData나 Flow를 observe할 때는 this 대신 viewLifecycleOwner를 사용해야 한다.

viewModel.data.observe(viewLifecycleOwner) { value ->
    binding.textView.text = value
}

👉 viewLifecycleOwner는 뷰가 생성될 때 활성화되고, 뷰가 파괴될 때 종료된다.
👉 따라서 UI 관련 작업을 안전하게 처리할 수 있고, 메모리 누수도 막을 수 있었다.


마무리

정리해보면

  1. Activity는 보이는 시점(onStart), 상호작용 가능한 시점(onResume), 포커스를 잃는 시점(onPause), 화면에서 사라지는 시점(onStop)을 구분하는 것이 핵심이었다.
  2. Fragment는 인스턴스와 View 생명주기가 별개라는 점을 반드시 기억해야 했다. 뷰는 사라졌다가 다시 만들어질 수 있으므로, ViewBinding 해제는 onDestroyView에서, LiveData observe는 viewLifecycleOwner로 하는 게 정석이었다.

나 역시 처음에는 생명주기를 “암기”로만 접근했지만, “언제 보이고, 언제 포커스를 잃고, 언제 뷰가 없어지는가”라는 관점으로 접근했을 때 비로소 정리가 됐다.
생명주기는 안드로이드 앱의 안정성과 직결되기 때문에, 이 부분을 정확히 이해하면 앱 품질 관리가 훨씬 수월해진다.

profile
데브누누

0개의 댓글