Fragment는 생명주기가 두 개다 — Fragment 자신과, 그 Fragment가 가진 뷰.
왜 둘인가
A 화면(Fragment) 에서 B 로 이동하면
- A 의 뷰는 파괴된다 (메모리 절약)
- A 의 Fragment 객체는 백스택에 살아 있다
뒤로 가기로 A 로 돌아오면
- 같은 Fragment 에 뷰만 새로 만든다
- onDestroyView → (백스택) → onCreateView 재호출
Fragment 생명주기 onCreate ─────────────────────── onDestroy
뷰 생명주기 onCreateView ─ onDestroyView ─ onCreateView ─ ...
(여러 번 반복될 수 있다)
여기서 사고가 난다
// ❌ Fragment 자신(this)으로 관찰한다
viewModel.uiState.observe(this) { render(it) }
뷰는 파괴됐는데 Fragment 는 살아 있으니 콜백이 계속 불린다
- 이미 파괴된 옛 binding 을 건드려 크래시
- 또는 옛 뷰를 붙잡아 메모리 누수
백스택에 5개 화면이 쌓여 있으면 관찰자가 5개 동시에 살아 있다
// ✅ 뷰 생명주기에 묶는다
override fun onViewCreated(view: View, s: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { render(it) }
}
}
}
viewLifecycleOwner.lifecycleScope로 시작한 코루틴은 onDestroyView에서 자동 취소된다 — 파괴된 뷰를 건드릴 일이 없다.
binding 누수
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(...): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null // ★ 이걸 빠뜨리면 뷰 트리 전체가 누수된다
}
면접 함정
- ❌ "Fragment도 Activity처럼 생명주기가 하나" → 두 개이고, 뷰 쪽이 여러 번 반복된다.
- ❌ "단일 Activity 아키텍처면 이 문제가 없다" → Navigation의 목적지도 Fragment면 동일하다. Compose 목적지라면 사라진다.
콜백 전체 순서
onAttach → onCreate → onCreateView → onViewCreated → onStart → onResume
↓
onPause → onStop → onDestroyView → onDestroy → onDetach
백스택으로 뒤로 갈 때는 onDestroyView 까지만 간다
→ 돌아오면 onCreateView 부터 다시 시작한다
Fragment 간 결과 전달
// setTargetFragment 는 폐기됐다
setFragmentResult("pick", bundleOf("id" to 42L))
setFragmentResultListener("pick") { _, bundle ->
val id = bundle.getLong("id")
}
FragmentManager 가 중개하므로 두 Fragment 가 서로를 참조하지 않는다 결과는 수신 Fragment 가 STARTED 가 될 때 전달된다 (생명주기 안전)
재생성 시 인자만 살아남는다
// ✅ arguments 는 저장되고 복원된다
MyFragment().apply { arguments = bundleOf("id" to 42L) }
// ❌ 생성자나 세터로 넘긴 값은 재생성 시 사라진다
MyFragment(id = 42L) // 시스템이 기본 생성자로 다시 만든다 → 크래시 또는 유실
시스템이 Fragment 를 복원할 때는 항상 '빈 생성자' 로 만들고 arguments 를 다시 넣는다
- 인자는 반드시 arguments 로