Compose - 개념

2024. 7. 30. 15:34[개발]/Kotlin 활용 앱 개발

# Compose의 개념

- Jetpack Compose : 안드로이드 UI를 선언적으로 구축하는 도구

- 기존의 View 방식(XML + kotlin)에서 발생하는 다양한 문제에 대응

 

# View 방식의 문제점

1) UI 개발에서의 높은 복잡성

2) 상속 문제

 

# Compose의 해결 방식

- 선언적인 UI 작성 방식

- 상속 대신 합성(머지) 채택 / 유지보수성 향상

 

# 선언형(Declarative) UI

- "어떻게 보여줄까?" 가 아니라 "무엇을 보여줄까"를 기술함

- 구현 세부 사항은 시스템이나 프레임워크에 위임

- Composable 함수라는 독립적 단위로 UI 구성 / 직관적 + 재사용성 高

 

# ViewModel 활용

- Compose의 선언형 접근 방식에서 위젯은 비교적 Stateless 상태로, setter/getter(setText(), getText() 등) 함수를 노출 X

- 위젯은 객체로 노출되지 않고, 동일한 Composable 함수를 다른 인수로 호출해 UI 업데이트

- ViewModel과 같은 아키텍쳐 패턴에 쉽게 State를 제공할 수 있음

 

# 데이터 흐름

- 최상위 구성 가능한 함수에 데이터 제공

- 함수를 데이터를 사용해 다른 Composable을 호출, UI 형성

- 적절한 데이터를 해당 Composablem, 계층 구조 아래로 전달

 

# 이벤트 흐름

- 사용자가 UI 요소와 상호작용함에 따라 이벤트 발생 / 앱 로직이 이벤트에 응답

- Composeable 함수가 필요한 경우 새 매개변수를 사용해 자동으로 다시 호출

(참고: https://developer.android.com/develop/ui/compose/mental-model?hl=ko)

 

# MVVM 패턴 활용 - 개념

1) Model (Data Layer)

- Viewmodel에서 요청한 데이터 처리, 반환

- Local DB(SQLite, Room), 네트워크 통신(Retrofit) 이용

 

2) View(UI Layer)

- 액티비티, 프래그먼트가 View 역할 담당

- 사용자의 액션을 받음

- ViewModel의 데이터를 관찰해 UI 갱신

- 사용자의 액션을 감지하고 데이터 변화를 통해 UI 갱신 처리

 

3) ViewModel(UI Layer) 

- 사용자의 액션, 라이프사이클에 의해 View에서 요청한 데이터, 비즈니스 로직 처리

- Model에 요청한 데이터를 받음

 

# MVVM 패턴 활용 - UI Layer

 

# UI State 관리

package com.example.mvvmcompose

import android.os.Bundle
import android.util.Log
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.flowWithLifecycle
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.collectLatest

// UI Layer - MvvmActivity
class MvvmActivity : AppCompatActivity() {

    // viewModel 선언
    private val viewModel : MvvmViewmodel by viewModels()


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        // 화면이 생성되자마자 Viewmodel 초기화 실행
        initViewModel()

        // viewModel에서는 uiState의 데이터를 받아옴
        viewModel.requestBoard()
    }
    
    // 화면 생성과 동시에 뷰모델을 초기화하는 함수를 정의
    private fun initViewModel() {
        lifecycleScope.launch {
            viewModel.uiState.flowWithLifecycle(lifecycle).collectLatest { state ->
                Log.d("jess", state.toString())
            }
        }
    }

}

 

package com.example.mvvmcompose

// Ui State를 관리하는 data class 정의
// 게시판 UI를 가정하고 컴포넌트를 정의(제목, 내용, 사용자..) 

data class MvvmUiState(
    val header: String?,
    val title: String?,
    val content: String?,
    val user: String?,
    val viewCount: Int,
) {
    // StateFlow 설계를 위한 초기값 설정
    companion object {

        fun empty(
            header: String? = null,
            title: String? = null,
            content: String? = null,
            user: String? = null,
            viewCount: Int = 0,
        ) = MvvmUiState (
            header = header,
            title = title,
            content = content,
            user = user,
            viewCount = viewCount,
        )

    }

}

 

package com.example.mvvmcompose

import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update

// 뷰모델 정의
class MvvmViewmodel : ViewModel() {

    private val _uiState = MutableStateFlow(MvvmUiState.empty())
	// uiState의 State 데이터값
	val uiState: StateFlow<MvvmUiState> = _uiState.asStateFlow()

    // UI Layer(Activity)에다가 게시판의 각 컴포넌트의 State 데이터를 넘겨주는 requestBoart() 함수 정의
    fun requestBoard() {
        // activity 시작하자마자 실행되는 함수(api 요청 or 데이터 가져오기 등)
        viewModelScope.launch{
            // update라는 StateFlow의 익스텐션을 활용, 게시판의 각 컴포넌트의 데이터를 update 후 저장
            _uiState.update {prev ->
                prev.copy(
                    // 데이터를 받아와서 viewModel에 데이터를 업데이트해줌
                    header = "내일움캠프 게시판",
                    title = "compose 강의",
                    content = "MVVM 디자인 패턴과 compose",
                    user = "정호정",
                    viewCount = 60000,
                )
            }

        }
    }
}

 

(참고: https://developer.android.com/topic/architecture/ui-layer?hl=ko)