private var _binding: FragmentXBinding? = null + private val binding get() = _binding!!
이 조합은 개발자의 생명을 구합니다.
반면 lateinit var binding 은 조용히 당신의 메모리를 잡아먹습니다.
밤 11시.
개발자는 평화롭게 앱을 종료했다.
하지만 Logcat에는 여전히 붉은 메시지가 깜빡인다.
“Memory leak detected: Fragment binding is still referenced after onDestroyView()”
그렇다.
우리의 lateinit var binding은 이미 죽은 뷰의 시체를 품고 있었다.
사건의 전말은 단순했다.
private lateinit var binding: FragmentArtistBinding
override fun onCreateView(...) : View {
binding = FragmentArtistBinding.inflate(inflater, container, false)
return binding.root
}
보기엔 깔끔하다.
lateinit — Kotlin의 마법 같은 단어.
“나 나중에 초기화할게, 지금은 그냥 믿어줘.”
문제는 Fragment의 생명주기다.
onDestroyView() 가 호출되어도 binding은 여전히 메모리에 남아,
사라진 뷰를 기억하는 망령이 된다.
이게 바로 “뷰 참조 누수(View leak)”이다.
구글 공식 문서는 이런 유령 사태를 방지하기 위해 백업 변수(backing property) 패턴을 소개한다.
private var _binding: FragmentArtistBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentArtistBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
이 한 줄이 바로 “구글이 승인한 엑소시즘”이다.
onDestroyView() 에서 _binding = null
즉, 망령을 보내버리는 구절.
이제 binding은 살아 있을 때만 non-null,
죽으면 확실히 null이 된다.
이게 바로 “메모리 누수 제로 라이프스타일”.
이쯤 되면 헷갈린다.
“아니, DataBindingUtil.inflate()랑 그냥 Binding.inflate()는 뭐가 달라요?”
이건 가톨릭과 개신교 정도의 차이다.
둘 다 신(=View)을 믿지만, 예배 방식이 다르다.
| 항목 | DataBindingUtil.inflate() | Binding.inflate() |
|---|---|---|
| 대상 | XML에 <layout> 태그와 @{} 표현식이 있을 때 | 단순 뷰 참조 바인딩 |
| 사용 목적 | 뷰모델 연결, 표현식 바인딩 가능 | 단순 findViewById 대체 |
| 난이도 | 약간 복잡 | 매우 단순 |
| 코드 스타일 | binding.viewModel = viewModel | binding.textView.text = "Hello" |
즉,
데이터바인딩은 계약,
뷰바인딩은 조작에 가깝다.
Stack Overflow에는 이런 명언이 있다.
“lateinit binding in Fragment is like keeping your ex’s photo in your wallet —
it will haunt you when you least expect it.”
따라서 프래그먼트에서는 lateinit을 버리고,
nullable backing property 패턴을 택해야 한다.
Fragment + ViewBinding ⇒ _binding? + binding get() = _binding!!
Activity + ViewBinding ⇒ lateinit binding 도 OK (Activity는 뷰 생명주기가 단순함)
DataBinding ⇒ 필요할 때만 사용 (무조건 쓰면 XML이 점점 주술서가 됨)

프래그먼트에서 메모리 누수를 막는 건
단순히 _binding = null 한 줄이 아니다.
그건 개발자와 뷰 사이의 관계를 정리하는 선언문이다.
뷰가 떠났으면, 깔끔히 보내줘야 한다.