이번에 기능 구현을 할 때 개선한 코드들에 대해서 살펴보겠습니다.
class MainViewModel(
private val mapRepository: MapRepository,
private val resourcesProvider: ResourcesProvider
) : BaseViewModel() {
private val _locationData = MutableLiveData<MainState>(MainState.Uninitialized)
val locationData: LiveData<MainState> = _locationData
...
}
이전에 위치 정보를 MainViewModel에서 관리하였다가 다른 화면에서 위치 정보가 필요하지만 MainViewHolder에 접근할 수 없는 경우를 위해 따로 object를 생성하여 위치 정보를 관리하도록 변경했었습니다.
하지만 다시 생각해본 결과 위치 정보를 MainViewModel에서 관리하기로 하였습니다. 그 이유는 다른 화면에서 위치 정보가 필요한 경우 Intent 등으로 넘겨 줄 수 있고, MainActivity에서 위치 권한을 관리하기 때문에 MainViewModel에서 위치 정보를 관리하는 것이 옳다고 판단하였기 때문입니다.
Base 클래스들의 필요없는 type parameter, method, property를 삭제하였습니다.
// 변경 전
// VM type parameter 삭제 예정
abstract class BaseActivity<VM : BaseViewModel, VB : ViewBinding> : AppCompatActivity() {
abstract val viewModel: VM // viewModel을 가지지 않을 수도 있으므로 삭제 예정
...
private lateinit var fetchJob: Job // 삭제 예정
...
open fun initState() {
initViews()
fetchJob = viewModel.fetchData() // 삭제 예정
observeData()
}
...
// fetchJob이 항상 있다는 보장이 없으므로 삭제 예정
override fun onDestroy() {
if (fetchJob.isActive) {
fetchJob.cancel()
}
super.onDestroy()
}
}
위는 변경전 BaseActivity로 강제적으로 ViewModel을 갖는 것을 볼 수 있습니다. 모든 Activity에서 ViewModel이 필요하다는 보장이 없으므로(단순 화면 출력 Activity 등) 필요한 경우에만 ViewModel을 가지도록 아래와 같이 수정하였습니다.
// 변경 후
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initState()
}
open fun initState() {
initViews()
observeData()
}
...
}
abstract class BaseFragment<VB: ViewBinding> : Fragment() {
protected lateinit var binding: VB
abstract fun getViewBinding(): VB
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = getViewBinding()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initState()
}
open fun initState() {
initViews()
observeData()
}
open fun initViews() = Unit
open fun observeData() = Unit
}
BaseActivity와 마찬가지로 ViewModel을 강제적으로 갖지 않도록 수정하고, BaseFragment의 argument를 viewModel.storeState(it)으로 저장하는 method를 현재 사용하지 않아 삭제하였습니다.
// 변경 전
private fun setCategoryButtonListener() = with(binding) {
val navController = findNavController()
foodCategoryListButton.setOnClickListener {
navController.navigate(
HomeMainFragmentDirections
.actionHomeMainFragmentToHomeFragment(HomeListCategory.FOOD)
)
}
martCategoryListButton.setOnClickListener {
navController.navigate(
HomeMainFragmentDirections
.actionHomeMainFragmentToHomeFragment(HomeListCategory.MART)
)
}
... // 다른 버튼에 대해서도 유사한 코드 반복
}
버튼별로 click listener를 설정하여 클릭을 하면 navigation을 통해 HomeFragment로 이동하도록 하였습니다. 코드를 살펴보면 넘겨주는 parameter만 다르고 나머지 부분은 동일한 것을 볼 수 있습니다. 그래서 forEachIndexed와 List를 이용하여 중복되는 코드를 줄였습니다.
private fun setCategoryButtonListener() = with(binding) {
val navController = findNavController()
val buttonList = listOf(
foodCategoryListButton, martCategoryListButton, serviceCategoryListButton,
fashionCategoryListButton, accessoryCategoryListButton, etcCategoryListButton
)
categories.forEachIndexed { index, homeListCategory ->
buttonList[index].setOnClickListener {
navController.navigate(
HomeMainFragmentDirections
.actionHomeMainFragmentToHomeFragment(homeListCategory)
)
}
}
}