Preview에 Viewmodel 연결하는 방법

이윤설·2024년 12월 17일
0

안드로이드 연구소

목록 보기
14/33

보통 Screen 클래스는 뷰모델을 필요로 한다.
하지만 Screen 클래스가 뷰모델을 필요로 하는 경우(사용하는 경우), 원칙적으로는 Preview 기능을 사용할 수 없다.

그럼에도 Preview를 사용할 방법이 있다.

1. 가짜 뷰모델 만들기

Step 1. 인터페이스, 진짜 뷰모델, 가짜 뷰모델 만들기

enum class Categories {
    EFFORT, WEALTH, BUSINESS, LOVE, EXERCISE, CONFIDENCE, CREATIVITY, HAPPINESS, OTHER;

    fun toKorean(): String {
        return when (this) {
            EFFORT -> "노력"
            WEALTH -> "부"
            BUSINESS -> "비즈니스"
            LOVE -> "사랑"
            EXERCISE -> "운동"
            CONFIDENCE -> "자신감"
            CREATIVITY -> "창의력"
            HAPPINESS -> "행복"
            OTHER -> "기타"
        }
    }
}
-----------------------------------------------------------
interface CategoryViewModelInterface {
    fun getCategories(): List<String>
}
-----------------------------------------------------------
@HiltViewModel
class CategoryViewModel @Inject constructor() : ViewModel(), CategoryViewModelInterface {
    private val categories = Categories.values().map { it.toKorean() }

    override fun getCategories(): List<String> {
        return categories
    }
}
-----------------------------------------------------------
class FakeCategoryViewModel : CategoryViewModelInterface {
    override fun getCategories(): List<String> {
        return listOf("노력", "부", "비즈니스", "사랑", "운동", "자신감", "창의력", "행복", "기타")
    }
}

뷰모델에서 카테고리의 항목을 한국어로 불러오는 기능을 사용한다고 가정하겠다.

CategoryViewModelInterface에 getCategories()을 작성하고,
두 뷰모델 모두 함수를 오버라이드 한다.

가짜 뷰모델은 어차피 Preview 용도 이기 때문에, 위처럼 정확한 기능을 구현할 필요성은 없다.

Step 2. 뷰모델 적용하기

@Preview
@Composable
fun CategoryScreenContentPreview() {
    Passion_DailyTheme {
        CategoryScreenContent(categoryViewModel = FakeCategoryViewModel())
    }
}

이런 식으로 Screen 클래스 내 Preview 함수에 가짜 뷰모델을 넣어주면 된다.

@Composable
fun CategoryScreen(categoryViewModel: CategoryViewModel = hiltViewModel()) {
    CategoryScreenContent(categoryViewModel)
}

@Composable
fun CategoryScreenContent(categoryViewModel: CategoryViewModelInterface) {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(BlackBackground)
    ) {
        Column(
            modifier = Modifier
                .offset(x = 24.dp, y = 24.dp)
                .align(Alignment.TopStart)
        ) {
            BackButton()
        }
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .offset(y = 24.dp)
                .align(Alignment.TopCenter)
        ) {
            Row(
                modifier = Modifier
                    .align(Alignment.CenterHorizontally)
            ) {
                Category()
            }
            Spacer(modifier = Modifier.height(26.dp))
            Row(
                modifier = Modifier
                    .align(Alignment.CenterHorizontally)
            ) {
                CategorySelectionGuide()
            }
            Spacer(modifier = Modifier.height(28.dp))
            CategoryBoxes(categoryViewModel)
        }
    }
}

@Preview
@Composable
fun CategoryScreenContentPreview() {
    Passion_DailyTheme {
        CategoryScreenContent(categoryViewModel = FakeCategoryViewModel())
    }
}

차이가 보이는가? CategoryScreenContent의 파라미터에는 CategoryViewModelInterface를 넣어줌으로써 진짜 뷰모델과 가짜 뷰모델 모두 허용하도록 만들고, CategoryScreen는 진짜 뷰모델을, CategoryScreenContentPreview에는 가짜 뷰모델을 넣어주는 방법을 사용하면 된다.


어차피 진짜, 가짜 뷰모델 모두 CategoryViewModelInterface의 자식이기 때문에 정상적으로 작동한다.

2. 매개변수 변경

@Composable
fun CategoryScreen(categoryViewModel: CategoryViewModel = hiltViewModel()) {
    val categories = categoryViewModel.getCategories()
    CategoryScreenContent(categories)
}

@Composable
fun CategoryScreenContent(categories: List<String>) {
			// 나머지 생략
            CategoryBoxes(categories)
        }
    }
}

@Composable
fun SingleCategoryBox(
    category: String,
    modifier: Modifier = Modifier
) {
    Box(
        modifier = modifier
            .width(125.dp)
            .height(125.dp)
            .background(color = Color(0xFFD9D9D9), shape = RoundedCornerShape(size = 16.dp)),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = category,
            style = TextStyle(
                fontSize = 20.sp,
                fontFamily = FontFamily(Font(R.font.inter_18pt_regular)),
                fontWeight = FontWeight(400),
                color = Color(0xFF000000),
            )
        )
    }
}

@Preview
@Composable
fun CategoryScreenContentPreview() {
    Passion_DailyTheme {
        CategoryScreenContent(
            categories = Categories.values().map { it.toKorean() }
        )
    }
}
----------------
@HiltViewModel
class CategoryViewModel @Inject constructor() : ViewModel(), CategoryViewModelInterface {
    private val categories = Categories.values().map { it.toKorean() }

    override fun getCategories(): List<String> {
        return categories
    }
}

기존에는 CategoryScreen, CategoryScreenContent 모두 뷰모델을 의존하도록 하였다.
하지만 서비스가 복잡한게 아니라면, Screen 함수에서만 뷰모델을 의존하도록 바꾸어도 좋다.

profile
화려한 외면이 아닌 단단한 내면

0개의 댓글

관련 채용 정보