[Coroutine] Kotlin Coroutine ν•„μš”μ„±

πŸ’‘ λ‹€μ–‘ν•œ 비동기 처리 방법에 λŒ€ν•˜μ—¬ μ•Œμ•„λ³΄κ³ , 예제λ₯Ό ν†΅ν•΄μ„œ ν•™μŠ΅ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

μ½”νˆ¬λ¦°μ΄ μ—†λ‹€λ©΄?

일반적인 비동기 둜직

  • 일반적인 ν”„λ‘ νŠΈμ—”λ“œ(μ•ˆλ“œλ‘œμ΄λ“œ) λ‹¨μ—μ„œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‘œμ§μ„ κ΅¬ν˜„ν•˜λŠ” 방법은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.
    • λ‹€μ–‘ν•œ μ†ŒμŠ€λ‘œλΆ€ν„° 데이터λ₯Ό λ°›μŒ
      • API, λ·° κ΅¬μ„±μš”μ†Œ, λ°μ΄ν„°λ² μ΄μŠ€, λ‹€λ₯Έ μ•±
    • 데이터 처리
    • κ°€κ³΅λœ λ°μ΄ν„°λ‘œ μž‘μ—…
      • λ·° 동기화
      • λ°μ΄ν„°λ² μ΄μŠ€ μ €μž₯
      • API둜 전솑
  • 일반적인 비동기 μ—°μ‚° λΌμ΄λΈŒλŸ¬λ¦¬λŠ” μ•„λž˜μ™€ 같이 μ‚¬μš©λ©λ‹ˆλ‹€.
fun onCreate() {
    val news = getNewsFromApi()
    val sortedNews = news
        .sortedByDescending { it.publishedAt }
    view.showNews { sortedNews }
}
  • μ•ˆλ“œλ‘œμ΄λ“œμ—μ„œλŠ” ν•˜λ‚˜μ˜ μ•±μ—μ„œ λ·°λ₯Ό λ‹€λ£¨λŠ” μŠ€λ ˆλ“œκ°€ 단 ν•˜λ‚˜λ§Œ μ‘΄μž¬ν•˜λ―€λ‘œ, μœ„ μ½”λ“œλŠ” κ°€μž₯ μ€‘μš”ν•œ μŠ€λ ˆλ“œλ₯Ό λΈ”λ‘œν‚Ήν•˜κ²Œ λ©λ‹ˆλ‹€.
  • μ΄λŸ¬ν•œ λ°©μ‹μœΌλ‘œλŠ” κ΅¬ν˜„ν•œλ‹€λ©΄ μ•± ν¬λž˜μ‹œ(비정상 μ’…λ£Œ)κ°€ λ°œμƒν•  κ²ƒμž…λ‹ˆλ‹€.

μŠ€λ ˆλ“œλ‘œ μ „ν™˜

  • μŠ€λ ˆλ“œλ₯Ό ν™œμš©ν•œλ‹€λ©΄ μœ„ 문제λ₯Ό κ°€μž₯ μ§κ΄€μ μœΌλ‘œ ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λΈ”λ‘œν‚Ήμ΄ κ°€λŠ₯ν•œ μŠ€λ ˆλ“œλ₯Ό λ¨Όμ € ν™œμš©ν•˜κ³ , 이후에 메인 μŠ€λ ˆλ“œλ‘œ μ „ν™˜ν•©λ‹ˆλ‹€.
