Primary/Compose

Composable 매개변수 줄이기 - 오버로딩 활용

해스끼 2024. 1. 10. 16:41

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(),
        )
    }
}

참고 문헌

내 머릿속