[Android] Coroutine Flow[1]๐Ÿค”

ํ‘ธ๋ฅธํ•˜๋Š˜ยท2022๋…„ 8์›” 18์ผ
2

flow

๋ชฉ๋ก ๋ณด๊ธฐ
1/2
post-thumbnail

๐Ÿ“ŒFlow

ํ๋ฅด๋‹ค / ์–ด๋–ค ์ผ์ด ์ง„ํ–‰์ด๋˜๋‹ค / ๋ถ„์œ„๊ธฐ๋ฅผ ํƒ€๋‹ค ์˜๋ฏธ๋ฅผ ๊ฐ€์ง„๋‹ค.

์šฐ๋ฆฌ๋Š” ๊ทธ์ค‘์— "๋ฐ์ดํ„ฐ์˜ ์ผ์ด ์ง„ํ–‰" ๋˜๋Š” ๊ณผ์ •์ธ Coroutine Flow๋ฅผ ์•Œ์•„๋ณด์ž

โœ… Coroutine Flow

river flow in you - ์ด๋ฃจ๋งˆ

"Coroutine Flow๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋‹น์‹ (User) ์—๊ฒŒ ๋ฐ์ดํ„ฐ๊ฐ€ ํ๋ฅธ๋‹ค...."

์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด Flow๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โ›ฝ๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(DB)

ํด๋ฆฐ ์•„ํ‚คํ…์ณ๋กœ ์•ฑ์„ ๋งŒ๋“ค์‹œ์— Data Layer๋ถ€๋ถ„์—์„œ Repostiroy ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋“ค์„ ๊ฐ€์ ธ์˜ค๊ฒŒ๋œ๋‹ค . ๋ฐ‘์— ๊ทธ๋ฆผ์„ ๋ณด์ž

๋ฌผ(Data)์ด ํ•„์š”ํ• ๋•Œ๋งˆ๋‹ค ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹

  • UI -> ViewModel -> Data Layer(Repository) -> ViewModel -> UI
  • ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ์ผ์ผ์ด ์š”์ฒญํ•˜์—ฌ DB์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค.
    (suspend fun ์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค)

โ—๏ธ ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉ๋งŒํ•œ๋‹ค๋ฉด ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์—†๋‹ค.

๋ฌผ(Data)์ด ์•Œ์•„์„œ ๊ด€์ฐฐํ•ด์„œ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹

  • ๋ฐ‘์— ๊ทธ๋ฆผ ์ฒ˜๋Ÿผ ์‚ฌ์šฉ๋œ๋‹ค๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์ž๋™์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜์„œ Flow๋ฅผ ํƒ€๊ณ  ๋‚ด๋ ค์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์„ โ—๏ธReactiveโ—๏ธ ๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค
  • ์™œ๋ƒํ•˜๋ฉด observer๊ฐ€ ๊ด€์ฐฐ ๋Œ€์ƒ์˜ ๋ณ€ํ™”๋ฅผ ์ž๋™ ๋Œ€์‘(Reactive) ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฐฉํ–ฅ์œผ๋กœ ํ๋ฅด๊ฒŒ ํ•˜๋Š”๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜ํ•  ๋ฐœ์ƒ์ด ์ ๊ณ 
    ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Reactive Programming

๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ ๋  ๋•Œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์†ํ•ด์„œ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹

๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๋ฐฉํ–ฅ ๋ฐฉ์‹

  • ๋ฐ‘์— ๊ทธ๋ฆผ์„ ๋ณด์‹œ๋ฉด view์—์„œ Auth ์ •๋ณด /User favaortie / items์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ทฐ์—์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ •๋ณด๋ฅผ ์—ฌ๋Ÿฌ ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ๊ฒฝ์šฐ ๋ฒ„๊ทธ๊ฐ€ ์ƒ๊ธธ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค

๋ฐ์ดํ„ฐ ๋‹จ๋ฐฉํ–ฅ ๋ฐฉ์‹

  • ๋ฐ‘์— ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ํ•œ Repository์—์„œ ์—ฌ๋Ÿฌ Data๋“ค์„ ์—ฐ๊ฒฐํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜์—ฌ ์•ˆ์ •์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์•„์›ƒ ๋ฐ ๋กœ๊ทธ์ธ์„ ํ•˜๋ฉด ๊ทธ์— ๋งž๋Š” ๊ด€(data)์„ ์ œ๊ฑฐํ•˜์—ฌ ์œ ๋™์ ์œผ๋กœ Data๋ฅผ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐ŸŽ Flow ์ƒ์„ฑํ•˜๊ธฐ