fun onCreate() {
    val news = getNewsFromApi()
    val sortedNews = news
        .sortedByDescending { it.publishedAt }
    runOnUiThread {
        view.showNews { sortedNews }
    }
}
  • μ΄λŸ¬ν•œ 방식 μ—­μ‹œ μ•„λž˜μ™€ 같은 문제λ₯Ό 가지고 μžˆμŠ΅λ‹ˆλ‹€.
    • μŠ€λ ˆλ“œκ°€ μ‹€ν–‰λ˜μ—ˆμ„ λ•Œ 멈좜 수 μžˆλŠ” 방법이 μ—†μ–΄ λ©”λͺ¨λ¦¬ λˆ„μˆ˜λ‘œ μ΄μ–΄μ§ˆ 수 있음
    • μŠ€λ ˆλ“œμ— λŒ€ν•œ λΉ„μš©κ³Ό, λ³΅μž‘λ„κ°€ 증가함
    • μ½”λ“œκ°€ 길어지고, μ΄ν•΄ν•˜κΈ° 어렀움
  • 생성 된 μŠ€λ ˆλ“œλ₯Ό μ œκ±°ν•˜μ§€ μ•ŠμœΌλ©΄, μŠ€λ ˆλ“œλŠ” 주어진 μž‘μ—…μ„ 계속 μˆ˜ν–‰ν•œ ν›„ 더 이상 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” λ·°λ₯Ό μˆ˜μ •ν•˜λ €κ³  μ‹œλ„ν•©λ‹ˆλ‹€.
  • λ°±κ·ΈλΌμš΄λ“œ μ˜ˆμ™Έλ₯Ό μœ λ°œν•˜κ±°λ‚˜ μ˜ˆμƒν•˜κΈ° μ–΄λ €μš΄ κ²°κ³Όκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

콜백 ν•¨μˆ˜ ν™œμš©

  • callback은 ν•¨μˆ˜λ₯Ό λ…ΌλΈ”λ‘œν‚Ή(non-blocking)으둜 λ§Œλ“€κ³ , ν•¨μˆ˜μ˜ μž‘μ—…μ΄ 끝났을 λ•Œ 호좜된 콜백 ν•¨μˆ˜λ₯Ό λ„˜κ²¨μ£ΌλŠ” λ°©μ‹μž…λ‹ˆλ‹€.
fun onCreate() {
    getNewsFromApi { news ->
        val sortedNews = news
            .sortedByDescending { it.publishedAt }
        view.showNews(sortedNews)
    }
}
  • μœ„μ™€ 같이 μ½œλ°±μ„ 톡해 κ΅¬ν˜„ν•œλ‹€λ©΄, 쀑간에 μž‘μ—…μ„ μ·¨μ†Œν•  수 μ—†μŠ΅λ‹ˆλ‹€.
  • μ·¨μ†Œν•  수 μžˆλŠ” μ½œλ°±λ„ κ°€λŠ₯ν•˜μ§€λ§Œ μ΄λŠ” μƒλ‹Ήνžˆ μ–΄λ €μš°λ©°, 각 ν•¨μˆ˜μ— λŒ€ν•œ μ·¨μ†Œ κ΅¬ν˜„μ„ ν•΄μ•Όν•˜λ©° λͺ¨λ“  객체λ₯Ό λΆ„λ¦¬ν•΄μ„œ λͺ¨μ•„μ•Ό ν•œλ‹€λŠ” 단점이 μžˆμŠ΅λ‹ˆλ‹€.
fun showNews() {
    getConfigFromApi { config ->
        getNewsFromApi(config) { news ->
            getUserFromApi { user ->
                view.showNews(user, news)
            }
        }
    }
}
  • μœ„ μ½”λ“œμ—μ„œλŠ” 병렬 처리λ₯Ό κΈ°λŒ€ν•˜μ§€λ§Œ, ν˜„μž¬ 두 μž‘μ—…μ„ λ™μ‹œμ— μ²˜λ¦¬ν•˜κΈ°λŠ” μ–΄λ ΅μŠ΅λ‹ˆλ‹€.
  • μ½œλ°±μ„ ν™œμš©ν• μˆ˜λ‘ λ“€μ—¬μ“°κΈ°κ°€ μƒλ‹Ήνžˆ λ§Žμ•„μ§€λ©°, 가독성이 λ–¨μ–΄μ§€λŠ” 상황이 λ°œμƒν•˜λŠ”λ° 이λ₯Ό 콜백 지μ˜₯(callback hell)이라 λΆ€λ¦…λ‹ˆλ‹€.

