Composable 매개변수 줄이기 - 오버로딩 활용
Compose로 복잡한 UI를 개발하다 보면, 함수의 매개변수가 너무 많아지는 문제가 발생한다. 특히 Activity에서 호출하는 최상위 Composable은 매개변수가 많아질 수밖에 없다.
// EditSubscriptionActivity.kt
binding.composeView.setContent {
val uiState by viewModel.uiState.collectAsState()
KuringTheme {
Subscriptions(
selectedTab = uiState.selectedTab,
categories = uiState.categories,
departments = uiState.departments,
onTabClick = viewModel::onTabClick,
onCategoryClick = viewModel::onNormalSubscriptionItemClick,
onDepartmentClick = viewModel::onDepartmentSubscriptionItemClick,
onAddDepartmentButtonClick = { navigator.navigateToEditSubscribedDepartment(this) },
onSubscriptionComplete = ::onSubscriptionComplete,
modifier = Modifier.fillMaxSize(),
)
}
}
이번 글에서는 ``Subscriptions`` Composable의 매개변수를 줄여 보자.
눈속임
사실 화면에서 제공하는 기능을 줄이지 않는 이상, 콜백을 없앨 수는 없다. 따라서 이 글의 진짜 목적은 겉으로 드러나는 매개변수를 줄이는 것이다. 겉으로 드러나는 매개변수란, Compose 외부의 ``Activity``, ``Fragment``에서 제공해야 하는 매개변수를 말한다.
관찰
코드를 자세히 보면, 매개변수 9개 중 6개가 ``uiState``와 ``viewModel``로부터 제공되고 있음을 알 수 있다. 사실 ``uiState``도 ``viewModel``에서 제공되고 있으므로, ``viewModel``이 6개의 매개변수를 대체할 수 있는 것이다.
따라서 ``Subscriptions``이 실질적으로 요구하는 매개변수는 ``viewModel``, ``onAddDepartmentButtonClick``, ``onSubscriptionComplete``, ``modifier`` 총 4개이다.
새 집 다오
관찰 결과를 바탕으로 새 Composable을 작성하였다. 이 함수를 앞으로 ``간결한 Composable``이라고 부르겠다.
@Composable
fun Subscriptions(
viewModel: EditSubscriptionViewModel,
onAddDepartmentButtonClick: () -> Unit,
onSubscriptionComplete: () -> Unit,
modifier: Modifier = Modifier,
) {
val uiState by viewModel.uiState.collectAsState()
// UI
}
헌 집 줄게?
그렇다면 기존에 구현되어 있던 ``장황한 Composable``은 이제 삭제해도 되나?
@Composable
fun Subscriptions(
selectedTab: EditSubscriptionTab,
categories: List<NormalSubscriptionUiModel>,
departments: List<DepartmentSubscriptionUiModel>,
onTabClick: (EditSubscriptionTab) -> Unit,
onCategoryClick: (Int) -> Unit,
onDepartmentClick: (String) -> Unit,
onAddDepartmentButtonClick: () -> Unit,
onSubscriptionComplete: () -> Unit,
modifier: Modifier = Modifier,
) {
// 제거해야?
}
코드만 보면 그렇지만, 장황한 Composable도 나름 쓸모가 있다.
현재 Compose Preview는 ``ViewModel``을 매개변수로 받는 Composable을 지원하지 않는다. 즉 간결한 Composable을 프리뷰로 볼 수는 없으며, 프리뷰에서는 장황한 Composable만을 볼 수 있다.
따라서 Compose 외부에는 간결한 Composable을 노출하고, 장황한 Composable은 private으로 선언하여 내부 호출 및 프리뷰 용으로 사용하는 것이 제일 좋다. 코드로 정리하면 다음과 같다.
// 간결한 Composable
@Composable
fun Subscriptions(
viewModel: EditSubscriptionViewModel,
onAddDepartmentButtonClick: () -> Unit,
onSubscriptionComplete: () -> Unit,
modifier: Modifier = Modifier,
) {
val uiState by viewModel.uiState.collectAsState()
Subscriptions(
selectedTab = uiState.selectedTab,
categories = uiState.categories,
departments = uiState.departments,
onTabClick = viewModel::onTabClick,
onCategoryClick = viewModel::onNormalSubscriptionItemClick,
onDepartmentClick = viewModel::onDepartmentSubscriptionItemClick,
onAddDepartmentButtonClick = onAddDepartmentButtonClick,
onSubscriptionComplete = onSubscriptionComplete,
modifier = modifier,
)
}
// 장황한 Composable
@Composable
private fun Subscriptions(
selectedTab: EditSubscriptionTab,
categories: List<NormalSubscriptionUiModel>,
departments: List<DepartmentSubscriptionUiModel>,
onTabClick: (EditSubscriptionTab) -> Unit,
onCategoryClick: (Int) -> Unit,
onDepartmentClick: (String) -> Unit,
onAddDepartmentButtonClick: () -> Unit,
onSubscriptionComplete: () -> Unit,
modifier: Modifier = Modifier,
) {
// UI
}
// 프리뷰
@LightPreview
@Composable
private fun DepartmentPagePreview_Empty() {
KuringTheme {
Subscriptions(
selectedTab = EditSubscriptionTab.DEPARTMENT,
onTabClick = {},
categories = emptyList(),
departments = emptyList(),
onCategoryClick = {},
onDepartmentClick = {},
onAddDepartmentButtonClick = {},
onSubscriptionComplete = {},
modifier = Modifier.fillMaxSize(),
)
}
}
``Activity``에서는 간결한 Composable을 호출하면 된다.
binding.composeView.setContent {
KuringTheme {
Subscriptions(
viewModel = viewModel,
onAddDepartmentButtonClick = { navigator.navigateToEditSubscribedDepartment(this) },
onSubscriptionComplete = ::onSubscriptionComplete,
modifier = Modifier.fillMaxSize(),
)
}
}
참고 문헌
내 머릿속