Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
선언형 UI에서의 상태관리
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
HyunWoo Lee
December 21, 2024
Programming
0
600
선언형 UI에서의 상태관리
GDG Devfest Songdo 2024에서 진행한 선언형 UI에서의 상태관리의 Speaker Deck입니다.
HyunWoo Lee
December 21, 2024
Tweet
Share
More Decks by HyunWoo Lee
See All by HyunWoo Lee
Navigating Dependency Injection with Metro
l2hyunwoo
1
230
How Android Uses Data Structures Behind The Scenes
l2hyunwoo
1
680
Understanding Kotlin Multiplatform (Busan)
l2hyunwoo
0
71
Understanding Kotlin Multiplatform
l2hyunwoo
0
320
파급효과: From AI to Android Development
l2hyunwoo
0
310
선언형 UI를 학습할 때 알아둬야하는 키워드들
l2hyunwoo
0
510
Essential concepts to know when learning Declarative UI
l2hyunwoo
2
1.6k
React Native under the hood
l2hyunwoo
0
160
유연한 Composable 설계
l2hyunwoo
0
740
Other Decks in Programming
See All in Programming
CSC307 Lecture 09
javiergs
PRO
1
840
MUSUBIXとは
nahisaho
0
140
CSC307 Lecture 05
javiergs
PRO
0
500
並行開発のためのコードレビュー
miyukiw
0
1.2k
フロントエンド開発の勘所 -複数事業を経験して見えた判断軸の違い-
heimusu
7
2.8k
疑似コードによるプロンプト記述、どのくらい正確に実行される?
kokuyouwind
0
390
ぼくの開発環境2026
yuzneri
0
240
今こそ知るべき耐量子計算機暗号(PQC)入門 / PQC: What You Need to Know Now
mackey0225
3
380
CSC307 Lecture 02
javiergs
PRO
1
780
15年続くIoTサービスのSREエンジニアが挑む分散トレーシング導入
melonps
2
230
「ブロックテーマでは再現できない」は本当か?
inc2734
0
1k
360° Signals in Angular: Signal Forms with SignalStore & Resources @ngLondon 01/2026
manfredsteyer
PRO
0
140
Featured
See All Featured
WCS-LA-2024
lcolladotor
0
450
The Pragmatic Product Professional
lauravandoore
37
7.1k
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.1k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
3.6k
Into the Great Unknown - MozCon
thekraken
40
2.3k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
350
Context Engineering - Making Every Token Count
addyosmani
9
670
Ten Tips & Tricks for a 🌱 transition
stuffmc
0
71
Building Adaptive Systems
keathley
44
2.9k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.3k
The untapped power of vector embeddings
frankvandijk
1
1.6k
The Cost Of JavaScript in 2023
addyosmani
55
9.5k
Transcript
Incheon ࢶഋ UIীࢲ ࢚కҙܻ - Circuitҗ Rinਸ बਵ۽ അ Android/React
Native Developer, Viva Republica(Toss) Organizer, Kotlin User Groups Seoul/GDG Incheon
࢚కҙܻ ѐ֛җ ࢎ ݾର ҙܻೡ “࢚క” ঌইࠁӝ ࢚క ઑӘ
؊ ੜ ҙܻ ೧ࠁӝ
Speakerdeck Youtube ࢶഋ UI ೨बѐ֛
࢚కҙܻ ѐ֛җ ࢎ
Before Compose class SampleActivity: AppCompatActivity() { private var name =
"" override fun onCreate(bundle: SavedInstanceState?) { super.onCreate(bundle) // ࢲߡాन APIService.getMyName().enqueue(object: Callback { override fun onResponse(ޤदӽ) { name = response.body().name updateUi() // binding.tv.text = name } }) } }
Before Compose • ѐߊо ߸ࣻܳ ߸҃ೞҊ UI ࢚కܳ ߸҃ೞب۾
٘ܳ ࢿ೧ঠೣ • ߸ࣻо ചݶ ߸҃ਸ दఃחؘ ਃبо ڄয • ചݶਸ ߸҃ೞח Ѫ ߸ࣻ ч ইצ ѐߊ
Before Compose - DataBinding class SampleActivity: AppCompatActivity() { private val
viewModel by viewModels() private lateinit var binding: ActivitySampleBinding override fun onCreate(bundle: SavedInstanceState?) { super.onCreate(bundle) binding = DataBindingUtil.setContentView(this, R.layout.ui) binding.viewModel = viewModel } } <layout> <data> <variable name=“vm” type=“com.sample.ViewModel” /> </data> <TextView <!— ࢤۚ —> android:text=“@{vm.user.nam e}” /> </layout>
Data Binding ߸ࣻܳ ߸҃݅ ೧ب ചݶ ࢚కо ߸҃ؽ ߸ࣻо ചݶ
“࢚క”о غب۾ ࢸ҅ ߑೱ ߸҃ , ചݶ ࢚క ߸҃ о ѐߊীࢲ दझమਵ۽ بؽ بੋ ࠽٘ఋ ࢚थҗ ೧ةೞӝ য۰ ী۞ झఖ ۨझ Data Binding -> View Bindingਵ۽ ֈযоח ୶ࣁ Ӓۢীب ࠛҳೞҊ ѐߊо ചݶ ߸ࣻо “࢚క”۽ ੋधೞѱ ػ ࣁ
Compose بੑ റ @Composable fun SampleText() { var text by
remember { mutableStateOf("") } LaunchedEffect(Unit) { text = APIService.getName().await() } TextField(value = text, onValueChange = { text = it }) }
Compose ߸ࣻܳ ߸҃݅ ೧ب ചݶ ࢚కо ߸҃ؽ ୶оੋ ࠽٘ కझ
হ ߸ࣻо ചݶ ࢚కо ؽ
Compose ߸ࣻо ചݶ ࢚క Android Developers - Ui State
࢚క ખ ؊ ࣁ ঌইࠁӝ
࢚కо ޤਃ? UIܳ അೞח ݽٚ ؘఠ
࢚కҙܻо ޤਃ? UIܳ അೞח ݽٚ ؘఠ ܳ ҙܻೞח ߑߨۿ
ࢤпࠁ ҙܻ೧ঠೡ ࢚క ઙܨח নೞ ഥਗоੑ ੑ۱ Form • ܴ/ݫੌ
ࣗ/࠺ߣഐ ١ ੑ۱ UI • ݫੌࣗ ࠂ ৈࠗ • ࠺ߣഐ Ѩૐ • ࠺ߣഐ ഛੋҗ ࠺ߣഐ ੌ ৈࠗ • ডҙ ز
ࢤпࠁ ҙܻ೧ঠೡ ࢚క ઙܨח নೞ
࢚కҙܻ Key Point ؘఠܳ য٣ী, যڌѱ ਤदఃחо?
࢚క৬ ࢚క
࢚క৬ ࢚క ࢚కо ־ҳীѱ ҕਬغҊ חо ೞա ஹನք && ध
ஹನքٜীѱ ҕਬ - ࢚క ஹನքٜ ࢎਊೡ ࣻ ѱ ҕਬ - ࢚క
࢚క(Global State) @Composable fun TodoTheme( darkTheme: Boolean = false,
content: @Composable () -> Unit ) { val colors = todoColors() val typography = TodoTypography() ProvideTodoColorAndTypography(colors, typography){ MaterialTheme(content = content) } }
উ٘۽٘ীࢲ ࢚కҙܻ
উ٘۽٘ীࢲ ࢚కҙܻ
Configuration Change CompositionLocal۽ ׳غח ч ী غפө Config Change۽ ч
ࠛউ೧ݶ ஹನքী ೱ -> জ উࢿ ೞ ۽ࣁझ ࢤݺӝী উਵ۽ ؘఠ द ਊ ޙઁ rememberSavableਸ ഝਊೞݶ 1MB ղ۽ ؘఠܳ ೧ঠೞחؘ ਗೞח ݅ఀ ؘఠܳ ਸ ೞо ࢚ ޅೠ Exception ఠ ࣻ উ٘۽٘ীࢲ ࢚కҙܻ
࢚కҙܻ Android OS: ॄب જؘ, ॄۄ
࢚క ੜ ҙܻೞӝ
ࠗ࠙ ࢚కҙܻ ViewModel ❤
݈ ViewModelী ֍חѱ ୭ࢶ? Configuration Changeী ٮۄࢲ ч ߸҃೧ঠೡ ࣻ
ѓ۟द Fold৬ э ҃ ਵݶ Phone UI, ಟݶ Tablet UI ࠁৈঠೞח ா झ ߊࢤ, ۠ ؘఠܳ Config Change উѱ فݶ য়۰ ী۞ ߊࢤ ܲ ஹನքٜী ࠛਃೠ ࠁܳ ֈѹ п ёח ೞѱ ҙ҅ػ ܲ ёٜী ೧ࢲ ઁೠػ ध݅ਸ оઉঠ ೠ. (Law of Demeter)
None
StateHolder Class @Stable class NiaAppState( val navController: NavHostController, coroutineScope: CoroutineScope,
networkMonitor: NetworkMonitor, userNewsResourceRepository: UserNewsResourceRepository, timeZoneMonitor: TimeZoneMonitor, ) { private val previousDestination = mutableStateOf<NavDestination?>(null) ……
StateHolder Class @Composable fun rememberNiaAppState( … ): NiaAppState { return
remember { NiaAppState( navController = navController, coroutineScope = coroutineScope, networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, timeZoneMonitor = timeZoneMonitor, )
State Holder https://developer.android.com/topic/architecture/ui-layer/ stateholders
Circuit ନݡೞӝ
Circuit? Circuit is a simple, lightweight, and extensible framework for
building Kotlin applications that’s Compose from the ground up.
Circuit? Circuit рױೞҊ о߶ݴ ഛ оמೠ ۨਕ۽, A-Zө Compose݅ਸ ഝਊೞৈ
Kotlin গܻா࣌ਸ ҳ୷ೡ ࣻ णפ.
Circuit? ࢚కҙܻ ߑध݅ਸ ۽ ঠӝ ೞӝ ٸޙী ജ҃ࢸ ١ ҳഅী
ਃೠ ࣁࠗࢎ೦ٜ ઁ৻ೞणפ
Circuit?
Circuit?
Circuit рױೠ ҳઑب
Screen data class CounterScreen(val initialCount: Int): Screen data class DetailScreen(val
id: Long): Screen data class FavoriteScreen: Screen
CircuitUiState, CircuitUiEvent sealed inte rf ace State : CircuitUiState {
data object Loading : State data object NoFavorites : State data class Results( val list: List<Favorite>, val eventSink: (Event) -> Unit ) : State } sealed inte rf ace Event : CircuitUiEvent { data class ClickFavorite(id: Long): Event }
@Composable fun CounterPresenter(): CounterState { var count by rememberSaveable {
mutableStateOf(0) } return CounterState(count) { event -> when (event) { CounterEvent.Increment -> count++ CounterEvent.Decrement -> count-- } } } Presenter
@Composable fun CounterPresenter(): CounterState { var count by rememberRetained {
mutableStateOf(0) } return CounterState(count) { event -> when (event) { CounterEvent.Increment -> count++ CounterEvent.Decrement -> count-- } } } Presenter
rememberRetained
// CircuitUiState ఋੑ expo rt inte rf ace CounterState {
value: number } // CircuitUiState ୡӝ ё const initialState: CounterState = { value: 0, } Redux Toolkitҗ Presenter
// Circuit presenter expo rt const counterSlice = createSlice({ name:
'counter', // Updateغח state initialState, reducers: { // event ࠗ࠙ increment: (state) => { state.value += 1 }, decrement: (state) => { state.value -= 1 } } ) Redux Toolkitҗ Presenter
@CircuitInject(FavoriteScreen::class, ActivityRetainedComponent::class) @Composable fun FavoritesList(state: FavoritesScreen.State) { when (state) {
Loading -> Text(text = stringResource(R.string.loading_favorites)) NoFavorites -> Text( modi fi er = Modi fi er.testTag("no favorites"), text = stringResource(R.string.no_favorites) ) is Results -> { LazyColumn { items(state.list) { Favorite(it, state.eventSink) } } } } } Screen
Circuitী ೠ рۚೠ ࣗх • ഋ জҗ э ҃, ఋੑ/ҳഅী
ೠ ӏઁٜ ࢤೞח ٘ ాੌࢿҗ оةࢿਸ ֫ৈ ࣻ • Unit Testب рױೞѱ ࢿೡ ࣻ • Compose runtimeਸ ഝਊ೧ࢲ ߈ഋ ࢚కܳ ઁೡ ࣻ ח Ѫী
Circuitী ೠ рۚೠ ࣗх • Ѧ ਊೡ ࣻ ח ഥࢎо
ݻա ਸө? • নೠ بҳٜ(Flow, Rx, Legacy View ١)җ interopਸ ೡ ࣻ ݅, ܳ interopೞӝ ਤ೧ ٘ח ݆ࣻ ҕٜࣻਸ ࢤп೧ঠؽ • ъઁ۽ ҳഅ/ࢎਊ೧ঠೞח Ѫٜ(Presenter, Navigator ١) ݆ই पઁ۽ ۽؋࣌ী ਊؼ݅ೠח ޙ
@Composable fun ScreenA() { var isB by rememberRetained { mutableStateOf(true)
} rememberRetained{ "A" } if(isB) { rememberRetained{ "B" } } else { rememberRetained{ "C" } } } takahirom/Rin
উ٘۽٘ীࢲ ࢚కҙܻ ࢚క৬ ࢚క ࢚కܳ ࠁ ؊ ੜ ҙܻೠ ߑߨ
Summary
Thank you Incheon HyunWoo Lee Android(React Native) Developer, Viva Republica(Toss)