RxJava와 ReactorλŠ” μ–΄λ•Œ?

  • μ•ˆλ“œλ‘œμ΄λ“œμ™€ λ°±μ—”λ“œμ—μ„œ 자주 ν™œμš©λ˜λŠ” μžλ°” μ–Έμ–΄μ—μ„œλŠ” RxJava와 Reactor와 같은 λ¦¬μ•‘ν‹°λΈŒ μŠ€νŠΈλ¦Όμ„ ν™œμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 이 방법을 μ‚¬μš©ν•˜λ©΄ 데이터 슀트림 λ‚΄μ—μ„œ μΌμ–΄λ‚˜λŠ” λͺ¨λ“  연산을 μ‹œμž‘, 처리, κ΄€μ°°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ¦¬νƒν‹°λΈŒ μŠ€νŠΈλ¦Όμ€ μŠ€λ ˆλ“œ μ „ν™˜κ³Ό λ™μ‹œμ„± 처리λ₯Ό μ§€μ›ν•˜λ©°, λ³‘λ ¬ μ²˜λ¦¬κ°€ κ°€λŠ₯ν•©λ‹ˆλ‹€.

RxJava ν™œμš©

fun onCreate() {
    disposable += getNesFromApi()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .map { news ->
            news.sortedByDescending { it.publishedAt }
        }
        .subscribe { sortedNews ->
            view.showNews(sortedNews)
        }
}
  • μœ„ μ˜ˆμ œμ—μ„œ disposables λ₯Ό ν™œμš©ν•˜μ—¬ μ‚¬μš©μžκ°€ μŠ€ν¬λ¦°μ„ λΉ μ Έλ‚˜κ°„ 경우 μŠ€νŠΈλ¦Όμ„ μ·¨μ†Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • callbackκ³Ό 비ꡐ
    • λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ μ—†κ³ , μ·¨μ†Œκ°€ κ°€λŠ₯ν•©λ‹ˆλ‹€.
    • μŠ€λ ˆλ“œλ₯Ό μ μ ˆν•˜κ²Œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • λ™μ‹œμ„± 처리λ₯Ό 보μž₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • ν•˜μ§€λ§Œ RxJavaλŠ” κ΅¬ν˜„ν•˜κΈ° 맀우 λ³΅μž‘ν•˜λ‹€λŠ” 단점이 μžˆμŠ΅λ‹ˆλ‹€.
fun onCreate() {
    val news = getNewsFromApi()
    val sortedNews = news
        .sortedByDescending { it.publishedAt }
    view.showNews { sortedNews }
}
  • μœ„ μ½”λ“œλŠ” 비정상 μ’…λ£Œλ₯Ό λ°œμƒμ‹œν‚€μ§€λ§Œ, κ°€μž₯ 이상적인 μ½”λ“œμž…λ‹ˆλ‹€.
  • μœ„ μ½”λ“œμ™€ λΉ„κ΅ν–ˆμ„ λ•Œ, RxJavaλŠ” 맀우 λ³΅μž‘ν•˜λ‹€λŠ” 것을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • subscribeOn, observeOn, subscribe… λ“± RxJavaλ₯Ό ν™œμš©ν•˜κΈ° μœ„ν•΄μ„œ λ‹€μ–‘ν•œ ν•¨μˆ˜λ₯Ό λ°°μ›Œμ•Ό ν•©λ‹ˆλ‹€.
  • λ˜ν•œ μ·¨μ†Œν•˜λŠ” μž‘μ—… λ˜ν•œ λͺ…μ‹œμ μœΌλ‘œ ν‘œμ‹œν•΄μ•Όν•˜λ©°, 객체λ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λ“€μ€ Observableμ΄λ‚˜ Single 클래슀둜 λ°˜λ“œμ‹œ λž˜ν•‘(wrapping)ν•΄μ•Ό ν•œλ‹€λŠ” 단점이 μžˆμŠ΅λ‹ˆλ‹€.
  • μ΄λŠ” RxJavaλ₯Ό λ„μž…ν•˜κΈ° μœ„ν•΄μ„œ λ§Žμ€ μ½”λ“œλ₯Ό μˆ˜μ •ν•΄μ•Ό ν•œλ‹€λŠ” λœ»ν•©λ‹ˆλ‹€.