[Flow๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋Š” ์ˆœ์„œ]

๐ŸProducer(์ƒ์‚ฐ์ž)

Flow์—์„œ flow{} ๋ธ”๋ก ๋‚ด๋ถ€์—์„œ emit() ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“์œ„์น˜(DataReomteSource)

userMessageDataSource์—์„œ ์ˆ˜์‹œ๋กœ ์•ฑ์—์˜จ ๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค๋ฉด?!

  • flow{} ๋ธ”๋ก์„ ์„ ์–ธํ•œ๋‹ค
  • newsApi ์ด์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„๋กœ ๋ถ€ํ„ฐ ๋ฐ›์•„์˜จ๋‹ค
  • emit(Producer) ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค
  • 5์ดˆ(refreshIntervalMs) ๋งˆ๋‹ค ๋ฐ˜๋ณตํ•ด์„œ ๊ฐ€์ ธ์˜จ๋‹ค
class UserMessagesDataSource(
    private val newsApi: NewsApi,
    private val refreshIntervalMs: Long = 5000
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        while(true) {
            val latestNews = newsApi.fetchLatestNews()
            emit(latestNews) 
            delay(refreshIntervalMs)
        }
    }
}

๐ŸIntermediary(์ค‘๊ฐœ์ž)

์ƒ์‚ฐ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์ค‘๊ฐ„ ์—ฐ์‚ฐ์ž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •/๊ฐ€๊ณต/์˜ˆ์™ธ ์ฒ˜๋ฆฌํ•œ๋‹ค.

๐Ÿ“์œ„์น˜(Repository)

  • map (๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ˜•ํ•œ๋‹ค)
  • filter(๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง)
  • onEach(๋ชจ๋“  ๋ฐ์ดํ„ฐ๋งˆ๋‹ค ์—ฐ์‚ฐ์„ ๊ฐ๊ฐ์ˆ˜ํ–‰)

์ƒ์‚ฐ์ž(newsRemoteDataSource)์—์„œ ๋งŒ๋“  (lateNews)๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์„œ
favorite topics ํ•„ํ„ฐํ•ด์„œ ๊ฐ€์ ธ์˜จ๋‹ค์Œ ๊ฐ๊ฐ(onEach) ์ตœ์‹  ๋‰ด์Šค

class NewsRepository(
    private val newsRemoteDataSource: NewsRemoteDataSource,
    private val userData: UserData
) {
    val favoriteLatestNews: Flow<List<ArticleHeadline>> =
        newsRemoteDataSource.latestNews
            .map { it -> it.filter { userData.isFavoriteTopic(it)}
            .catch{ e ->
            	log.d("Error Loading reserved event")
            }
        
}

๐ŸConsumer(์†Œ๋น„์ž)

Collect๋ฅผ ์ด์šฉํ•ด ์ „๋‹ฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์†Œ๋น„ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“์œ„์น˜(ViewModel)

  • ๋ทฐ๋ชจ๋ธ Scope์•ˆ์—์„œ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋กœ ์‹คํ–‰๋˜์–ด ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ collectํ•˜์—ฌ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค
class LatestNewsViewModel(
    private val newsRepository: NewsRepository
) : ViewModel() {

    init {
        viewModelScope.launch {
            newsRepository.favoriteLatestNews.collect { favoriteNews ->
            //
            }
        }
    }

์—ฌ๊ธฐ๊นŒ์ง€๋Š” ๊ธฐ์ดˆ์ ์ธ Flow์— ๋Œ€ํ•ด์„œ ์‚ดํŽด ๋ณด์•˜๊ณ  ๋‹ค์ŒํŽธ์—๋Š” Flow์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์œผ๋กœ ์‚ดํŽด๋ณผ ๊ฒƒ์ด๋‹ค

0๊ฐœ์˜ ๋Œ“๊ธ€