Slide 1

Slide 1 text

Essesntial concepts to know when learning Declarative UI Korea Android HyunWoo Lee Android/React Native Developer, Viva Republica(Toss) Organizer, Kotlin User Groups Seoul/GDG Songdo

Slide 2

Slide 2 text

ࢶ঱ഋ UIܳ ೟णೡ ٸ ঌইىঠೞח ఃਕٜ٘ Korea Android ੉അ਋ Android/React Native Developer, Viva Republica(Toss) Organizer, Kotlin User Groups Seoul/GDG Songdo

Slide 3

Slide 3 text

ࢶ঱ഋ UIח ޖ঺ੋоਃ? ݾର ࢶ঱ഋ UI੄ ઱ਃ ਃٜࣗ ࢚కҙܻ੄ ޷ې - ࠺زӝ ࢚క ҙܻ

Slide 4

Slide 4 text

੉ߣ ߊ಴ח DroidKaigi 2024ীࢲ ૓೯ೠ ߊ಴ܳ ೠҴয۽ ׮द Recapೠ Ѫ੐ਸ ঌ۰٘݀פ׮ https://www.youtube.com/watch?v=txaf9F0d-aE

Slide 5

Slide 5 text

ࢶ঱ഋ UIח ޖ঺ੋоਃ?

Slide 6

Slide 6 text

ࢶ঱ഋ UIۆ ޖ঺ੋоਃ? যڌѱ Ӓ۰ঠ ೞחо? VS যڃ Ѫਸ Ӓ۰ঠ ೞחо?

Slide 7

Slide 7 text

fun sum(): Int { var result = 0 for(i in 1 . . 10) { sum += i } return result }

Slide 8

Slide 8 text

fun sum(): Int { return (1 .. 10).sum() }

Slide 9

Slide 9 text

ࢶ঱ഋ UIח ؀ࣁੑפ׮.

Slide 10

Slide 10 text

ࢶ঱ഋ UI੄ ઱ਃ ਃٜࣗ

Slide 11

Slide 11 text

੹୓ח ࠗ࠙੄ ೤੉׮ • ೞա੄ ചݶ, ೞա੄ ஹನք౟ח ஹನ ք౟੄ ઑ೤੉׮. • Custom View ٜ݅ӝ ਤ೧ ࢚ࣘ ೞ؍ җѢ੄ दрٜ਷ ੉ઁ Ӓ݅

Slide 12

Slide 12 text

fun TimeTableItem( tags: List, title: String, isLike: Boolean ) { Column() { Row() { Tags(tags = tags) Like(isLike = isLike) } SectionTitle(title = title) } }

Slide 13

Slide 13 text

struct TimeTableItem: View { var tags: [String] var title: String var isLike: Bool var body: some View { VStack { HStack { Tags(tags: tags) Like(isLike: isLike) } SectionTitle(title: title) } } }

Slide 14

Slide 14 text

function TimeTableItem({ tags, title, isLike }: Props) { return ( < / View> < / View> ) }

Slide 15

Slide 15 text

೒ۖಬ ߹ ର੉੼ • ೣࣻ ஹನք౟ vs ௿ېझ ஹನք౟ • ೣࣻ ஹನք౟ח ಁ۞޷ఠ ഑਷ ೣࣻ ղ ߸ࣻ۽ UI ҙܻ • Jetpack Compose, React • ௿ېझ ஹನք౟ח ࢤࢿ੗, ݯߡ ߸ࣻ۽ UI ҙܻ • SwiftUI, Flutter, (җѢ) React

Slide 16

Slide 16 text

Flex(FlexBox) • CSSীࢲ ࢎਊೞח ۨ੉ইਓ ߓ஖ ӝߨ ઺ ೞա • ࢶ঱ഋ UI ೒ۖಬ ؀׮ࣻо ੉ܳ ଻ఖೣ • ConstraintLayoutҗח ׮ܲ ߑधਵ۽ UIܳ ੘ࢿ೧ঠ ೞפ CSS Flex ѐ֛ਸ ޷ܻ ೠߣঀ ঌҊ оݶ ઑӘ ؊ औѱ UI ੘ࢿਸ ೡ ࣻ ੓ णפ׮.