Kotlin Coroutines

  • μ½”ν‹€λ¦° 코루틴이 λ„μž…ν•΄μ•Ό ν•˜λŠ” μ΄μœ λŠ” μœ„ 단점듀을 ν•΄κ²°ν•  수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.
  • μ½”λ£¨ν‹΄μ˜ κ°€μž₯ 핡심 κΈ°λŠ₯은 νŠΉμ • μ§€μ μ—μ„œ λ©ˆμΆ”κ³  이후에 μž¬κ°œν•  수 μžˆλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€..!
  • μ΄λŠ” 메인 μŠ€λ ˆλ“œμ—μ„œ μ‹€ν–‰ν•˜κ³ , APIμ—μ„œ 데이터λ₯Ό μ–»μ–΄μ˜¬ λ•Œ μž μ‹œ 쀑단이 κ°€λŠ₯ν•˜λ‹€λŠ” μ˜λ―Έμž…λ‹ˆλ‹€.
  • 코루틴을 μ€‘λ‹¨μ‹œμΌ°μ„ λ•Œ μŠ€λ ˆλ“œλŠ” λΈ”λ‘œν‚Ήλ˜μ§€ μ•ŠμœΌλ©°, λ·°λ₯Ό λ°”κΎΈκ±°λ‚˜ λ‹€λ₯Έ 코루틴을 μ‹€ν–‰ν•˜λŠ” λ“±μ˜ 또 λ‹€λ₯Έ μž‘μ—…μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.
    • 데이터가 μ€€λΉ„λ˜λ©΄ 코루틴은 메인 μŠ€λ ˆλ“œμ—μ„œ λŒ€κΈ°ν•˜κ³  μžˆλ‹€κ°€ 메인 μŠ€λ ˆλ“œκ°€ μ€€λΉ„λ˜λ©΄ 멈좘 μ§€μ μ—μ„œ λ‹€μ‹œ μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
    • μ΄λŠ” 극히 λ“œλ¬Έ 상황이며, μŠ€λ ˆλ“œλ₯Ό 기닀리고 μžˆλŠ” 코루틴이 μŒ“μ—¬μžˆλŠ” 경우λ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
val scope = CoroutineScope(Dispatchers.Main)

fun onCreate() {
    scope.launch { updateNews() }
    scope.launch { updateProfile() }
}

suspend fun updateNews()
suspend fun updateProfile()
  • μœ„ λ™μž‘μ€ μ•„λž˜μ™€ 같은 μˆœμ„œλ‘œ μ‹€ν–‰λ©λ‹ˆλ‹€.
    1. updateNews() ν•¨μˆ˜κ°€ μ‹€ν–‰ λ©λ‹ˆλ‹€.
    2. updateNews() ν•¨μˆ˜κ°€ λ„€νŠΈμ›Œν¬ 응닡을 κΈ°λ‹€λ¦¬λŠ” λ™μ•ˆ 메인 μŠ€λ ˆλ“œλŠ” updateProfile() ν•¨μˆ˜κ°€ μ‚¬μš©ν•©λ‹ˆλ‹€.
    3. updateProfile() 호좜이 μ€‘λ‹¨λ˜μ§€ μ•ŠλŠ”λ‹€κ³  κ°€μ •ν•œλ‹€λ©΄, 메인 μŠ€λ ˆλ“œ λ‚΄μ—μ„œ λ©ˆμΆ”μ§€ μ•Šκ³  μ‹€ν–‰λ©λ‹ˆλ‹€.
    4. updateProfile() λ™μž‘μ΄ μ™„λ£Œλ˜λ©΄, 메인 μŠ€λ ˆλ“œλŠ” updateNews()λ₯Ό μ²˜λ¦¬ν•˜λŠ” 코루틴에 μ˜ν•΄ updateNews()의 μž‘μ—…μ„ μ™„λ£Œν•©λ‹ˆλ‹€.
  • 이제 μ½”ν‹€λ¦° 코루틴을 ν™œμš©ν•΄ μ•„λž˜μ™€ 같은 이상적인 λ™μž‘μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
