Android Architecture - Picplz/picplz-aos GitHub Wiki
- ์๋๋ก์ด๋์์ ๊ถ์ฅํ๋ 3๊ฐ์ง ๊ณ์ธต์ ๋ฐ๋ผ ์ค๊ณ๋์์ต๋๋ค.
- MVIํจํด์ ๋ฐ๋ผ ๋ฐ์ดํฐ ํ๋ฆ์ด ๋จ๋ฐฉํฅ์ผ๋ก ์ด๋ฃจ์ด์ง๋๋ก ์ค๊ณ๋์์ต๋๋ค.
โโโ Application.kt
โโโ MainActivity.kt
โโโ data
โ โโโ model
โ โโโ repository
โ โโโ service
โ โโโ source
โโโ di
โโโ domain
โ โโโ model
โ โโโ repository
โ โโโ usecase
โโโ navigation
โ โโโ NavHost.kt
โโโ ui
โ โโโ component
โ โ โโโ login
โ โ โโโ LoginItem.kt
โ โโโ screen
โ โ โโโ common
โ โ โโโ login
โ โ โโโ LoginIntent.kt
โ โ โโโ LoginScreen.kt
โ โ โโโ LoginSideEffect.kt
โ โ โโโ LoginState.kt
โ โโโ theme
โ โโโ Color.kt
โ โโโ Font.kt
โ โโโ Theme.kt
โ โโโ Type.kt
โโโ viewmodel
MVI (Model-View-Intent) ํจํด์์๋ ๋ฐ์ดํฐ ํ๋ฆ์ด ๋จ๋ฐฉํฅ์ผ๋ก ์ด๋ฃจ์ด์ง๋ค.
์ํ ๋ณ๊ฒฝ์ Intent๋ก ๋ช ํํ ์ ์๋๋ค. View์์ ๋ฐ์ํ Intent๋ฅผ ํตํด Model์ด ๋ณ๊ฒฝ๋๊ณ , Model์ด ๋ณ๊ฒฝ๋๋ฉด ๋ฐ์ํ์ฌ View๊ฐ ๋ณ๊ฒฝ๋๋ค.
Intent์์ ์ํ ๋ณ๊ฒฝ์ด๋ผ๋ ์๋(Intent)๊ฐ ์ ์๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๊ทธ ์๋๊ฐ ๋ช ํํด์ง๋ค. ๋ฐ์ดํฐ ํ๋ฆ์ด ๋จ๋ฐฉํฅ์ด๋ฉฐ ์ถ์ ํ๊ธฐ๊ฐ ์ฝ๋ค.
UI ์ด๋ฒคํธ๊ฐ ๋ฐ๋ก ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ผ๋ก ๋ฐ์๋๋ ๊ฒ(์: ๋ฒํผ ํด๋ฆญ -> count++)๋ณด๋ค๋, ์ด๋ฌํ ์๋(Intent)๊ฐ ์ ์๋ ๊ฐ์ฒด๋ฅผ ํธ์ถํ๊ณ ์ด๋ฅผ ํตํด ๋์์ด ๋ฐ์ํ๋๋ก ํ๋ ๊ฒ(๋ฒํผ ํด๋ฆญ -> ๊ฐฏ์ ์ฆ๊ฐ -> count ++)์ด ๋ช ์์ฑ์ ๋์ ๋๋ค. ๊ธฐ์กด ์ฝ๋ ์์ฑ์์๋ ui event ์ค์ฝํ์์ ์ง์ ๋์์ด ์ด๋ฃจ์ด์ง๊ธฐ๋ณด๋ค๋, ๋ช ํํ๊ฒ ์ ์๋ ์ค๊ฐ ๋ก์ง์ ๊ฑฐ์ณ์ ๋์์ ๋ฐ์์ํค๋ ๊ฒ์ ์ ํธํ์ต๋๋ค. mvi ํจํด์ ์ด๋ฅผ ์ฒด๊ณํํ๊ณ intent๋ค์ ๋ถ๋ฆฌํด์ ๋ชจ์์ ํ์ธํ ์ ์์ด ๋ฐ์ดํฐ ํ๋ฆ๊ณผ ๊ทธ ํ๋ฆ์ ์๋๋ฅผ ๋ณด๋ค ์ฝ๊ฒ ํ์ ํ ์ ์๊ณ , ๋๋ฒ๊น ํ๊ธฐ์๋ ํธํฉ๋๋ค.
์ํ๋ ๋ถ๋ณ(Immutable)ํ๊ฒ ์ ์งํ๋ ๊ฒ์ ์์น์ผ๋ก ํ๋ค. ๋จ์ผ ์ํ ๊ด๋ฆฌ ๋ฐฉ์์ ์๋๋ก์ด๋์ Recomposition ๊ฐ๋ ๊ณผ ์ ๋ง์๋จ์ด์ง๋ค. ์์ฑ์ ์ง์ ์ ๊ทผํ์ฌ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๋์ , ๋ถ๋ณ์ ์ํ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ณ ๋ณ๊ฒฝํ ๋๋ ๊ธฐ์กด ๊ฐ์ฒด๋ฅผ ๋ณต์ ํ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํด์ ์ ์ฒด State๋ฅผ ๋ณ๊ฒฝํ๋ค. ์ด๋ก์จ ๋์์ฑ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ๊ณ , ์ค๋ ๋ ์์ ์ฑ์ ํ๋ณดํ ์ ์๋ค.
์ฑ ์ธ๋ถ ๋ฐ์ดํฐ๋ฅผ ๋ณํ์ํค๊ฑฐ๋, ์ธ๋ถ๋ก๋ถํฐ ๋ด๋ถ ๋ฐ์ดํฐ๋ฅผ ๋ณํ์ํค๋ ํ์
- mvi ํจํด์์ state๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ์ด intent์ด๋ผ ์ ์ํ์ง๋ง, state๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ ์ธ์๋ ๋ฐ์ํ๋ ์ด๋ฒคํธ๋ค์ด ์กด์ฌ, ์ด๋ฅผ ๋์ํ๋ ๊ฒ์ด side-effect
- Intent์ ํด๋นํ์ง ์์ง๋ง View์์ ๋์ํ๋ค
์์) ํ ์คํธ ์ถ๋ ฅ, ๋ค๋น๊ฒ์ดํธ๋ฅผ ํตํด ๋ค๋ฅธ ์คํฌ๋ฆฐ์ผ๋ก ์ด๋, ํ์ผ ํผ์ปค ์ถ๋ ฅ
์ด ํ๋ก์ ํธ์์๋ intent์ ํจ๊ป ์ ์ํด์ viewModel์์ ๋์์ํค์ง๋ง intent์์ ํ๋ฒ๋ sideEffect์์ ์ ์ํ ํด๋์ค๋ฅผ ๋์์ํค๋ ๋ก์ง์ ์ ์ฉ
// ui/viewmodel/SignUpClientState.kt
package com.hm.picplz.ui.screen.sign_up
import ...
data class SignUpClientState(
val currentStep: Int? = 0,
val isLoading: Boolean = false,
val error: Throwable? = null,
val userInfo: User = emptyUserData,
val nickname: String = "",
val profileImageUri: Uri? = null
){
companion object {
fun idle(): SignUpClientState {
return SignUpClientState(
currentStep = 0,
isLoading = false,
error = null,
userInfo = emptyUserData,
nickname = "",
profileImageUri = null
)
}
}
}
data class๋ฅผ ํตํด ํด๋น ์์ญ์์ ์ฌ์ฉํ state๋ค์ ์ ์ํด ์ค๋ค.
์ฝํ๋ฆฐ์์ ๊ฐ์ฒด๋ฅผ ์ ์ํด์ฃผ๋ ๋ฐฉ์
์ผ๋ฐ class
์ ๋ค๋ฅธ ์
- ์ปดํ์ผ ์ ์๋์ผ๋ก
equals()
,hashCode()
,toString()
,copy()
,componentN()
๊ณผ ๊ฐ์ ๋ฉ์๋๋ค์ ์์ฑํด์ค๋ค. - ์์ฑ์์ ํ๋กํผํฐ๋ฅผ ์ ์ํ๊ณ ํด๋์ค์ ํ๋๋ก๋ ๋ค์ ์์ฑํด์ผ ํ๋
class
์ ๋ฌ๋ฆฌ ์์ฑ์์ ํ๋กํผํฐ๋ง ์ ๋ ฅํ๋ฉด ํ๋๋ ์์ฑํด์ค๋ค. mviํจํด์์ state๋ ๋ถ๋ณํ๊ฒ ์ ์งํ ๊ฒ์ด๊ณ , ์๋ก์ด state๋ฅผ ์ ์ฉํ๊ธฐ ์ํด.copy()
๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ data class๋ฅผ ์ฌ์ฉํ๋ค.
ํด๋์ค ๋ด์์ ์ ์ธ๋ ๊ฐ์ฒด. ๋ด๋ถ์์๋ฟ ์๋๋ผ ์ธ๋ถ์์๋ ์ ์ธํ์ฌ ์ธ๋ถ์์๋ ์ ๊ทผํ ์ ์๋ ๊ฐ์ฒด ์ ์ธ
idle()
: state์ ์ด๊ธฐ ์ํ๋ฅผ ์ ์ธํ๋ ๋ฉ์๋
companion object๋ก ์ ์ธํ๊ธฐ์ ์ธ์คํด์ค๋ฅผ ์์ฑํ์ง ์๊ณ ๋ idle()์ ์ ์ธํ ์ ์๋ค.
// ui/sign_up/sign_up_client/SignUpClientViewModel
package com.hm.picplz.viewmodel
import ...
class SignUpClientViewModel : ViewModel() {
private val _state = MutableStateFlow<SignUpClientState>(SignUpClientState.idle())
val state: StateFlow<SignUpClientState> get() = _state
...
}
viewModel์ State๋ฅผ ๊ด๋ฆฌํ๊ณ , intent๊ฐ ํธ์ถ๋๋ฉด State๋ฅผ ๋ณ๊ฒฝํ๋ ์์ ์ด ์ด๋ฃจ์ด์ง๋ ์์ญ
_state
- ํ์
:
MutableStateFlow<SignUpClientState>
- ๋ณ๊ฒฝ ๊ฐ๋ฅํ ์ํ ํ๋ฆ ํ์ - ์ด๊ธฐ๊ฐ:
SignUpClientState.idle()
- ์ํ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ ๋ณ์, ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ private์ผ๋ก ์ ์ธ๋์ด ViewModel ๋ด๋ถ์์๋ง ์์ ๊ฐ๋ฅ
state
- ํ์
:
StateFlow<SignUpClientState>
- ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ์ํ ํ๋ฆ ํ์ -
get()
= ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ๊ฐ์ ๊ฐ์ ธ์ค๋ ํจ์ - view์์ ์ด state๋ฅผ ๊ตฌ๋
ํ์ฌ ์ฌ์ฉ
- view์์๋ state๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ธฐ ๋๋ฌธ์ ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ์ ๊ณต
// ui/sign_up/sign_up_client/SignUpClientIntent.kt
package com.hm.picplz.ui.screen.sign_up.sign_up_client
import ...
sealed class SignUpClientIntent {
data class SetUserInfo(val userInfo: User) : SignUpClientIntent()
data class SetNickname(val newNickname: String) : SignUpClientIntent()
data class SetProfileImageUri(val newProfileImageUri: Uri?) : SignUpClientIntent()
data object NavigateToPrev : SignUpClientIntent()
data class ChangeStep(val stepNum: Int) : SignUpClientIntent()
data object ClickSubmitButton : SignUpClientIntent()
}
Intent
๋ ์ํ๋ฅผ ์กฐ์ํ๋ ์ด๋ฒคํธ
์ด ํ์ผ ๋ด์์ ์ด ์ด๋ฒคํธ๋ค์ ๋ช ํํ๊ฒ ์ ์
์์ํ ์ ์๋ ํ์ ํด๋์ค์ ์ ์๋ฅผ ์ ํํ๋ ํด๋์ค
์ํ ๊ด๋ฆฌ๋ฅผ ํ ๋ ์ฌ์ฉํ๋ค
- ๊ฐ์ ํ์ผ ๋ด์์๋ง ํ์ ํด๋์ค๋ฅผ ์ ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ์ด ํ์ผ ๋ด์ ๋ช ํํ๊ฒ ํ๋์ ๊ณ์ธต์ผ๋ก ํ์ ํด๋์ค๋ค์ ์ ์ ๊ฐ๋ฅ
- when๋ฌธ์ ์ฌ์ฉํ ๋ ๋ชจ๋ ํ์ ํด๋์ค๋ฅผ ํ์ธํ ์ ์๋๋ก ์ปดํ์ผ๋ฌ๊ฐ ํ์ธํด์ฃผ์ด ์๊ฒฉํ ์ ์ด๊ฐ ๊ฐ๋ฅ
data class SetUserInfo(val userInfo: User) : SignUpClientIntent()
์์ : SignUpClientIntent()
๋ SetUserInfo๋ผ๋ ํ์ ํด๋์ค๊ฐ SignUpClientIntent๋ผ๋ ํด๋์ค๋ฅผ ์์ํ๋ค๋ ์๋ฏธ
-> SignUpClientIntent ํ์ ์ ๋ณ์์์ SetUserInfo๋ผ๋ ํด๋์ค๋ฅผ ์ฌ์ฉํ ์ ์๊ณ viewModel์์ ์ด๋ฅผ ์ด์ฉํด์ handleIntentํจ์์ ๋ณ์๋ก SignUpClientIntent๋ฅผ ๋ฐ๊ณ ๊ทธ ํ์ ํด๋์ค๋ก ๊ฐ๊ฐ์ ์ ์๋ ๋์๋ค์ ์ ์ดํ ์ ์๋ค.
- when๋ฌธ์ ์ด์ฉํด ํ์ ํด๋์ค์ ๊ฒฝ์ฐ๋ฅผ ๋๋๊ณ ๋ชจ๋ ํ์ ํด๋์ค๊ฐ ์กฐ๊ฑด์ผ๋ก ์ฌ์ฉ๋๋์ง ๊ฒ์ฆ ๊ฐ๋ฅ
๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ ๊ฒฝ์ฐ - data class
- ์ฌ๋ฌ ์ธ์คํด์ค๋ฅผ ์์ฑํ ์ ์๋ค
- ๋ฐ์ดํฐ๋ฅผ ๋ด๊ณ ์ ๋ฌํ๋ ์ฉ๋๋ก ์ฌ์ฉ, ์ํ ๊ด๋ฆฌ์ ์ฐ์ธ๋ค
๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์์ง ์๋ ๊ฒฝ์ฐ - data object
- ์ฑ๊ธํค : ๋จ ํ๋์ ์ธ์คํด์ค๋ง ์กด์ฌ
- ๋จ์ผ ์ํ ๊ด๋ฆฌ์ ์ฐ์
data๋ฅผ ์์ ๋ถ์ด๋ ๊ฒ์ ๋ฐ์ดํฐ ๊ด๋ฆฌ์ ์ฌ์ฉ๋๋ค๋ ๋ป์ผ๋ก sealed class(ํ์ ํด๋์ค์ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋๋ ํด๋์ค)์ ํ์ ํด๋์ค์ ์ ํฉ
- ๋ฐ์ดํฐ ๊ด๋ฆฌ์ ํ์ํ ๋ฉ์๋(equals(), hashCode(), toString())๋ค์ ์ ๊ณต
ํ์ ํด๋์ค๋ค์ ์ํ ๊ด๋ฆฌ์ ์ฐ์ด๋ sealed class
์์ํด๋์ค๋ก Intent ์ ์, ๊ทธ ์์ ํด๋์ค๋ฅผ ์์๋ฐ์ ํ์ ํด๋์ค๋ค์ ๊ฐ ๋์๋ค์ ์ ์ํ๋๋ฐ ๋ฐ์ดํฐ๊ฐ ์๋ ๊ฒฝ์ฐ data class, ์๋ ๊ฒฝ์ฐ data object๋ก ์ ์
class SignUpClientViewModel : ViewModel() {
private val _state = MutableStateFlow<SignUpClientState>(SignUpClientState.idle())
val state: StateFlow<SignUpClientState> get() = _state
...
fun handleIntent(intent: SignUpClientIntent) {
when (intent) {
is SetUserInfo -> {}
is SetNickname -> {
val newNicknameState = _state.value.copy(nickname = intent.newNickname)
_state.value = newNicknameState
}
is SetProfileImageUri -> {
val newProfileImageUrlState = _state.value.copy(profileImageUri = intent.newProfileImageUri)
_state.value = newProfileImageUrlState
}
is NavigateToPrev -> {
...
}
is ChangeStep -> {
val newStepState = _state.value.copy(currentStep = intent.stepNum)
_state.value = newStepState
}
is ClickSubmitButton -> {
...
}
}
}
}
handleIntent
๋ผ๋ ํจ์๋ฅผ ์ ์ธํ๊ณ intent๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์ ์๋๋ก ์ค์ , intent์ ํ์
์ intentํ์ผ์์ ์์ ์ ์ํ SignUpClientIntent ํด๋์ค๋ค.
view์์ viewModel.handleIntent({์์ ํด๋์ค}.{์์๋ฐ์ ํ์ ํด๋์ค})
๋ก ํธ์ถํ๋ค.
// ui/sign_up/sign_up_client/SignUpClientScreeen
...
onClickBackIcon = { viewModel.handleIntent(SignUpClientIntent.NavigateToPrev) },
...
when์์ ๊ฐ ๋์๋ณ๋ก ๊ฒฝ์ฐ๋ฅผ ๋๋ ์ ๋์์ ์ค์ ํ ์ ์๋ค
- sealed class๋ก ํ๊ธฐ ๋๋ฌธ์ ๋ชจ๋ ํ์ ํด๋์ค๋ค์ ์กฐ๊ฑด์ ๊ฑธ์ด์คฌ๋์ง ๊ฒ์ฌํด์ค๋ค
mvi ํจํด์์ state ๊ฐ์ฒด๋ ๋ถ๋ณํจ์ ์ ์งํด์ผํ๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์
// don`t
is SetNickname -> {
_state.value.nickname = intent.newNickname
}
์ฒ๋ผ ์์ฑ์ ์ ๊ทผํด์ ๋ณ๊ฒฝํ๋ ๊ฒ์ด ์๋
// do
is SetNickname -> {
_state.update{ it.copy(nichname = intent.newNickname) }
}
์ด๋ ๊ฒ ์์ ํ๊ณ ์ ํ๋ ์์ฑ์ ๋ณ๊ฒฝํ ๋ณต์ ๋ ์ ์ฒด state ๊ฐ์ฒด๋ฅผ ๋ง๋ ํ ์ ์ฒด state๋ฅผ ๋ฐ๊พธ์ด์ค๋ค.
- state๋ฅผ data class๋ก ์ ์ธํ๊ธฐ ๋๋ฌธ์ .copy()์ ๊ฐ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
package com.hm.picplz.ui.screen.sign_up.sign_up_client
import android.net.Uri
sealed class SignUpClientSideEffect {
data class SubmitProfileInfo(
val nickname: String,
val profileImageUrl: Uri?,
) : SignUpClientSideEffect()
data object ShowFileUploadDialog : SignUpClientSideEffect()
data object NavigateToPrev : SignUpClientSideEffect()
}
Intent
์ ์ํ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ์ฒด seal class๋ก ์ ์ฒด ํด๋์ค๋ฅผ ์ ์ํ๊ณ ๋ฐ์ดํฐ ๋ณด์ ์ฌ๋ถ์ ๋ฐ๋ผ data class์ data object๋ก ๊ฐ ๋์์ ํด๋นํ๋ ํ์ ํด๋์ค๋ค ์ ์
package com.hm.picplz.viewmodel
import ...
class SignUpClientViewModel : ViewModel() {
...
private val _sideEffect = MutableSharedFlow<SignUpClientSideEffect>()
val sideEffect: SharedFlow<SignUpClientSideEffect> get() = _sideEffect
fun handleIntent(intent: SignUpClientIntent) {
when (intent) {
is SetUserInfo -> {}
is SetNickname -> {
...
}
is SetProfileImageUri -> {
...
}
is NavigateToPrev -> {
viewModelScope.launch {
_sideEffect.emit(SignUpClientSideEffect.NavigateToPrev)
}
}
is ChangeStep -> {
...
}
is ClickSubmitButton -> {
viewModelScope.launch {
_sideEffect.emit(
SignUpClientSideEffect.SubmitProfileInfo(
nickname = _state.value.nickname,
profileImageUrl = _state.value.profileImageUri,
)
)
}
}
}
}
}
_sideEffect
- ํ์ : MutableSharedFlow
- ์ด๋ฒคํธ๋ฅผ emit์ํค๊ณ collector๋ค์๊ฒ ์ ๋ฌํ๋ ์ญํ
- ๋น๋๊ธฐ์ ์ผ๋ก ์คํ
-
MutableSharedFlow
- ์ด๋ฒคํธ ๊ด๋ฆฌํ ๋ ์ฌ์ฉ flow
- ์ฌ๋ฌ event๋ฅผ ์ฐ์์ ์ผ๋ก emitํ ์ ์๋ค
- ๋จ๋ฐ์ ์ผ๋ก ๋ฐ์ํ๊ณ collector๊ฐ ์๋ ๊ฒฝ์ฐ emitํ ์ด๋ฒคํธ๋ ์ฌ๋ผ์ง๋ค
- ์ฌ๋ฌ collector๋ฅผ ๋๊ณ event๋ฅผ ๋ฐ๊ฒ๋ ํ ์ ์์
- replay์ buffer ์ค์ ์ ํตํด ์ด๋ฒคํธ๋ฅผ ์ค์ ๋ ๋ฒ์ ๋ด์์ ๋ค์ ๋ฐ์ ์ ์๋๋ก ๊ด๋ฆฌ
-
MutableStateFlow
(state์ ์ฌ์ฉ๋ flow)- ์ํ ๊ด๋ฆฌ์ ์ฃผ๋ก ์ฌ์ฉํ๋ flow
- ์ต์ ์ state๋ฅผ ์ ์ง(๋ฐ๋๋ฉด ์ด์ ์ ๊ฐ์ ์ ์ง๋์ง ์์)
- ์ด๊ธฐ๊ฐ์ด ํ์ํ๊ณ , collector๊ฐ ์์ด๋ ์ํ ์กด์ฌ
- ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋, collector๋ค์๊ฒ ์ต์ ์ state ์ ๋ฌ
package com.hm.picplz.ui.screen.sign_up.sign_up_client
import ...
@Composable
fun SignUpClientScreen(
modifier: Modifier = Modifier,
navController: NavController,
userInfo: User = emptyUserData,
viewModel: SignUpClientViewModel = viewModel(),
) {
val currentState = viewModel.state.collectAsState().value
Scaffold(
modifier = Modifier.fillMaxSize(),
containerColor = MainThemeColor.White
) { innerPadding ->
when (currentState.currentStep) {
0 -> SignUpClientNicknameView(
modifier = modifier,
currentState = currentState,
viewModel = viewModel,
innerPadding = innerPadding
)
1 -> SignUpClientProfileImageView(
modifier = modifier,
currentState = currentState,
viewModel = viewModel,
innerPadding = innerPadding
)
else -> {}
}
}
val context = LocalContext.current
LaunchedEffect(Unit) {
viewModel.sideEffect.collectLatest { sideEffect ->
when (sideEffect) {
is SignUpClientSideEffect.SubmitProfileInfo -> {
Toast.makeText(context, "๊ฐ์
", Toast.LENGTH_SHORT).show()
}
is SignUpClientSideEffect.ShowFileUploadDialog -> {}
is SignUpClientSideEffect.NavigateToPrev -> {
navController.popBackStack()
}
}
}
}
}
side effect๋ context, navController๋ฑ view์์ ์กฐ์ํด์ผ ํ๋ ์์๋ค์ ์ฃผ๋ก ์ฌ์ฉํ๋ ui ์์์ด๊ธฐ ๋๋ฌธ์ view์์ ๋์์ ๋ฐ์์ํจ๋ค viewModel์์ sideEffect๊ฐ emitํ ์ด๋ฒคํธ๋ฅผ collectํ๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ๊ฐ ์ด๋ฒคํธ์ ๋ฐ๋ผ ์คํ๋๋ ๋์ ์ฝ๋๊ฐ ์์ฑ๋๋ค.
์๋๋ก์ด๋ ์ ํธํฉ compose์ composable์ด ์์ฑ๋ ๋ ์คํ๋๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ ํจ์ key๊ฐ์ด ๋ณํ ๋๋ง๋ค ์คํ
LaunchedEffect(Unit) {
// ์คํ
}
- key๊ฐ์ Unit(์๋ฌด๊ฒ๋ ์๋ ๊ฒ์ ์๋ฏธํ๋ ๊ฐ์ฒด)์ ๋ฃ์ ๊ฒฝ์ฐ Composable์ด ์ฒ์ ๋ ๋๋ง ๋ ๋ ํ๋ฒ๋ง ์คํ๋๋ค
Flow๋ฅผ collectํ๋ ์ฐ์ฐ์ ๊ฐ์ฅ ์ต์ ๋ฐ์ดํฐ๋ฅผ collect, ๊ธฐ์กด ์์
์ค์ผ ๋์๋ ์๋ก์ด ์ต์ ๋ฐ์ดํฐ๊ฐ collect๋๋ฉด ์ต์ ๋ฐ์ดํฐ์ ๋ฐ์
๋ง์ฝ ํน์ side-effect๊ฐ ์คํ์ค์ธ๋ฐ ๋ค๋ฅธ side-effect๋ฅผ ์คํ์ํฌ(collectํ )๊ฒฝ์ฐ ๊ธฐ์กด ์์
์ ์ทจ์ํ๊ณ ์๋ก์ด side-effect
์ทจ์ํ์ง ์๊ณ ์์ฐจ์ ์ผ๋ก ์คํํ๊ณ ์ ํ ๋๋ collect
์ฌ์ฉ
https://developer.android.com/develop/ui/compose/documentation?hl=ko