Fragment 생명주기
Fragment 생명주기
Fragment는 호스트 Activity의 생명주기와 밀접하게 연결되어 있으면서도 독립적인 자체 생명주기(lifecycle)를 갖습니다. Fragment는 생성, 뷰 생성, 가시성 확보, 인터랙티브 상태, 소멸이라는 단계를 거치며, 각 단계마다 전용 콜백이 제공됩니다. 특히 Fragment 자체의 생명주기와 Fragment 뷰의 생명주기가 분리되어 있다는 점은 안드로이드 개발에서 가장 오해가 많은 부분 중 하나이며, 면접에서도 자주 출제되는 핵심 주제입니다. 이 둘의 차이를 명확히 이해하지 못하면 메모리 누수나 크래시가 발생하기 쉬우므로, 반드시 정확하게 파악하고 계셔야 합니다.
이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.
onAttach()부터onDetach()까지Fragment생명주기 콜백의 전체 흐름을 추적할 수 있습니다.Fragment생명주기와Fragment뷰 생명주기의 차이를 명확하게 구분할 수 있습니다.onDestroyView()에서 뷰 참조를 반드시 해제해야 하는 이유를 설명할 수 있습니다.- 데이터 초기화와 UI 초기화에 각각 적합한 콜백을 식별할 수 있습니다.
viewLifecycleOwner가 뷰 생명주기에 맞춰 관찰(observation)을 스코핑하는 원리를 설명할 수 있습니다.
연결(Attachment)과 생성
Fragment가 FragmentManager에 추가되면 가장 먼저 onAttach()가 호출됩니다. 이 시점에서 Fragment는 requireActivity()를 통해 호스트 Activity에 대한 참조를 갖게 되며, Activity의 Context에도 접근할 수 있습니다. 호스트와 상호작용할 수 있는 가장 이른 시점이 바로 여기입니다.
이어서 onCreate()가 호출되며, 여기서는 뷰 재생성(view recreation) 이후에도 유지되어야 하는 데이터를 초기화합니다. ViewModel 설정, Fragment의 arguments Bundle로부터 인자 처리, 저장된 상태 복원 등이 이 콜백에서 이루어집니다. 뷰가 아직 존재하지 않으므로, 이 시점에서는 어떠한 뷰 관련 작업도 수행하면 안 됩니다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val userId = requireArguments().getString("user_id")
viewModel.loadUser(userId) // ViewModel을 통한 데이터 로딩
}
Fragment 생성과 뷰 생성이 분리되어 있는 이유를 이해하는 것이 중요합니다. Fragment는 자신의 뷰보다 더 오래 살아남을 수 있기 때문입니다. Fragment가 백 스택(back stack)에 놓이면 시스템은 메모리를 확보하기 위해 뷰 계층을 소멸시키지만, Fragment 인스턴스 자체는 살려둡니다. Fragment의 ViewModel 역시 이 전환을 살아남아 메모리에 있는 모든 데이터를 유지합니다. 사용자가 다시 돌아오면 동일한 Fragment 인스턴스에서 onCreateView()가 재호출되며, ViewModel이 새 뷰를 채우는 데 필요한 데이터를 제공합니다. 면접에서 "왜 Fragment에서 뷰 생명주기가 별도로 존재하나요?"라는 질문이 나올 수 있으므로, 이 구조를 명확히 설명할 수 있어야 합니다.
뷰 생성과 인터랙션
onCreateView()는 Fragment의 레이아웃을 인플레이트하여 반환합니다. onViewCreated()는 그 직후에 호출되며, 뷰 바인딩, RecyclerView 어댑터 설정, ViewModel의 LiveData나 Flow 관찰, 클릭 리스너 등록 등을 수행하는 표준적인 위치입니다.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_user, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// viewLifecycleOwner를 사용하여 뷰 생명주기에 맞춘 Flow 수집
viewLifecycleOwner.lifecycleScope.launch {
viewModel.userState.collect { state ->
bindUserData(state)
}
}
}
생명주기 스코핑된 관찰에서 this(Fragment 자신) 대신 viewLifecycleOwner를 사용하는 것이 매우 중요합니다. Fragment의 생명주기는 뷰의 생명주기보다 길기 때문입니다. Fragment를 생명주기 소유자(lifecycle owner)로 사용하여 Flow를 수집하면, 뷰가 소멸된 이후(Fragment가 백 스택에 있을 때)에도 수집이 계속됩니다. 이 상태에서 수집기가 이미 존재하지 않는 뷰를 업데이트하려고 시도하면 크래시가 발생합니다. viewLifecycleOwner는 뷰 생명주기에 스코핑되어 있어, onDestroyView() 실행 시 자동으로 수집을 취소합니다.