fun onCreate() {
    viewModelScope.launch {
        val news = getNewsFromApi()
        val sortedNews = news
            .sortedByDescending { it.publishedAt }
        view.showNews { sortedNews }
    }
}

μ—¬λŸ¬ 개의 μ—”λ“œν¬μΈνŠΈλ₯Ό ν˜ΈμΆœν•˜λŠ” 경우

  • μŠ€λ ˆλ“œλ₯Ό λΈ”λ‘œν‚Ήν•˜λŠ” 문제λ₯Ό ν•΄κ²°ν–ˆμœΌλ―€λ‘œ, 이제 λ‹€μŒκ³Ό 같은 λ¬Έμ œμ— μ ‘κ·Όν•΄μ•Ό ν•©λ‹ˆλ‹€.
  • λ§Œμ•½ 3개의 μ—”λ“œν¬μΈνŠΈλ₯Ό ν˜ΈμΆœν•΄μ•Ό ν•˜λŠ” 상황이 λ°œμƒν–ˆλ‹€κ³  κ°€μ •ν•΄λ΄…λ‹ˆλ‹€.
fun showNews() {
    viewModelScope.launch {
        val config = getConfigFromApi()
        val news = getNewsFromApi(config)
        val user = getUserFromApi()
        view.showNews(user, news)
    }
}
  • μœ„ μ½”λ“œλŠ” μž‘λ™ν•˜λŠ” 방식이 νš¨μœ¨μ μ΄μ§€ μ•Šλ‹€λŠ” 단점이 μžˆμŠ΅λ‹ˆλ‹€.
  • ν˜ΈμΆœμ€ 순차적으둜 μΌμ–΄λ‚˜κΈ° λ•Œλ¬Έμ—, ν•œ μž‘μ—…μ— 1μ΄ˆμ”© κ±Έλ¦°λ‹€λ©΄ API ν˜ΈμΆœμ— 3초λ₯Ό κΈ°λ‹€λ €μ•Ό ν•©λ‹ˆλ‹€.
  • 이런 경우 async μ™€ 같은 코틀린이 μ œκ³΅ν•˜λŠ” 코루틴 라이브러리λ₯Ό ν™œμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • async() : μš”μ²­μ„ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ λ§Œλ“€μ–΄μ§„ 코루틴을 μ¦‰μ‹œ μ‹œμž‘ν•˜λŠ” ν•¨μˆ˜
    • await() : 호좜 ν›„ κ²°κ³Όλ₯Ό κΈ°λ‹€λ¦¬λŠ” ν•¨μˆ˜
fun showNews() {
    viewModelScope.launch {
        val config = async { getConfigFromApi() }
        val news = async { getNewsFromApi(config) }
        val user = async { getUserFromApi() }
        view.showNews(user.await(), news.await())
    }
}
  • μœ„ 경우λ₯Ό ν™œμš©ν•˜λ©΄ 병렬 호좜이 κ°€λŠ₯ν•˜λ©°, 2μ΄ˆλ§Œμ— μž‘μ—…μ„ 끝낼 수 μžˆμŠ΅λ‹ˆλ‹€.

for 문을 ν™œμš©ν•˜λŠ” 경우

  • μ½”νˆ¬λ¦°μ„ ν™œμš©ν•˜λ©΄ for, μ»¬λ ‰μ…˜μ„ μ²˜λ¦¬ν•˜λŠ” ν•¨μˆ˜λ₯Ό λΈ”λ‘œν‚Ή 없이 κ΅¬ν˜„μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.
fun showAllNews() {
    viewModelScope.launch {
        val allNews = (0 until getNumberOfPages())
            .map { page -> async { getNewsFromApi(page) } }
            .flatMap { it.await() }
        view.showAllNews(allNews)
    }
}
  • μœ„μ²˜λŸΌ λͺ¨λ“  λ‰΄μŠ€λ₯Ό λ³‘λ ¬μ²˜λ¦¬λ₯Ό ν†΅ν•΄μ„œ λ°›μ•„μ˜€λŠ” λ‘œμ§μ„ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