Slide 17

Slide 17 text

Flex(FlexBox) • https://flexboxfroggy.com/ • http://www.flexboxdefense.com/

Slide 18

Slide 18 text

State৬ UI ࢎਊ੗о ਗೞח؀۽ ചݶਸ ߸҃दఆ ࣻ ੓ਸө?

Slide 19

Slide 19 text

State৬ UI • Stateܳ ഝਊೞৈ UIܳ ߸҃೧ࠁ੗ • Stateח ചݶਸ ߸҃दఆ ࣻ ੓ח ؘ੉ఠ੉׮. • Stateо ߸҃੉ غݶ ചݶ਷ ߸҃ػ׮. • ੉ ߸҃ਸ ೒ۖಬ݃׮ ࢚੉ೞѱ ࠗܰ૑݅ ؀ѐ ܻ۪؊݂(re- rendering)੉ۄ ೠ׮.

Slide 20

Slide 20 text

fun TimeTableItem( tags: List, title: String, isLike: Boolean ) { Column() { Row() { Tags(tags = tags) Like(isLike = isLike) } SectionTitle(title = title) } }

Slide 21

Slide 21 text

fun TimeTableItem( tags: List, title: String, isLike: Boolean ) { var isLike by remember { mutableStateOf(false) } Column() { Row() { Tags(tags = tags) Like( isLike = isLike, modifier = Modifier.clickable { isLike = !isLike } ) } SectionTitle(title = title) } }

Slide 22

Slide 22 text

fun TimeTableItem( tags: List, title: String, isLike: Boolean ) { var (isLike, setIsLike) = remember { mutableStateOf(false) } Column() { Row() { Tags(tags = tags) Like( isLike = isLike, modifier = Modifier.clickable { setIsLike(!isLike) } ) } SectionTitle(title = title) } }

Slide 23

Slide 23 text

function TimeTableItem({ tags, title, isLike }: Props) { const [isLike, setIsLike] = useState(false); return ( setIsLike(!isLike)} / > < / View> < / View> ) }

Slide 24

Slide 24 text

struct TimeTableItem: View { var tags: [String] var title: String @State var isLike: Bool var body: some View { VStack { HStack { Tags(tags: tags) Like(isLike: isLike) } SectionTitle(title: title) } } } struct Like: View { @Binding var isLike: Bool var body: some View { if isLike { Image(.icFavoriteFill) } else { Image( .icFavoriteOutline ) } } }

Slide 25

Slide 25 text

Re-rendering • ࢶ঱ഋ UI ೐ۨ੐ਕ௼ীࢲח ࢚కܳ ߄Բח ઱୓о ѐߊ੗ীࢲ दझ మ ೒ۖಬী ҳগ߉૑ ঋҊ ࠺तೠ ߑधਸ ഝਊ • п ೒ۖಬ݃׮ ܻ۪؊݂ ݫழפ્੉ ࢓૟ ׮ܴ • উ٘۽੉٘: (Smart) Recomposition • React(React Native): Re-rendering/Virtual • SwiftUI: ViewGroup੄ idী ੄ೠ সؘ੉౟

Slide 26

Slide 26 text

UI সؘ੉౟ ੉৻੄ ӝמٜ਷? • ૑Әө૑ ׮ܘ Ѫٜ਷ UI ۪؊݂ী ҙ۲ػ ӝמٜ੉঻਺ • ݅ডী ചݶী ૓ੑೞ੗݃੗ ࢲߡాनਸ ೧ঠೠ׮Ѣա ӂೠ ਃ୒ਸ ೧ঠೠ׮ݶ?

