일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 개발자
- 어플개발
- 동성로카페
- 수성구카페
- 수성구맛집
- 안드로이드앱
- 의무경찰
- 파이썬
- Android
- 애드센스
- 감성카페
- 코딩공부
- compose
- 지산동카페
- 안드로이드개발
- 대구카페
- 동성로핫플
- 범물동카페
- 대구인스타핫플
- 애드몹
- 동성로맛집
- 안드로이드앱개발
- 대구핫플
- 앱개발
- 안드로이드
- androidstudio
- 코딩
- 안드로이드스튜디오
- 대구맛집
- 인스타핫플
Archives
- Today
- Total
Glacier's Daily Log
Android Compose + MVI 아키텍처 개발 그라운드 룰 본문
반응형
회사에서 Compose를 최초 도입하는 프로젝트를 리딩하고, 팀원들이 Compose를 도입하기 편하게 초기 작업들을 많이 진행하고 있다.
Compose는 Recomposition과 상태관리 관점에서 많은 이점을 얻을 수 있는 MVI 아키텍처와 잘 어울리는데,
따라서 상용 운영중인 앱에 Compose를 도입할때는 MVI아키텍처로 리팩토링도 함께 진행하고 있다.
Android Compose + MVI 아키텍처를 도입할 때 주의해야 할 점을 그라운드 룰로 정리해본다.
Jetpack Compose에서 MVI(Model–View–Intent) 아키텍처를 준수하며 개발할 때, 구조적 일관성과 유지보수성을 높이기 위한 Ground Rule
1. 단방향 데이터 흐름 (Unidirectional Data Flow)
- 상태 직접 변경 금지 (uiState.value = ... 등은 금지)
- View → ViewModel (event) → ViewModel → State/Effect → View로만 흐름
2. 초기 데이터 로딩 방식 통일 (논의 필요)
방법1 - MVI 원칙은 따르나, 데이터 로딩 시점과 UI 렌더링 시점을 잘 고려해야함
- Init 이벤트를 명시적으로 호출하여 상태 사이클 돌리기
- LaunchedEffect(Unit) 또는 remember { ... } 내에서 명시적으로 viewModel.sendEvent(MyEvent.Init) 호출
- LaunchedEffect(Unit) { viewModel.sendEvent(MyEvent.Init) }
방법2 - 모든 흐름은 event로 부터 시작된다는 MVI 원칙에는 위배되지만, 초기 데이터 로딩에는 적합
- StateFlow Flow.stateIn() 하기 전에 onStart {} 내에서 초기 데이터 로딩 함수 호출
방법3 - 비추천
- ViewModel init {} 블록 내에서 초기 데이터 로딩 함수 호출
3. 상태(State)와 부수효과(SideEffect)는 명확히 분리
항목 예시
UiState | 로딩 상태, 리스트 데이터, 선택 항목 등 UI에 반영될 정보 |
SideEffect | 토스트, 네비게이션, 다이얼로그, 팝업 등 일회성 UI 동작 |
- 상태는 StateFlow, 부수효과는 SharedFlow/Channel로 관리
4. Intent/Event는 명시적으로 설계
- 모든 사용자 액션, UI 진입, 리트라이 등은 Event로 정의
sealed interface MyEvent {
object Init : MyEvent
data class OnClickItem(val id: String) : MyEvent
object Retry : MyEvent
}
5. ViewModel은 상태 중심 로직만 관리
- ViewModel은 다음만 처리:
- Event 처리 (reduceEvent(event: MyEvent))
- 상태 변경 (uiState.copy { ... })
- Effect 발행 (sendEffect(...))
- Repository 등 도메인 계층 호출
- 절대 ViewModel에서 직접 UI 관련 코드 호출하지 말 것
6. Composable은 상태만 소비하고 이벤트만 전송
- Composable은 state를 collectAsState()로 수신
- 사용자 입력 등은 sendEvent(...)로 ViewModel에 전달
- 직접 Repository 호출, 상태 변경 금지
7. 상태는 Immutable하게 구성
- data class 사용하여 불변성 유지
- .copy(...)를 통해 상태 변경
- UIState는 명확히 스냅샷처럼 구성
data class MyState(
val isLoading: Boolean = false,
val items: List<Item> = emptyList(),
val error: String? = null
)
8. ViewModel의 상태 스트림은 stateIn 로 관리
val uiState: StateFlow<MyState> = _event
.onStart { emit(MyEvent.Init) }
.runningFold(initialState, ::reduce)
.stateIn(viewModelScope, SharingStarted.Eagerly, initialState)
9. 비동기 작업은 반드시 ViewModel 내부에서 처리
- viewModelScope.launch { ... }로 처리
- Repository 호출 시 결과를 받아 상태/이펙트로 반영
- Composable이나 UI에서 suspend fun 직접 호출 금지
10. Error Handling은 일관되게: State or Effect
처리 유형 방법
화면 내 에러 메시지 | uiState.errorMessage 등으로 상태에 포함 |
일회성 팝업/토스트 | SideEffect.ShowErrorPopup(...) 등으로 처리 후 UI에서 소비 |
Layer 정리
UI (Compose)
└─ State + Effect 소비 + 이벤트 전송
ViewModel
└─ Event 수신 → 비즈니스 처리 → State / Effect 발행
UseCase/Repository
└─ 데이터 처리, 네트워크 호출, 결과 반환
반응형
'Coding > Android' 카테고리의 다른 글
Compose에서 동일한 Composable에 대해 여러 옵션의 Preview가 필요한 경우 (0) | 2025.02.13 |
---|---|
[React-Native] 리액트 네이티브 버전 업그레이드시 참고할 사이트 (1) | 2025.01.07 |
Compose Strong Skipping Mode의 람다 메모라이제이션 기능 (0) | 2024.12.13 |
Compose의 Strong Skipping Mode에 대해 (0) | 2024.12.13 |
Compose의 리컴포지션 판단 메커니즘과 Stability (0) | 2024.12.13 |
Comments