정리

  • μ½”ν‹€λ¦° 코루틴을 μ‚¬μš©ν•˜λŠ” κ°€μž₯ μ€‘μš”ν•œ μ΄μœ λŠ” μŠ€λ ˆλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” λΉ„μš©μ΄ 크게 κ°μ†Œν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.
  • μŠ€λ ˆλ“œλŠ” λͺ…μ‹œμ μœΌλ‘œ μƒμ„±λ˜κ³  μœ μ§€λ˜λ©° λ©”λͺ¨λ¦¬λ₯Ό ν• λ‹Ήν•©λ‹ˆλ‹€.
fun main() {
    // Thread ν™œμš©
    repeat(100) {
        thread {
            Thread.sleep(1000)
            print(".")
        }
    }

    // Coroutine ν™œμš©
    repeat(100) {
        launch {
            delay(1000)
            print(".")
        }
    }
}
  • μœ„ μ½”λ“œμ˜ Threadλ₯Ό ν™œμš©ν•œ 뢀뢄을 μ‚΄νŽ΄λ³΄λ©΄, λ§Žμ€ 수의 μŠ€λ ˆλ“œλ₯Ό ν™œμš©ν•΄μ„œ μŠ€λ ˆλ“œλ‹Ή 1μ΄ˆμ”© μž λ“€κ²Œ λ§Œλ“­λ‹ˆλ‹€.
    • μ—¬κΈ°μ„œ μž λ“œλŠ” λ™μž‘μ€ λ°μ΄ν„°λ² μ΄μŠ€ λ˜λŠ” λ‹€λ₯Έ μ„œλΉ„μŠ€λ‘œλΆ€ν„° 응닡을 κΈ°λ‹€λ¦¬λŠ” 상황을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
  • 이 ν”„λ‘œκ·Έλž¨μ„ 각자의 μ»΄ν“¨ν„°μ—μ„œ μ‹€ν–‰ν•œλ‹€λ©΄, λͺ¨λ“  점을 μ°λŠ” 데 μƒλ‹Ήν•œ μ‹œκ°„μ΄ 걸릴 κ²ƒμž…λ‹ˆλ‹€.
  • λ°˜λ©΄μ— Coroutine을 ν™œμš©ν•œ λΆ€λΆ„μ—μ„œλŠ” μŠ€λ ˆλ“œλ₯Ό μž¬μš°λŠ” λŒ€μ‹  코루틴을 μ€‘λ‹¨μ‹œν‚΅λ‹ˆλ‹€.
  • ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰ν•˜λ©΄ 1초 뒀에 λͺ¨λ“  점을 좜λ ₯ν•˜κ²Œ λ©λ‹ˆλ‹€.
  • 이λ₯Ό 톡해 μ €λ ΄ν•˜κ³  μ•ˆμ „ν•œ λ°©μ‹μ˜ λ™μž‘μ„ κΈ°λŒ€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

https://m.yes24.com/Goods/Detail/123034354

 

μ½”ν‹€λ¦° 코루틴 - 예슀24

μ½”ν‹€λ¦° μ „λ¬Έ 강사가 μ•Œλ € μ£ΌλŠ” μ½”ν‹€λ¦° 코루틴에 λŒ€ν•œ λͺ¨λ“  것!μ½”ν‹€λ¦° 코루틴은 효율적이고 μ‹ λ’°ν•  수 μžˆλŠ” λ©€ν‹°μŠ€λ ˆλ“œ ν”„λ‘œκ·Έλž¨μ„ μ‰½κ²Œ κ΅¬ν˜„ν•  수 있게 ν•΄ μ£Όμ–΄ μžλ°” 가상 λ¨Έμ‹ (JVM), 특히 μ•ˆλ“œλ‘œ

m.yes24.com