Android

์•ˆ๋“œ๋กœ์ด๋“œ Android ์• ๋‹ˆ๋ฉ”์ด์…˜์œผ๋กœ Flow ์ดํ•ดํ•˜๊ธฐ

๋…ธ๋ฃจ๋ฃฝ 2024. 7. 21. 21:56

 

ํ•ซ ์ŠคํŠธ๋ฆผ

  • ๋ฐ์ดํ„ฐ๋ฅผ ์†Œ๋น„ํ•˜๋Š” ๊ฒƒ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ์›์†Œ๋ฅผ ์ƒ์„ฑ
  • Coroutines Channel
  • ์ปฌ๋ ‰์…˜(List, Set)

 

์ฝœ๋“œ ์ŠคํŠธ๋ฆผ

  • ์š”์ฒญ์ด ์žˆ์„ ๋•Œ๋งŒ ์ž‘์—…์„ ์ˆ˜ํ–‰
  • Sequence, Stream
  • Flow, RxJava ์ŠคํŠธ๋ฆผ

 

Flow

  • Flow๋ฅผ ๋ฌด๊ธฐ ์•„์ดํ…œ ๊ณต์žฅ ์•ˆ์— ์žˆ๋Š” ์ปจ๋ฒ ์ด์–ด ๋ฒจํŠธ๋ผ๊ณ  ์ƒ๊ฐ๐Ÿ’จ
  • ๊ฐ๊ฐ์˜ ์•„์ดํ…œ๋“ค์„ ๋ฐ์ดํ„ฐ๋ผ๊ณ  ์ƒ๊ฐ
  • ๋ฌด๊ธฐ ์ €์žฅ๊ณ ์—์„œ ๋ฌด๊ธฐ(๋ฐ์ดํ„ฐ)๋“ค์„ ๋žœ๋ค ํ•˜๊ฒŒ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ , ๊ฐ ์ƒํ™ฉ๋“ค์„ ์ €์žฅํ•ด ๋ณด์ž!
sealed class Weapon {
    class Sword(): Weapon()
    class Staff(): Weapon()
    class SpellBook(): Weapon()
}

 

Flow ์ƒ์„ฑ

val weaponProducer: Flow<Weapon>
  • Flow๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ• → ์ฝ”๋ฃจํ‹ด ์ฑ„๋„์˜ produce์™€ ๋น„์Šทํ•œ ํ˜•ํƒœ์˜ ๋นŒ๋” flow ์„ ์–ธ
  • Flow๋Š” ๊ฐ’์ด ํ•„์š”ํ•  ๋•Œ๋งŒ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋”ฐ์ง€๊ณ  ๋ณด๋ฉด ๋นŒ๋”๋Š” ์•„๋‹ˆ๊ณ  collect() ๊ฐ™์€ ์ตœ์ข… ์—ฐ์‚ฐ ์‹œ ๋ฐ์ดํ„ฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์ •์˜ํ•œ ๊ฒƒ

 

Emit๊ณผ Collect

  • ์ƒˆ๋กœ์šด ๋ฌด๊ธฐ(๋ฐ์ดํ„ฐ)๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ๋งˆ๋‹ค weaponProducer๋Š” emit() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  • collect()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฌด๊ธฐ(๋ฐ์ดํ„ฐ)๋ฅผ ์ˆ˜์‹ 
import kotlinx.coroutines.flow.flow

private val weaponProducer: Flow<Weapon> = flow {
    emit(Weapon.Sword())
    emit(Weapon.Staff())
    emit(Weapon.SpellBook())
}
private val weaponBox = mutableListOf<Weapon>()

suspend fun main() {
    weaponProducer.collect { weapon ->
        weaponBox.add(weapon)
    }
}

 

Kotlin Flow์˜ Operators ์ข…๋ฅ˜

  1. Terminal Operators
    • Flow์˜ ์‹คํ–‰์„ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ตœ์ข… ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ
    • collect(), first(), last() ๋“ฑ
  2. Intermediate Operators
    • ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜, ํ•„ํ„ฐ๋ง ๋˜๋Š” ์กฐ์ž‘
    • Flow๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์ •์˜
    • filter, map, transform ๋“ฑ

 

Intermediate Operators

Filter

  • ํŠน์ • ์กฐ๊ฑด์„ ์ถฉ์กฑํ•˜๋Š” ์š”์†Œ๋ฅผ ์„ ํƒ
  • ๋ฌด๊ธฐ๊ฐ€ Sword์ธ ๊ฒฝ์šฐ๋งŒ collect()
