티스토리 뷰

안드로이드/일기

2023년 07월 4주 차 일기

알렌보이스 2023. 7. 30. 19:49
728x90

2023년 07월 4주 차 일기입니다.

이번 주에는 우선 코드랩 학습을 진행하였습니다.

https://developer.android.com/codelabs/jetpack-compose-advanced-state-side-effects?hl=ko#0 

 

Jetpack Compose의 고급 상태 및 부작용  |  Android Developers

이 Codelab에서는 Jetpack Compose의 상태 및 부수 효과에 관한 고급 개념을 알아봅니다. 복잡한 스테이트풀(Stateful) 컴포저블의 상태 홀더를 만드는 방법, Compose 코드에서 코루틴을 만들고 정지 함수를

developer.android.com

사용해보지 않았던 내용이 많아서 추후에 상황에 따라 사용해 봐야겠습니다.

 

코드랩 학습을 진행하면서 컴포저블을 만들 때 공통부분과 약간 수정한 버전?을 따로 만들어서 관리하는 게 잘 되어있다고 느꼈습니다.

그래서 저도 조금 더 상세하게 컴포저블을 만들어서 관리해야겠다고 생각하였습니다.

 

다음은 진행하던 프로젝트의 구조를 변경하였습니다.

어떻게 하면 좋을까 많이 고민하였습니다.
나중에 수정할 수도 있겠지만 일단은 방향은 정했습니다.

개발을 진행하는 도중 로딩 화면과 네트워크 오류 화면을 앱에 넣고 싶다는 생각이 들었습니다. 당연히 화면 단위로 각자 구현하는 건 실수로 빼먹을 가능성과 관리하기 힘들다는 문제가 있으니 공용으로 사용할 무언가가 필요했습니다.

고민을 하던 중 코드랩을 통해 collectAsStateWithLifecycle()를 알게 되었습니다.

이걸 이용해서 화면의 상태를 관리해 보려고 합니다.

class BaseStatus {
    var message by mutableStateOf("")
        private set

    var isLoading by mutableStateOf(false)
        private set

    var isNetworkError by mutableStateOf(false)
        private set

    fun updateMessage(message: String) {
        this.message = message
    }

    fun startLoading() {
        isLoading = true
        isNetworkError = false
    }

    fun endLoading() {
        isLoading = false
    }

    fun updateNetworkErrorState(value: Boolean) {
        isNetworkError = value
    }
}

상태를 관리하기 위한 클래스입니다.

나중에 더 추가할지는 모르겠지만 우선 메시지와 로딩 여부, 네트워크 여부를 저장하고 있습니다.

또한 private set을 이용해서 BaseStatus의 함수를 이용해서만 값을 변경하게 하였습니다.

 

abstract class BaseViewModel: ViewModel() {

    private val _status = MutableStateFlow(BaseStatus())
    val status: StateFlow<BaseStatus> = _status

    protected fun startLoading() {
        _status.value.startLoading()
    }

    protected fun endLoading() {
        _status.value.endLoading()
    }

    protected fun updateMessage(message: String) {
        _status.value.updateMessage(message)
    }

    protected fun updateNetworkErrorState(value: Boolean) {
        _status.value.updateNetworkErrorState(value)
    }

}

공용으로 사용할 ViewModel입니다.

StateFlow 변수와 값을 변경할 함수들입니다.

상황에 맞게 함수를 이용하여 상태 값을 관리합니다.

@Composable
fun BaseStructureScreen(
    status: BaseStatus,
    errorScreen: @Composable () -> Unit,
    content: @Composable () -> Unit,
) {
    val context = LocalContext.current

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(MyColorWhite)
    ) {
        if (status.isNetworkError) {
            errorScreen()
        } else {
            content()
        }

        if (status.isLoading) {
            LoadingScreen()
        }
    }

    LaunchedEffect(status.message) {
        if (status.message.trim().isEmpty()) return@LaunchedEffect
        context.toast(status.message)
    }
}

기본 구조의 컴포저블을 위와 같이 만들어 보았습니다.

네트워크 에러가 발생할 시에는 errorScreen을 아니면 content를 표시합니다.

또한 로딩 중일 때에는 LoadingScreen을 표시합니다.

message의 값이 변경되면 내용을 ToastMesaage로 출력합니다.

@Composable
fun BaseContainer(
    status: BaseStatus,
    paddingValues: PaddingValues = PaddingValues(top = 22.dp, start = 20.dp, end = 17.dp),
    reload: (() -> Unit)? = null,
    onBackClick: (() -> Unit)? = null,
    color: Color = MyColorRed,
    errorScreen: (@Composable () -> Unit)? = null,
    content: @Composable ColumnScope.() -> Unit
) {
    BaseStructureScreen(
        status = status,
        content = {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(paddingValues)
            ) {
                content()
            }
        },
        errorScreen = {
            if (errorScreen != null)
                errorScreen()
            else {
                NetworkErrorScreen(
                    onBackClick = onBackClick,
                    color = color,
                    reload = {
                        reload?.invoke()
                    }
                )
            }
        }
    )
}

그다음 BaseStructureScreen을 좀 더 수월하게 사용할 수 있게 만들어 주었습니다.

마지막으로 가장 많은 형태로 사용될 형태 2가지를 만들어 주었습니다.

@Composable
fun HeaderBodyContainer(
    status: BaseStatus,
    paddingValues: PaddingValues =
        PaddingValues(top = 22.dp, start = 20.dp, end = 17.dp, bottom = 10.dp),
    reload: (() -> Unit)? = null,
    onBackClick: (() -> Unit)? = null,
    color: Color = MyColorRed,
    errorScreen: (@Composable () -> Unit)? = null,
    headerContent: @Composable () -> Unit,
    bodyContent: @Composable () -> Unit
) {
    BaseContainer(
        status = status,
        paddingValues = paddingValues,
        reload = reload,
        onBackClick = onBackClick,
        color = color,
        errorScreen = errorScreen
    ) {
        headerContent()
        Box(modifier = Modifier.weight(1f)) {
            bodyContent()
        }
    }
}

@Composable
fun HighMediumLowContainer(
    status: BaseStatus,
    paddingValues: PaddingValues =
        PaddingValues(top = 22.dp, start = 20.dp, end = 17.dp, bottom = 10.dp),
    reload: (() -> Unit)? = null,
    onBackClick: (() -> Unit)? = null,
    color: Color = MyColorRed,
    errorScreen: (@Composable () -> Unit)? = null,
    heightContent: @Composable () -> Unit,
    mediumContent: @Composable () -> Unit,
    lowContent: @Composable () -> Unit
) {
    BaseContainer(
        status = status,
        paddingValues = paddingValues,
        reload = reload,
        onBackClick = onBackClick,
        color = color,
        errorScreen = errorScreen
    ) {
        heightContent()
        Box(modifier = Modifier.weight(1f)) {
            mediumContent()
        }
        lowContent()
    }
}

 

 

이렇게 구조를 결정을 하고 이를 바탕으로 기존에 만들어 두었던 화면들을 수정하는 작업을 진행하였습니다.

아직 다는 못 해서 다음 주에도 마저 작업을 진행하고 새로운 화면의 작업을 할 예정입니다.

728x90

'안드로이드 > 일기' 카테고리의 다른 글

2023.08.07 ~ 08.13  (0) 2023.08.13
2023.07.31 ~ 08.06 일기  (0) 2023.08.07
2023년 7월 일기  (0) 2023.07.23
2023년 6월 3 ~ 4주 차 일기  (0) 2023.06.25
2023년 6월 1 ~ 2주 차 일기  (0) 2023.06.11
댓글