์๋๋ก์ด๋ ์ฑ์์ UI ์ด๋ฒคํธ๋ ์ฌ์ฉ์์ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ์ ํต์ฌ์ ์ธ ์ํธ์์ฉ์ ๋ด๋นํ๋ค. ์๋ฅผ ๋ค์ด, ๋ฒํผ ํด๋ฆญ, ์คํฌ๋กค, ์ ๋ ฅ ํ๋ ๋ณ๊ฒฝ ๋ฑ์ ๋์์ด ๋ชจ๋ ์ด๋ฒคํธ๋ก ์ฒ๋ฆฌ๋๋ค. ์ด๋ฌํ ์ด๋ฒคํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ์ง ์์ผ๋ฉด ์ฑ์ ์๋ต์ฑ์ด ๋จ์ด์ง๊ฑฐ๋ ์๊ธฐ์น ๋ชปํ ๋์์ด ๋ฐ์ํ ์ ์๋ค. ๋ฐ๋ผ์, ์ฒด๊ณ์ ์ธ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฐฉ์์ด ํ์ํ๋ค.
UI ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๊ธฐ๋ณธ ์์น
1. ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ(One-way Data Flow, UDF) ์ ์ง
์๋๋ก์ด๋์์ UI ์ํ๋ฅผ ๊ด๋ฆฌํ ๋ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ์ ์งํ๋ ๊ฒ์ด ์ค์ํ๋ค.
์ฆ, ์ด๋ฒคํธ โ ์ํ ๋ณ๊ฒฝ โ UI ์
๋ฐ์ดํธ์ ํ๋ฆ์ ๋ฐ๋ผ์ผ ํ๋ค.
์ด๋ฌํ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ฅด๋ฉด ์ํ ๋ณํ์ ์์ธ์ ์ถ์ ํ๊ธฐ ์ฌ์์ง๊ณ , ๋๋ฒ๊น ์ด ๊ฐํธํด์ง๋ค. ๋ํ, ์๊ธฐ์น ๋ชปํ ๋ถ์์ฉ์ ๋ฐฉ์งํ์ฌ ์ ์ง๋ณด์์ฑ์ ๋์ผ ์ ์๋ค.
์์
- ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ด๋ฒคํธ(event) ๊ฐ ๋ฐ์ํ๋ค.
- ์ด ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ์ฌ ์๋ก์ด ์ํ(state) ๋ฅผ ์์ฑํ๋ค.
- UI๋ ์ด ์๋ก์ด ์ํ๋ฅผ ๋ฐ์ํ์ฌ ์ ๋ฐ์ดํธ๋๋ค.
์ด ์์น์ ์งํค๋ฉด ์์ธก ๊ฐ๋ฅํ UI ๋์์ ๊ตฌํํ ์ ์๋ค.
2. ์ด๋ฒคํธ ์๋น์ ์ํ ์ ๋ฐ์ดํธ์ ๋ถ๋ฆฌ
์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๋, ์ด๋ฒคํธ ์์ฒด๋ฅผ ์ง์ UI ์ํ์ ๋ฐ์ํ๋ ๊ฒ์ด ์๋๋ผ, ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ก์ง๊ณผ ์ํ ์ ๋ฐ์ดํธ ๋ก์ง์ ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
์๋ฅผ ๋ค์ด, ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋,
- ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ์ฒ๋ฆฌ
- ์ํ ์ ๋ฐ์ดํธ๋ ViewModel์์ ์ฒ๋ฆฌ
์ด๋ ๊ฒ ํ๋ฉด ์ด๋ฒคํธ์ ์ํ ๋ณํ์ ์ญํ ์ด ๋ถ๋ฆฌ๋์ด ์ฝ๋๊ฐ ๋์ฑ ๋ช ํํด์ง๋ค.
3. ๋น๋๊ธฐ ์ฒ๋ฆฌ ์ผ๊ด์ฑ ์ ์ง
์ด๋ฒคํธ ์ฒ๋ฆฌ๋ ์ข
์ข
๋คํธ์ํฌ ์์ฒญ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
๊ณผ ๊ฒฐํฉ๋๋ค.
๋ฐ๋ผ์, ๋น๋๊ธฐ ์ฒ๋ฆฌ ํจํด์ ์ผ๊ด๋๊ฒ ์ ์งํ๋ ๊ฒ์ด ์ค์ํ๋ค.
โ ViewModel์์ Coroutine ๋๋ Flow๋ฅผ ํ์ฉํ๋ฉด ๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ํจ์จ์ ์ผ๋ก ํ ์ ์๋ค.
UI ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจํด
์๋๋ก์ด๋์์๋ UI ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ฌ๋ฌ ๊ฐ์ง ํจํด์ ์ฌ์ฉํ ์ ์๋ค.
๊ทธ์ค์์๋ ๋ํ์ ์ธ ๋ ๊ฐ์ง ํจํด์ ๋ค์๊ณผ ๊ฐ๋ค.
1. ์ํ ๊ธฐ๋ฐ ํจํด (State-based Pattern)
์ด ํจํด์์๋ UI ์ํ๋ฅผ ๋ํ๋ด๋ ๋ฐ์ดํฐ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก UI ๋ ๋๋งํ๋ค. (๋ ๋๋ง = ๊ทธ๋ ค์ง)
UI์์ ์คํํด์ผ ํ๋ ์์ ์ด ์๋๋ผ ์ด๋ฌํ ์์ ์ด UI ์ํ์ ๋ฏธ์น๋ ์ํฅ์ ์๊ฐํด ๋ณด๋ฉฐ ๋ชจ๋ธ๋ง ํด์ผํ๋ค.
ํต์ฌ์ฌํญ : ViewModel ์ด๋ฒคํธ๋ ํญ์ UI ์ ๋ฐ์ดํธ๋ก ์ด์ด์ง๋ค.
๐ ๊ตฌ์ฑ ์์
- UIState: UI์ ์ํ๋ฅผ ๋ํ๋ด๋ ๋ฐ์ดํฐ ํด๋์ค
- ViewModel: UI ์ํ๋ฅผ ๊ด๋ฆฌํ๋ฉฐ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ์ฌ ์ํ๋ฅผ ๋ณ๊ฒฝ
- Composable: ์ํ๋ฅผ ๋ฐ์ UI๋ฅผ ๊ทธ๋ฆฌ๋ ์ญํ
๐ ์์ ์ฝ๋ (Jetpack Compose)
data class UiState(val count: Int = 0)
class CounterViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun increment() {
_uiState.value = _uiState.value.copy(count = _uiState.value.count + 1)
}
}
@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsState()
Column {
Text("Count: ${uiState.count}")
Button(onClick = { viewModel.increment() }) {
Text("Increment")
}
}
}
๐ ํน์ง
- UI ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ํ๋ฉด์ด ๊ฐฑ์ ๋จ
- ์ํ ๋ณํ๋ฅผ ์ถ์ ํ๊ธฐ ์ฌ์
- ํ ์คํธ๊ฐ ์ฉ์ดํจ
๐จ ์ฃผ์ํ ์
- ์ํ ํด๋์ค๊ฐ ๋ณต์กํด์ง ์ ์์ โ ์ํ๋ฅผ ์ ์ ํ ๋ถ๋ฆฌํ์ฌ ๊ด๋ฆฌํด์ผ ํจ
2. ์ด๋ฒคํธ ์คํธ๋ฆผ ํจํด (Event Stream Pattern)
์ด ํจํด์์๋ ์ด๋ฒคํธ๋ฅผ ์คํธ๋ฆผ ํํ๋ก ๊ด๋ฆฌํ์ฌ ๋
๋ฆฝ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ค.
๋ณดํต Flow ๋๋ LiveData๋ฅผ ํ์ฉํ์ฌ ๊ตฌํ๋๋ค.
๐ ๊ตฌ์ฑ ์์
- SharedFlow ๋๋ Channel์ ์ฌ์ฉํ์ฌ ์ด๋ฒคํธ๋ฅผ ์คํธ๋ฆผ์ผ๋ก ๊ด๋ฆฌ
- ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ณ๋์ ๋ก์ง ์ ์ง
๐ ์์ ์ฝ๋ (SharedFlow ์ฌ์ฉ)
class EventViewModel : ViewModel() {
private val _events = MutableSharedFlow<UiEvent>()
val events = _events.asSharedFlow()
fun onButtonClick() {
viewModelScope.launch {
_events.emit(UiEvent.ShowToast("๋ฒํผ์ด ํด๋ฆญ๋จ"))
}
}
}
sealed class UiEvent {
data class ShowToast(val message: String) : UiEvent()
}
@Composable
fun EventScreen(viewModel: EventViewModel = viewModel()) {
val context = LocalContext.current
LaunchedEffect(Unit) {
viewModel.events.collect { event ->
when (event) {
is UiEvent.ShowToast -> Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
}
}
}
Button(onClick = { viewModel.onButtonClick() }) {
Text("Click Me")
}
}
๐ ํน์ง
- ์ด๋ฒคํธ๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก ์ฒ๋ฆฌ๋จ
- UI ์ํ์ ์ด๋ฒคํธ๋ฅผ ๋ถ๋ฆฌํ ์ ์์
- ๋น๋๊ธฐ ์ด๋ฒคํธ ๊ด๋ฆฌ์ ์ ๋ฆฌ
๐จ ์ฃผ์ํ ์
- ์ด๋ฒคํธ๊ฐ ๋๋ฝ๋์ง ์๋๋ก ์ ์คํ๊ฒ ๊ด๋ฆฌํด์ผ ํจ
- ์ด๋ฒคํธ๊ฐ ์ฌ๋ฌ ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ ์์๋ฅผ ๊ณ ๋ คํด์ผ ํจ
UI ์ด๋ฒคํธ ์ฒ๋ฆฌ์์ ๊ถ์ฅ๋๋ ๋ฐฉ๋ฒ
- ViewModel์ ํ์ฉํ๋ค.
- UI ๋ก์ง๊ณผ ๋ฐ์ดํฐ๋ฅผ ViewModel์์ ๊ด๋ฆฌํ๋ฉด, ์๋ช ์ฃผ๊ธฐ์ ๋ฌด๊ดํ๊ฒ ์ํ๋ฅผ ์ ์งํ ์ ์๋ค.
- ์ํ ํธ์ด์คํ
(State Hoisting)์ ์ ์ฉํ๋ค.
- ์ํ๋ฅผ ์์ ์ปดํฌ๋ํธ๋ก ๋์ด์ฌ๋ ค UI์ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๊ณ , ์ํ ๊ด๋ฆฌ ๋ก์ง์ ๋จ์ํํ๋ค.
- ๋จ์ผ ์ง์ค์ ์์ฒ(Single Source of Truth)์ ์ ์งํ๋ค.
- ์ํ๋ฅผ ์ฌ๋ฌ ๊ณณ์์ ๊ด๋ฆฌํ์ง ์๊ณ , ํ๋์ ViewModel ๋๋ Repository์์ ๊ด๋ฆฌํ์ฌ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ์ ์งํ๋ค.
- ์ด๋ฒคํธ๋ฅผ UI ์ํ์ ๋ถ๋ฆฌํ๋ค.
- ์ด๋ฒคํธ(ํด๋ฆญ, ์ ๋ ฅ ๋ฑ)๋ SharedFlow ๊ฐ์ ๋ณ๋์ ์คํธ๋ฆผ์ผ๋ก ๊ด๋ฆฌํ๊ณ , UI ์ํ์ ํผํฉํ์ง ์๋๋ก ํด์ผ ํ๋ค.
- ๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ๋ช
ํํ๊ฒ ํด์ผํ๋ค.
- Coroutine, Flow, LiveData๋ฅผ ํ์ฉํ์ฌ ๋น๋๊ธฐ ์ด๋ฒคํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ค.
๊ฒฐ๋ก
์๋๋ก์ด๋ ์ฑ์์ UI ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๋๋ ๋ช ํํ ์์น๊ณผ ํจํด์ ๋ฐ๋ฅด๋ ๊ฒ์ด ์ค์ํ๋ค.
- ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ์ ์งํ๊ณ ,
- ์ด๋ฒคํธ์ ์ํ๋ฅผ ๋ถ๋ฆฌํ๋ฉฐ,
- ๋น๋๊ธฐ ์ด๋ฒคํธ๋ฅผ ์ผ๊ด๋๊ฒ ์ฒ๋ฆฌํ๋ฉด
๋ณด๋ค ๊ฒฌ๊ณ ํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ์ฑ์ ๊ฐ๋ฐํ ์ ์๋ค.
'๐ค ์๋๋ก์ด๋ > ์ํคํ ์ฒ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์๋๋ก์ด๋] UI ์ํ ๊ด๋ฆฌ: ์ํ ํ๋(State Holder) (0) | 2025.03.30 |
---|---|
[์๋๋ก์ด๋] UI ๋ ์ด์ด ์ํคํ ์ฒ (0) | 2025.03.28 |
[์๋๋ก์ด๋] ์๋๋ก์ด๋ ์ํคํ ์ฒ ๊ถ์ฅ์ฌํญ (0) | 2025.03.26 |
[์๋๋ก์ด๋] ์๋๋ก์ด๋ ์ข ์ ํญ๋ชฉ ์ฝ์ (DI) (0) | 2025.03.25 |
[์๋๋ก์ด๋] Android 4๋ ์ปดํฌ๋ํธ๋ฅผ ๋งค๋ํ์คํธ ์์ ์ ์ธํ๋ ๋ฐฉ๋ฒ (0) | 2025.03.24 |
์๋๋ก์ด๋ ์ฑ์์ UI ์ด๋ฒคํธ๋ ์ฌ์ฉ์์ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ์ ํต์ฌ์ ์ธ ์ํธ์์ฉ์ ๋ด๋นํ๋ค. ์๋ฅผ ๋ค์ด, ๋ฒํผ ํด๋ฆญ, ์คํฌ๋กค, ์ ๋ ฅ ํ๋ ๋ณ๊ฒฝ ๋ฑ์ ๋์์ด ๋ชจ๋ ์ด๋ฒคํธ๋ก ์ฒ๋ฆฌ๋๋ค. ์ด๋ฌํ ์ด๋ฒคํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ์ง ์์ผ๋ฉด ์ฑ์ ์๋ต์ฑ์ด ๋จ์ด์ง๊ฑฐ๋ ์๊ธฐ์น ๋ชปํ ๋์์ด ๋ฐ์ํ ์ ์๋ค. ๋ฐ๋ผ์, ์ฒด๊ณ์ ์ธ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฐฉ์์ด ํ์ํ๋ค.
UI ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๊ธฐ๋ณธ ์์น
1. ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ(One-way Data Flow, UDF) ์ ์ง
์๋๋ก์ด๋์์ UI ์ํ๋ฅผ ๊ด๋ฆฌํ ๋ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ์ ์งํ๋ ๊ฒ์ด ์ค์ํ๋ค.
์ฆ, ์ด๋ฒคํธ โ ์ํ ๋ณ๊ฒฝ โ UI ์
๋ฐ์ดํธ์ ํ๋ฆ์ ๋ฐ๋ผ์ผ ํ๋ค.
์ด๋ฌํ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ฅด๋ฉด ์ํ ๋ณํ์ ์์ธ์ ์ถ์ ํ๊ธฐ ์ฌ์์ง๊ณ , ๋๋ฒ๊น ์ด ๊ฐํธํด์ง๋ค. ๋ํ, ์๊ธฐ์น ๋ชปํ ๋ถ์์ฉ์ ๋ฐฉ์งํ์ฌ ์ ์ง๋ณด์์ฑ์ ๋์ผ ์ ์๋ค.
์์
- ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ด๋ฒคํธ(event) ๊ฐ ๋ฐ์ํ๋ค.
- ์ด ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ์ฌ ์๋ก์ด ์ํ(state) ๋ฅผ ์์ฑํ๋ค.
- UI๋ ์ด ์๋ก์ด ์ํ๋ฅผ ๋ฐ์ํ์ฌ ์ ๋ฐ์ดํธ๋๋ค.
์ด ์์น์ ์งํค๋ฉด ์์ธก ๊ฐ๋ฅํ UI ๋์์ ๊ตฌํํ ์ ์๋ค.
2. ์ด๋ฒคํธ ์๋น์ ์ํ ์ ๋ฐ์ดํธ์ ๋ถ๋ฆฌ
์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๋, ์ด๋ฒคํธ ์์ฒด๋ฅผ ์ง์ UI ์ํ์ ๋ฐ์ํ๋ ๊ฒ์ด ์๋๋ผ, ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ก์ง๊ณผ ์ํ ์ ๋ฐ์ดํธ ๋ก์ง์ ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
์๋ฅผ ๋ค์ด, ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋,
- ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ์ฒ๋ฆฌ
- ์ํ ์ ๋ฐ์ดํธ๋ ViewModel์์ ์ฒ๋ฆฌ
์ด๋ ๊ฒ ํ๋ฉด ์ด๋ฒคํธ์ ์ํ ๋ณํ์ ์ญํ ์ด ๋ถ๋ฆฌ๋์ด ์ฝ๋๊ฐ ๋์ฑ ๋ช ํํด์ง๋ค.
3. ๋น๋๊ธฐ ์ฒ๋ฆฌ ์ผ๊ด์ฑ ์ ์ง
์ด๋ฒคํธ ์ฒ๋ฆฌ๋ ์ข
์ข
๋คํธ์ํฌ ์์ฒญ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
๊ณผ ๊ฒฐํฉ๋๋ค.
๋ฐ๋ผ์, ๋น๋๊ธฐ ์ฒ๋ฆฌ ํจํด์ ์ผ๊ด๋๊ฒ ์ ์งํ๋ ๊ฒ์ด ์ค์ํ๋ค.
โ ViewModel์์ Coroutine ๋๋ Flow๋ฅผ ํ์ฉํ๋ฉด ๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ํจ์จ์ ์ผ๋ก ํ ์ ์๋ค.
UI ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจํด
์๋๋ก์ด๋์์๋ UI ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ฌ๋ฌ ๊ฐ์ง ํจํด์ ์ฌ์ฉํ ์ ์๋ค.
๊ทธ์ค์์๋ ๋ํ์ ์ธ ๋ ๊ฐ์ง ํจํด์ ๋ค์๊ณผ ๊ฐ๋ค.
1. ์ํ ๊ธฐ๋ฐ ํจํด (State-based Pattern)
์ด ํจํด์์๋ UI ์ํ๋ฅผ ๋ํ๋ด๋ ๋ฐ์ดํฐ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก UI ๋ ๋๋งํ๋ค. (๋ ๋๋ง = ๊ทธ๋ ค์ง)
UI์์ ์คํํด์ผ ํ๋ ์์ ์ด ์๋๋ผ ์ด๋ฌํ ์์ ์ด UI ์ํ์ ๋ฏธ์น๋ ์ํฅ์ ์๊ฐํด ๋ณด๋ฉฐ ๋ชจ๋ธ๋ง ํด์ผํ๋ค.
ํต์ฌ์ฌํญ : ViewModel ์ด๋ฒคํธ๋ ํญ์ UI ์ ๋ฐ์ดํธ๋ก ์ด์ด์ง๋ค.
๐ ๊ตฌ์ฑ ์์
- UIState: UI์ ์ํ๋ฅผ ๋ํ๋ด๋ ๋ฐ์ดํฐ ํด๋์ค
- ViewModel: UI ์ํ๋ฅผ ๊ด๋ฆฌํ๋ฉฐ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ์ฌ ์ํ๋ฅผ ๋ณ๊ฒฝ
- Composable: ์ํ๋ฅผ ๋ฐ์ UI๋ฅผ ๊ทธ๋ฆฌ๋ ์ญํ
๐ ์์ ์ฝ๋ (Jetpack Compose)
data class UiState(val count: Int = 0)
class CounterViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun increment() {
_uiState.value = _uiState.value.copy(count = _uiState.value.count + 1)
}
}
@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsState()
Column {
Text("Count: ${uiState.count}")
Button(onClick = { viewModel.increment() }) {
Text("Increment")
}
}
}
๐ ํน์ง
- UI ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ํ๋ฉด์ด ๊ฐฑ์ ๋จ
- ์ํ ๋ณํ๋ฅผ ์ถ์ ํ๊ธฐ ์ฌ์
- ํ ์คํธ๊ฐ ์ฉ์ดํจ
๐จ ์ฃผ์ํ ์
- ์ํ ํด๋์ค๊ฐ ๋ณต์กํด์ง ์ ์์ โ ์ํ๋ฅผ ์ ์ ํ ๋ถ๋ฆฌํ์ฌ ๊ด๋ฆฌํด์ผ ํจ
2. ์ด๋ฒคํธ ์คํธ๋ฆผ ํจํด (Event Stream Pattern)
์ด ํจํด์์๋ ์ด๋ฒคํธ๋ฅผ ์คํธ๋ฆผ ํํ๋ก ๊ด๋ฆฌํ์ฌ ๋
๋ฆฝ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ค.
๋ณดํต Flow ๋๋ LiveData๋ฅผ ํ์ฉํ์ฌ ๊ตฌํ๋๋ค.
๐ ๊ตฌ์ฑ ์์
- SharedFlow ๋๋ Channel์ ์ฌ์ฉํ์ฌ ์ด๋ฒคํธ๋ฅผ ์คํธ๋ฆผ์ผ๋ก ๊ด๋ฆฌ
- ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ณ๋์ ๋ก์ง ์ ์ง
๐ ์์ ์ฝ๋ (SharedFlow ์ฌ์ฉ)
class EventViewModel : ViewModel() {
private val _events = MutableSharedFlow<UiEvent>()
val events = _events.asSharedFlow()
fun onButtonClick() {
viewModelScope.launch {
_events.emit(UiEvent.ShowToast("๋ฒํผ์ด ํด๋ฆญ๋จ"))
}
}
}
sealed class UiEvent {
data class ShowToast(val message: String) : UiEvent()
}
@Composable
fun EventScreen(viewModel: EventViewModel = viewModel()) {
val context = LocalContext.current
LaunchedEffect(Unit) {
viewModel.events.collect { event ->
when (event) {
is UiEvent.ShowToast -> Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
}
}
}
Button(onClick = { viewModel.onButtonClick() }) {
Text("Click Me")
}
}
๐ ํน์ง
- ์ด๋ฒคํธ๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก ์ฒ๋ฆฌ๋จ
- UI ์ํ์ ์ด๋ฒคํธ๋ฅผ ๋ถ๋ฆฌํ ์ ์์
- ๋น๋๊ธฐ ์ด๋ฒคํธ ๊ด๋ฆฌ์ ์ ๋ฆฌ
๐จ ์ฃผ์ํ ์
- ์ด๋ฒคํธ๊ฐ ๋๋ฝ๋์ง ์๋๋ก ์ ์คํ๊ฒ ๊ด๋ฆฌํด์ผ ํจ
- ์ด๋ฒคํธ๊ฐ ์ฌ๋ฌ ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ ์์๋ฅผ ๊ณ ๋ คํด์ผ ํจ
UI ์ด๋ฒคํธ ์ฒ๋ฆฌ์์ ๊ถ์ฅ๋๋ ๋ฐฉ๋ฒ
- ViewModel์ ํ์ฉํ๋ค.
- UI ๋ก์ง๊ณผ ๋ฐ์ดํฐ๋ฅผ ViewModel์์ ๊ด๋ฆฌํ๋ฉด, ์๋ช ์ฃผ๊ธฐ์ ๋ฌด๊ดํ๊ฒ ์ํ๋ฅผ ์ ์งํ ์ ์๋ค.
- ์ํ ํธ์ด์คํ
(State Hoisting)์ ์ ์ฉํ๋ค.
- ์ํ๋ฅผ ์์ ์ปดํฌ๋ํธ๋ก ๋์ด์ฌ๋ ค UI์ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๊ณ , ์ํ ๊ด๋ฆฌ ๋ก์ง์ ๋จ์ํํ๋ค.
- ๋จ์ผ ์ง์ค์ ์์ฒ(Single Source of Truth)์ ์ ์งํ๋ค.
- ์ํ๋ฅผ ์ฌ๋ฌ ๊ณณ์์ ๊ด๋ฆฌํ์ง ์๊ณ , ํ๋์ ViewModel ๋๋ Repository์์ ๊ด๋ฆฌํ์ฌ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ์ ์งํ๋ค.
- ์ด๋ฒคํธ๋ฅผ UI ์ํ์ ๋ถ๋ฆฌํ๋ค.
- ์ด๋ฒคํธ(ํด๋ฆญ, ์ ๋ ฅ ๋ฑ)๋ SharedFlow ๊ฐ์ ๋ณ๋์ ์คํธ๋ฆผ์ผ๋ก ๊ด๋ฆฌํ๊ณ , UI ์ํ์ ํผํฉํ์ง ์๋๋ก ํด์ผ ํ๋ค.
- ๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ๋ช
ํํ๊ฒ ํด์ผํ๋ค.
- Coroutine, Flow, LiveData๋ฅผ ํ์ฉํ์ฌ ๋น๋๊ธฐ ์ด๋ฒคํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ค.
๊ฒฐ๋ก
์๋๋ก์ด๋ ์ฑ์์ UI ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๋๋ ๋ช ํํ ์์น๊ณผ ํจํด์ ๋ฐ๋ฅด๋ ๊ฒ์ด ์ค์ํ๋ค.
- ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ์ ์งํ๊ณ ,
- ์ด๋ฒคํธ์ ์ํ๋ฅผ ๋ถ๋ฆฌํ๋ฉฐ,
- ๋น๋๊ธฐ ์ด๋ฒคํธ๋ฅผ ์ผ๊ด๋๊ฒ ์ฒ๋ฆฌํ๋ฉด
๋ณด๋ค ๊ฒฌ๊ณ ํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ์ฑ์ ๊ฐ๋ฐํ ์ ์๋ค.
'๐ค ์๋๋ก์ด๋ > ์ํคํ ์ฒ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์๋๋ก์ด๋] UI ์ํ ๊ด๋ฆฌ: ์ํ ํ๋(State Holder) (0) | 2025.03.30 |
---|---|
[์๋๋ก์ด๋] UI ๋ ์ด์ด ์ํคํ ์ฒ (0) | 2025.03.28 |
[์๋๋ก์ด๋] ์๋๋ก์ด๋ ์ํคํ ์ฒ ๊ถ์ฅ์ฌํญ (0) | 2025.03.26 |
[์๋๋ก์ด๋] ์๋๋ก์ด๋ ์ข ์ ํญ๋ชฉ ์ฝ์ (DI) (0) | 2025.03.25 |
[์๋๋ก์ด๋] Android 4๋ ์ปดํฌ๋ํธ๋ฅผ ๋งค๋ํ์คํธ ์์ ์ ์ธํ๋ ๋ฐฉ๋ฒ (0) | 2025.03.24 |