weaponProducer
  .filter { weapon -> 
    weapon is Sword
  }.collect()

 

DistinctUntilChanged

  • ์—ฐ์†๋œ ์ค‘๋ณต ์š”์†Œ๋ฅผ ํ•„ํ„ฐ๋ง
  • ์ด์ „ ๊ฐ’๊ณผ ๋‹ค๋ฅธ ๊ฒฝ์šฐ์—๋งŒ emit ๊ฐ€๋Šฅ
weaponProducer
  .distinctUntilChanged()
  .collect()

 

Map

  • ๊ฐ ์ž…๋ ฅ ์š”์†Œ๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ
  • ์›๋ž˜ Flow๋‚˜ Collection์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ , ๋ณ€ํ™˜๋œ ๊ฐ’์œผ๋กœ ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“ ๋‹ค.
  • ์ผ๋ฐ˜ ๋ฌด๊ธฐ(Weapon)์—์„œ EnchantedWeapons๋กœ ๋ณ€ํ™˜
weaponProducer
  .map { weapon -> 
      mapToEnchantedWeapon(weapon)
   }.collect()

 

FlatMap

  • Map์€ ๊ฐ ์š”์†Œ๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋ณ€ํ™˜
  • FlatMap์€ ๊ฐ ์š”์†Œ๋ฅผ ๋‹ค๋ฅธ Flow๋กœ ๋ณ€ํ™˜ ํ›„ ์ด๋“ค์„ ๋ณ€ํ™˜

 

Map์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„

inline fun <T, R> Flow<T>.map(
  crossinline transform: suspend (value: T) -> R
): Flow<R>

 

FlatMap์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„

inline fun <T, R> Flow<T>.flatMapLatest(
    crossinline transform: suspend (value: T) -> Flow<R>
): Flow<R>
  • ๊ฐ๊ฐ์˜ ์š”์†Œ๋ฅผ Flow๋กœ ๋ฆฌํ„ดํ•œ ํ›„, ๊ทธ Flow๋“ค์„ ๋ณ‘ํ•ฉ
val weaponTypes: Flow<WeaponType> = queryWeaponTypes()

private fun produceWeapons(weaponType: WeaponType): Flow<Weapon> {
  // Emit weapons of the specified type
}

weaponTypes
  .flatMap { type -> 
    produceWeapons(type)
  }.collect()

  • WeaponType์ด ๋ฐฉ์ถœ๋˜๋ฉด์„œ Flow<Weapon>์ด ์ƒ์„ฑ

 

Zip

  • ๋‘ Flow๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ๋Œ€์‘ํ•˜๋Š” ์š”์†Œ๋“ค์„ Pair๋กœ ๋ฌถ์–ด ์ƒˆ๋กœ์šด ์š”์†Œ๋ฅผ ์ƒ์„ฑ
  • Flow A์˜ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ์™€ Flow B์˜ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๊ฐ€ ์ง์„ ์ด๋ฃจ๊ณ , ๋‘ ๋ฒˆ์งธ ์š”์†Œ๋ผ๋ฆฌ ์ง์„ ์ด๋ฃจ๊ณ  . . .
  • enchantments flow์™€ weapons flow๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ EnchentedWeapons๋ฅผ ์ƒ์„ฑ
val enchantments: Flow<Enchantment> = queryEnchantments()
val weapons: Flow<Weapon> = queryWeapons()

enchantments.zip(weapons) { enchantment, weapon -> 
    createMagicWeapon(enchantment, weapon)
    }

 

๊ฒฐ๋ก 

Flow๋ฅผ ์‹œ๊ฐํ™”ํ•ด์„œ ๋ณผ ์ˆ˜ ์žˆ์–ด์„œ ์‰ฝ๊ณ  ์žฌ๋ฐŒ๊ฒŒ ์ดํ•ด๊ฐ€ ๊ฐ€๋Šฅํ–ˆ๋‹ค! ๐Ÿ˜Ž

Flow๋ฅผ ์ž์œ ์ž์žฌ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์ •๋ง ํŽธ๋ฆฌํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค!!

 

ํ•ด๋‹น ํฌ์ŠคํŒ…์˜ ์›๋ฌธ์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

 

Reference

https://medium.com/@robert.baricevicpetrus/kotlin-flows-animated-55640aa48ac9

 

Kotlin Flows Animated

This article takes a playful approach to explaining Kotlin Flow, using animations that even a six-year-old can grasp.

medium.com