Slide 27

Slide 27 text

Side Effect ചݶਸ Ӓܻ૑ ঋח ӝמ ੉৻ী ݽٚ ӝמٜਸ Side Effectۄ ೠ׮.

Slide 28

Slide 28 text

fun TimeTableItem( tags: List, title: String, isLike: Boolean ) { var isLike by remember { mutableStateOf(false) } Column() { Row() { Tags(tags = tags) Like( isLike = isLike, modifier = Modifier.clickable { isLike = !isLike } ) } SectionTitle(title = title) } }

Slide 29

Slide 29 text

fun TimeTableItem( tags: List, title: String, isLike: Boolean ) { var isLike by remember { mutableStateOf(false) } LaunchedEffect(isLike) { toast(if (isLike) “੷੢೮णפ׮!” else “׮द ੷੢ೞ۰ݶ ੷੢ ߡౡਸ ׂ۞઱ࣁਃ!”) } Column() { Row() { Tags(tags = tags) Like( isLike = isLike, modifier = Modifier.clickable { isLike = !isLike } ) } SectionTitle(title = title)

Slide 30

Slide 30 text

Compose੄ Side Effectٜ • LaunchedEffect • DisposableEffect • SideEffect • etc (୶റ ࠶۽Ӓ ѱ੤)

Slide 31

Slide 31 text

function TimeTableItem({ tags, title, isLike }: Props) { const [isLike, setIsLike] = useState(false); return ( setIsLike(!isLike)} / > < / View> < / View> ) }

Slide 32

Slide 32 text

function TimeTableItem({ tags, title, isLike }: Props) { const [isLike, setIsLike] = useState(false); useEffect(() = > { alert(`${isLike ? ‘੷੢೮णפ׮’ : ‘׮द ੷੢ೞ۰ݶ ੷੢ ߡౡਸ ׂ۞઱ࣁਃ!’) }, [isLike]) return ( setIsLike(!isLike)} / > < / View> < / View>

Slide 33

Slide 33 text

function TimeTableItem({ tags, title, isLike }: Props) { useEffect( () = > { / / ਗೞח Side Effect ୊ܻ }, [isLike] ) } Dependency Array • ࠼ ߓৌ: ചݶী ૓ੑೠ ੉റ ࢎ੉٘ ੉ಖ౟ ୊ܻ • ࠺য੓૑ ঋ਷ ߓৌ(ਃࣗ A, B, etc): Aա B੄ ч੉ ߸҃غח ҃਋ী Side E ff ect ୊ܻ • ߓৌਸ ૘য֍૑ ঋ਷ ҃਋: ݒ Re-rendering݃׮ ࢎ੉٘ ੉ಖ౟ ୊ܻ

Slide 34

Slide 34 text

function TimeTableItem({ tags, title, isLike }: Props) { useEffect( () = > { () = > { / * Clean Up Function * / } }, [isLike] ) } Clean Up Function • ചݶ੉ ӝӝীࢲ ߩযզ ٸ ೧׼ ௒ߔ উী ٜয੓ח ۽૒੉ प೯ؽ

Slide 35

Slide 35 text

Compose੄ Side Effectٜ • LaunchedEffect • DisposableEffect • SideEffect • etc (୶റ ࠶۽Ӓ ѱ੤)

Slide 36

Slide 36 text

LaunchedEffect(key1, key2) { / / Side Effect } LaunchedE ff ect • (key1, key2, …etc):useE ff ect੄ Dependency Array৬ э਷ ৉ೡ • ૊ key1, key2о ߸҃غݶ ೧׼ Side E ff ect प೯ • ݅ড ചݶী ૓ੑೞ੗݃੗ Side E ff ectܳ प ೯ೞҊ र׮ݶ? • keyী ࠛ߸чਸ ֍ח׮ • true, false, Unit, etc • trailing lambdaח suspend function

Slide 37

Slide 37 text

DisposableEffect(key1, key2) { / / Side Effect onDispose { } } DisposableE ff ect • onDispose: useE ff ect੄ Clean Up Functionҗ زੌೠ ৉ೡ • trailing lambdaо non-suspend function

Slide 38

Slide 38 text

SideEffect { / / Side Effect } SideE ff ect • SideE ff ectо ݒ Recomposition݃׮ प೯ؽ

Slide 39

Slide 39 text

SwiftUI੄ Side Effectٜ • onAppear • onDisappear • onChange

Slide 40

Slide 40 text

VStack { Text(text).padding() .onAppear { } .onDisappear { } .onChange(of: text) { newValue in print("Text changed to: \(newValue)”) } Button("Change Text") { text = "Text changed \(count + 1) times" } }

Slide 41

Slide 41 text

࢚క ҕਬ ߑߨۿ • ੗ध ҅க੉ Өয૕ࣻ۾ ࠗݽ੄ ࢚కܳ ੗ध ҅கীѱ ੹׳ೞҊ ҕ ਬೞӝо ҭ੢൤ য۰ਕ૗ • ੗ध ஹನք౟ٜীѱ ؊ ੗ਬ܂ѱ ࢚కܳ ҕਬೡ ࣻ ੓ח ߑध਷ হ ਸө?

Slide 42

Slide 42 text

࢚క ҕਬ ߑߨۿ • Props Drilling • Local Scoped Data

Slide 43

Slide 43 text

Props Drilling • ੗ध ஹನք౟ীѱ ࢚కܳ ੹׳ೞӝ ਤ೧ ࢤࢿ੗ա ೣࣻ ಁ۞޷ఠ ܳ ഝਊೞৈ ੹׳ೞח Ѫ • Composeীࢲ ӂ੢ೞח ߑध • https://developer.android.com/develop/ui/compose/ state-hoisting

Slide 44

Slide 44 text

Local Scoped Data • ࠗݽ ஹನք౟ীࢲ ੗ध҅கীѱ ࢚కܳ ੹׳ೡ ࣻ ੓ח Provider ܳ ࢸ੿ೞҊ ੗ध ஹನք౟ীࢲח ੉ܳ ࢎਊೡ ࣻ ੓ب۾ ೞח ߑߨ • Reactীࢲ ઱۽ ࢎਊೞח ߑߨۿ • https://react.dev/learn/passing-data-deeply-with- context#replace-prop-drilling-with-context

Slide 45

Slide 45 text

Local Scoped Data • Android - CompositionLocal • iOS - EnvironmentValues • React(React Native) - Context

Slide 46

Slide 46 text

/ / Contextܳ ੿੄ೠ׮. const ThemeContext = createContext() / / Context Providerܳ ੿੄ೠ׮. const ThemeProvider = ({ children }) = > { const [theme, setTheme] = useState(‘light’) return ( {children} < / ThemeContext.Provider> ) }

Slide 47

Slide 47 text

/ / Contextܳ ࢎਊೞח ஹನք౟ীࢲ useContext ೣࣻܳ ഝਊೠ׮. function Component() { const { theme } = useContext() return {`Current Theme is ${theme}`} < / Text> } / / Contextܳ ࢎਊೞҊ੗ ೞח ஹನք౟ܳ Provider۽ хऴ׮. function Component() { return < / ThemeProvider> }

Slide 48

Slide 48 text

/ / CompositionLocal ੿੄ val LocalColor = staticCompositionLocalOf { Error(“No Colors”) } / / LocalColorܳ ઁҕೞח CompositionLocalProviderܳ ࢸ੿ೠ׮. CompositionLocalProvider( LocalColor provides appColors() ) { / / The components within this block can use LocalColors }

Slide 49

Slide 49 text

CompositionLocalProvider( LocalColor provides appColors() ) { DroidKnightsRow() } fun DroidKnightsRow() { val colors = LocalColors.current Row(modifier = Modifier.background(color.background)) { } }

Slide 50

Slide 50 text

CompositionLocal • staticCompositionLocalOf • CompositionLocal۽ ઁҕೞח ч੉ ࠛ߸ೠ׮Ҋ о੿ೞҊ CompositionLocal чਸ ઁҕೞח җ੿ਸ ୭੸ച • ч੉ ߸҃੉ ػ׮ݶ ੹୓ ചݶী recomposition ੌযթ • compositionLocalOf • CompositionLocal੉ ߸҃غݶ ੉ чਸ ੍ח ஹನք౟݅ Recomposition੉ ੌযթ

Slide 51

Slide 51 text

/ / EnvironmentValues۽ ղ۰ࠁյ ч੄ keyܳ ੿੄ struct ThemeKey: EnvironmentKey { static let isDarkMode: Bool = false } / / EnvironmentValues੄ ഛ੢ೣࣻܳ ੿੄೧ࢲ / / ਗೞח EnvironmentValuesܳ ࡳইৢ ࣻ ੓ѱ ࢸ੿(setter, getter) extension EnvironmentValues { var isDarkMode: Bool { get { self[ThemeKey.self] } set { self[ThemeKey.self] = newValue } } }

Slide 52

Slide 52 text

/ / EnvironmentValuesܳ .environment۽ ઁҕ struct ContentView: View { var body: some View { VStack { Child() .environment(\.isDarkMode, true) } } } / / EnvironmentValuesܳ ࢎਊೞח ੗ध ஹನք౟ח ݯߡ߸ࣻ۽ ੿੄ struct Child: View { @Environment(\.isDarkMode) var isDarkMode }

Slide 53

Slide 53 text

࢚కҙܻ੄ ޷ې - ࠺زӝ ࢚క ҙܻ

Slide 54

Slide 54 text

Single Store ӝ߈ Architecture੄ ਬ೯ • Android, iOS ন ೒ۖಬ ݽف അ੤ Single Store ӝ߈ Architecureо ൜೯ ೞҊ ੓਺ • Androidীࢲח MVI • iOSীࢲח TCA(The Composable Architecture) • ੉۠ ইఃఫ୊о ਬ೯੉ ػѱ ਋োੌөਃ?

Slide 55

Slide 55 text

Single Store ӝ߈ Architecture੄ ਬ೯ • ੉޷ Javascript ࢤక҅ীࢲח 2014֙ী ୹दೠ Cycle.jsܳ ӝ߈ ਵ۽ MVI ѐ֛੉ ؀فؽ

Slide 56

Slide 56 text

Single Store ӝ߈ Architecture੄ ਬ೯ Redux

Slide 57

Slide 57 text

૑Ә JSীࢲ੄ Reduxח?

Slide 58

Slide 58 text

Reduxח ৵ ۨѢद۽ ੋधೞҊ ੓חо? • ੺؀੸ੋ दр੉ ݆੉ ૑լ਺ (Initiial Commit 2015֙) • Boilerplate ௏٘੄ ন੉ ࢚׼൤ ݆਺ • API ਃ୒ ਽׹чী ؀ೠ ࢚క ੿੄о ࠗ੤ -> ӏѺചػ ߑध੉ হ਺ • ࠂ੟ೠ ਬ੷ दաܻয়ী ؀਽ೞӝ ਤ೧ࢲ ݆ࣻ਷ Ҋ޹җ ௏٘ ੘ࢿ

Slide 59

Slide 59 text

࠺زӝ ࢚క ҙܻ੄ ੿੄ - SWR • stale-while-revalidate • ߔӒۄ਍٘ীࢲ ࢜۽਍ ؘ੉ఠܳ ߉ইয়ח زউ நदػ ؘ੉ఠܳ ࢎਊೡ ࣻ ੓ח ૑੿ػ ӝрਸ ੄޷ • HTTP ೻؊ীࢲ ੉ ೐۽ಌ౭о ੿੄غয੓૑݅ ؀ѐ Cached ؘ ੉ఠ৬ ࢜۽਍ ؘ੉ఠ ਃ୒ਸ ҙܻೞח ߑߨۿਸ ੄޷ೣ

Slide 60

Slide 60 text

࠺زӝ ࢚క ҙܻ • ࠺زӝ ࢚కҙܻח ࢲߡীࢲ ߉ইয়ח “࠺زӝ” ؘ੉ఠܳ ҙܻೞח ߑߨ ۿ • ݒߣ ചݶী ૓ੑೡ ٸ݃׮ ࢜۽਍ ؘ੉ఠܳ ߉ই৬ঠೡө? • ੌ੿ ӝр ੉ղী ചݶী ੤૓ੑೠ׮ݶ ӝઓ ؘ੉ఠܳ ੤ഝਊ೧ب ௾ ର੉о হ਺ • நदػ ؘ੉ఠܳ ࢎਊೞח ҃਋ ҭ੢൤ ࡅܰѱ ചݶਸ ҳࢿೡ ࣻ ੓਺

Slide 61

Slide 61 text

࠺زӝ ࢚క ҙܻ • ৘द: Optimistic Update • ࢲߡо ࢿҕ੸ਵ۽ ਽׹ೡ Ѫ੉ۄ о੿ೞҊ UIܳ ޷ܻ ߸҃ೞҊ पಁೞݶ ܀ߔਸ ೞח ӝߨ

Slide 62

Slide 62 text

React Query • SWR ੿଼ਸ ӝ߈ਵ۽ ࠺زӝ ࢚కܳ ҙܻೡ ࣻ ੓ח ۄ੉࠳۞ܻ • ࠺زӝ ࢚క ҙܻܳ ਊ੉ೞѱ ೞח ӝמٜ੉ ઁҕؽ • ۽٬/ࢿҕ/पಁ ١ ࠺زӝ ࢚క੄ ӏѺ ಴ળച • ࠺زӝ ؘ੉ఠ நय • stale(ؘ੉ఠо য়ېؼ ٸ) ੗زਵ۽ நद ؘ੉ఠ Ү୓ • ъઁ நद ޖബച

Slide 63

Slide 63 text

௏٘ ৘द function Example() { const {data, isLoading, isError, refetch} = useQuery(‘index’, fetchData, { staleTime: 5000 }); if (isLoading) return Loading… < / Text> if (isError) return < / View> const invalidate = () = > { queryClient.invalidateQueries(‘index’) } return {data}Invalidate < / View> < / View> }

Slide 64

Slide 64 text

௏٘ ৘द function Example() { const {data, isLoading, isError, refetch} = useQuery(‘index’, fetchData, { staleTime: 5000 }); if (isLoading) return Loading… < / Text> if (isError) return < / View> const invalidate = () = > { queryClient.invalidateQueries(‘index’) } return {data}Invalidate < / View> < / View> }

Slide 65

Slide 65 text

௏٘ ৘द function Example() { const {data, isLoading, isError, refetch} = useQuery(‘index’, fetchData, { staleTime: 5000 }); if (isLoading) return Loading… < / Text> if (isError) return < / View> const invalidate = () = > { queryClient.invalidateQueries(‘index’) } return {data}Invalidate < / View> < / View> }

Slide 66

Slide 66 text

௏٘ ৘द function Example() { const {data, isLoading, isError, refetch} = useQuery(‘index’, fetchData, { staleTime: 5000 }); if (isLoading) return Loading… < / Text> if (isError) return < / View> const invalidate = () = > { queryClient.invalidateQueries(‘index’) } return {data}Invalidate < / View> < / View> }

Slide 67

Slide 67 text

Thank you Korea Android HyunWoo Lee Android(React Native) Developer, Viva Republica(Toss)