๐Ÿ—จ๏ธ Composable Convention

์Šคํฌ๋ฆฐ์— ๋Œ€ํ•œ ์ตœ์ƒ์œ„ Composable์˜ ๋„ค์ด๋ฐ์€ ~ScreenContainer() ์œผ๋กœ ๊ฐ€์ ธ๊ฐ‘๋‹ˆ๋‹ค.

์ด๋•Œ ScreenContainer์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋ทฐ๋ชจ๋ธ์„ ์ง€์ •ํ•˜๊ณ , state์˜ ๊ตฌ๋…์„ ์ด๊ณณ์—์„œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

@Composable
fun SignInScreenContainer(
    modifier: Modifier = Modifier,
    viewModel: SignInViewModel = viewModel()
) {
    val uiState by viewModel.collectAsState()
    ...
}

ScreenContainer์˜ ํ•˜์œ„ Composable๋กœ Screen()๋ฅผ ์ •์˜ํ•˜๊ณ  ScreenContainer์— ์ด๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

@Composable
fun SignInScreen(
    uiState: SignInUiState,
    modifier: Modifier = Modifier
) {
    // ์—ฌ๊ธฐ๋ถ€ํ„ฐ ๋ณธ๊ฒฉ์ ์œผ๋กœ UI ๋กœ์ง ์ž‘์„ฑ
}
@Composable
fun SignInScreenContainer(
    modifier: Modifier = Modifier,
    viewModel: SignInViewModel = viewModel()
) {
    val uiState by viewModel.collectAsState()
    
    SignInScreen(
        uiState = uiState,
        modifier = Modifier.fillMaxSize()
    )
    
    viewModel.collectSideEffect {
        ...
    }
}

Screen์ด ์•„๋‹Œ ScreenContent์— ๋Œ€ํ•œ ํ”„๋ฆฌ๋ทฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์‹์œผ๋กœ ๋ทฐ๋ชจ๋ธ๊ณผ ๋ Œ๋”๋งํ•  UI ๋กœ์ง์„ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ํ”„๋ฆฌ๋ทฐ๊ฐ€ ์›ํ™œํžˆ ๋™์ž‘ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

@Preview
@Composable
private fun SignInScreenPreview() {
    SignInScreen(...)
}

๐Ÿง Route๊ฐ€ ์•„๋‹Œ ScreenContainer๋ฅผ ๋„ค์ด๋ฐ์œผ๋กœ ๊ฐ€์ ธ๊ฐ€๋Š” ์ด์œ 

SOPT ๋‚ด์—์„  ์ตœ์ƒ์œ„ Composable์˜ ๋„ค์ด๋ฐ์„ ~Route()๋กœ ๊ฐ€์ ธ๊ฐ€๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋„ค๋น„๊ฒŒ์ด์…˜ ๊ฒฝ๋กœ ๋ช…๋„ ~Route๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ธฐ์—, ํ˜ผ๋™์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ˜ผ๋™์„ ์ตœ์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์œ„์™€ ๊ฐ™์€ ๋ฐฉ์‹์„ ์ฐจ์šฉํ•ฉ๋‹ˆ๋‹ค.

@Serializable
sealed interface OnboardingRoute {

    @Serializable
    data object ChoosePlace: OnboardingRoute

    @Serializable
    data object ChooseFoods: OnboardingRoute
}
...

@Composable
fun OnboardingRoute() { ... }