[TECH] Kotlin Coroutineμ μ¨μΌνλ μ΄μ
[ν°λ] ν°λ Android Kotlin Coroutineμ μ¨μΌ νλ μ΄μ μ μ¬μ©λ²
μλ
νμΈμ ν°νμ μλλ‘μ΄λ κ°λ°μ μ μ±νμ
λλ€ππππ
μ΄λ²μλ λ©κ²λ§ λκ»΄μ§κ³ μ²μ 보면 μ΄λ ΅κ² λκ»΄μ§λ κ°λ μ μ λ μ 리νλ©΄μ νμΈνλ μκ°μ κ°μ Έλ³΄λ €κ³ ν©λλ€!
μλλ‘μ΄λλ₯Ό κ°λ°νλ μ¬λμ΄λΌλ©΄ Coroutineμ λ€μ΄λ³΄μ ¨μ κ±°λΌ μκ°ν©λλ€.
Coroutineμ 무μμ΄κ³ λΉλκΈ° μ²λ¦¬λ 무μμΈμ§ μμλ³΄λ¬ κ°λ΄ μλ€.π¨
π©π» Coroutine? μ½λ£¨ν΄μ΄ λμΌ?
μ½λ£¨ν΄μ μμΆν΄μ μ€λͺ νμλ©΄ μ¬μ΄ λΉλκΈ° μ²λ¦¬λ‘ μ½λλ₯Ό μ½κ³ κ°λ μ± λκ² μμ±ν μ μλ νΉμ§μ κ°μ§ λꡬμ λλ€.
μ½λ£¨ν΄(Coroutines)μ Kotlinμμ λμμ±μ κ°λ¨νκ² μ²λ¦¬ν μ μκ² νλ κΈ°λ₯ μ€ νλμ λλ€.
μ½λ£¨ν΄μ λΉλκΈ° μ½λλ₯Ό λκΈ°μ μΌλ‘ μμ±νλ κ²μ²λΌ 보μ΄κ² νμ¬ μ½λμ κ°λ μ±μ λμ΄κ³ , 볡μ‘ν μ½λ°±μ΄λ λ€λ₯Έ λμμ± ν¨ν΄μ λΉν΄ μ½κ² μμ±ν μ μκ² ν©λλ€.
μ½λ£¨ν΄μ μν€νΌλμμμ μλμ κ°μ΄ μ μνκ³ μμ΅λλ€.
μ€νμ μ§μ°κ³Ό μ¬κ°λ₯Ό νμ©ν¨μΌλ‘μ¨, λΉμ μ μ λ©ν°νμ€νΉμ μν
μλΈ λ£¨ν΄μ μΌλ°νν μ»΄ν¨ν° νλ‘κ·Έλ¨ κ΅¬μ±μμ
*λΉμ μ μ λ©ν°νμ€νΉ
- μ μ ν : taskκ° cpuλ₯Ό μ¬μ©νκ³ μλλΌλ μ΄κ±Έ λΊμ΄μ μ€λ¨μν¬ μ μμ΅λλ€.
- λΉμ μ ν : taskκ° cpu μ¬μ©κΆμ ν λΉλ°μμ λ μ€μΌμ€λ¬κ° κ°μ λ‘ cpuμ¬μ©κΆμ λΊμ μ μμ΅λλ€.
μ½λ£¨ν΄μ λ³νμ±μ μ 곡νμ§λ§, λ³λ ¬μ±μ μ 곡νμ§ μμ΅λλ€.
- λ³νμ±(Concurrency) : 물리μ μΌλ‘ λ³λ ¬λ‘ μμ μ΄ μ€νλλ κ²μ μλμ§λ§ κ·Έλ κ² λ³΄μ΄λ κ² (λ Όλ¦¬μ μΌλ‘ λ³λ ¬λ‘ μμ μ΄ μ€νλλ κ²μ²λΌ 보μ΄λ μ²λ¦¬ = λμμ±)
- λ³λ ¬μ±(Parallelism) : 물리μ μΌλ‘ λ³λ ¬λ‘ μμ μ΄ μ€νλλ κ² (μ€μ λ‘ λμμ μμ μ΄ μ²λ¦¬)
π©π» μ½λ£¨ν΄μ μ μ¨μΌ νλλ°?
Task1, Task2, Task3 μ΄ μλ£λμ΄μΌ μ¬μ©μκ° κ°μ λ³΄λ €λ©΄ μ΄ 28μ΄κ° κ±Έλ¦¬κ² λ©λλ€.(11+14+3 = 28)
μλ²μμ ν° λ°μ΄ν°λ₯Ό κ°μ Έμ€λ κ²½μ°λ₯Ό μκ°ν΄λ³΄λ©΄ μ¬μ©μ±μ΄ κ΅μ₯ν λ¨μ΄μ§κ² λκ² μ£ ?
λ무 λ§μ΄ κΈ°λ€λ¦¬κ² λλ€λ©΄ μΌλΆ μ¬μ©μλ νλ₯Ό λ΄λ©° μ±μ μ κ±°ν μλ μμ΅λλ€ π€―
μ΄ λͺ¨λ λ°μ΄ν°λ₯Ό λ³λ ¬λ‘ λ€μ΄λ‘λν μ μλ€λ©΄ μ΄λ¨κΉμ? μ΄ 14μ΄λ©΄ λ°μ΄ν°λ₯Ό μ²λ¦¬ν μ μμ΅λλ€.
νμ§λ§ λΉ λ₯΄λ€κ³ λͺ¨λ κ² μ’μ 건 μλλλ€!
ꡬ쑰νλμ§ μμ λμμ±μ λͺ¨λ μμ μ μλ£λ₯Ό 보μ₯νμ§ μκΈ° λλ¬Έμ μ€μ λ‘ μμ μ½λ£¨ν΄μ΄ μλ£λ νμλ
νμ μ½λ£¨ν΄μ κ³μ μ€νλ μ μμ΅λλ€.
μ΄λ° κ²½μ°λ μμΈ‘νμ§ λͺ»ν κ° λλ μ€λ₯κ° λ°μν μ μμ΅λλ€.
κ·Έλ κΈ° λλ¬Έμ μ½λ£¨ν΄μ λΉλκΈ° λΉλλ₯Ό μ¬μ©νκ³ λ°νκ°μ wait ν¨μ νΈμΆμ μ¬μ©νλ λ±μ λ°©μμΌλ‘ ν΄κ²°ν μ μμ΅λλ€.
μ€λͺ μ μν΄ μ½λ£¨ν΄μ λν λ μμΈν λ΄μ©μΌλ‘ μ΄μ΄κ°κ² μ΅λλ€.
π©π» μ½λ£¨ν΄μ μ΄λ»κ² μ¬μ©νμ§?
μμ‘΄μ± μ£Όμ build.gradle
coroutine_version = "1.6.0"
//coroutine
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
μ½λ£¨ν΄μ μ£Όμ μμ:
- suspend : suspend funμΌλ‘ μ μΈν ν¨μλ μ½λ£¨ν΄ λ΄μμλ§ νΈμΆλ μ μμ΅λλ€. μ΄ ν¨μλ μ€κ°μ μΌμ μ€μ§λ μ μκ³ λμ€μ λ€μ μμλ μ μμ΅λλ€.
- launch: μ½λ£¨ν΄μ μμνκΈ° μν ν¨μμ λλ€. μ£Όλ‘ λΉλκΈ° μμ μ μ¬μ©λ©λλ€.
- async/await: λΉλκΈ° μμ μ μμνκ³ κ²°κ³Όλ₯Ό κΈ°λ€λ¦¬κΈ° μν ν¨ν΄μ λλ€.
- runBlocking: μ£Όλ‘ ν μ€νΈλ λ©μΈ ν¨μμμ μ¬μ©λλ λΈλ‘νΉ ν¨μμ λλ€.
μ½λ£¨ν΄ Dispatchers:
μ½λ£¨ν΄μ νΉμ CoroutineDispatcherμμ μ€νλ©λλ€. μ©λμ λ§λ Dispatcherλ₯Ό μ¬μ©ν΄μ μμ νλ©΄ λ©λλ€.
- Dispatchers.Main: μ£Ό UI μ€λ λμμ μμ μ μ€ννκΈ° μν λμ€ν¨μ²μ λλ€. UI μ λ°μ΄νΈλ μ λλ©μ΄μ λ±μ μμ μ μ ν©ν©λλ€. Androidμμλ Dispatchers.Mainμ΄ λ©μΈ μ€λ λμ μ°κ²°λ©λλ€. => UI μμ
- Dispatchers.IO :μ μΆλ ₯ μμ (λ€νΈμν¬ νΈμΆ, λ°μ΄ν°λ² μ΄μ€ μμ λ±)μ μ΅μ νλ λμ€ν¨μ²μ λλ€. => λ€νΈμν¬λ λ°μ΄ν°λ² μ΄μ€ μμ
- Dispatchers.Default: CPU μ§μ€μ μΈ μμ μ μ΅μ νλ λμ€ν¨μ²μ λλ€. μλ₯Ό λ€μ΄, ν° λ¦¬μ€νΈμ μ λ ¬μ΄λ κ³μ° μμ λ±μ μ¬μ©λ©λλ€. => CPU μ§μ€μ μΈ μμ
- Dispatchers.Unconfined: νΉμ μ€λ λμ μ’ μλμ§ μλ λμ€ν¨μ²μ λλ€. μ£Όλ‘ ν μ€νΈ μ©λλ‘ μ¬μ©λ©λλ€. => ν μ€νΈ
ex. launchλ₯Ό μ¬μ©νμ¬ μ½λ£¨ν΄μ μμνκ³ , Dispatchers.IOμμ λ€νΈμν¬μμ λ°μ΄ν°λ₯Ό κ°μ Έμ¨ ν, Dispatchers.Mainμμ UIλ₯Ό μ λ°μ΄νΈνλ κ²½μ°
suspend fun fetchDataFromNetwork(): String { //suspend fun -> μ½λ£¨ν΄λ΄μμλ§ νΈμΆ
// λ€νΈμν¬ λλ DBμ κ°μ λΉλκΈ° μμ
μ κ°μ
delay(1000) // λΉλκΈ° μ§μ° ν¨μλ‘ 1μ΄ μ§μ°μ λνλ
λλ€.
return "λ°μ΄ν° λ°μμ€κΈ° μλ£"
}
fun updateUI(data: String) {
println("UI μ
λ°μ΄νΈ: $data")
}
fun main() {
runBlocking { // ν
μ€νΈ λ° μμ λͺ©μ μΌλ‘ runBlocking μ¬μ©
launch(Dispatchers.IO) {
// IO μμ
μν
val data = fetchDataFromNetwork()
withContext(Dispatchers.Main) {
// UI μ
λ°μ΄νΈ
updateUI(data)
}
}
}
}
π©π» μ½λ£¨ν΄ μ μ©νκΈ°
μ½λ£¨ν΄μ λν κΈ°λ³Έ μ΄ν΄κ° λμλ€λ©΄ μ€μ λ‘ μ°λ¦¬ μ½λμμ μ½λ£¨ν΄μ μμ±νκΈ° μν΄ λ μμλ³΄κ² μ΅λλ€.
(λ΄μ©μ΄ κΉλλ€.. μλμ μμ½λ³Έ μμ΅λλ€.)
- withContext: μ£Όλ‘ νμ¬ μ½λ£¨ν΄μ μ€ν 컨ν μ€νΈλ₯Ό μμλ‘ λ³κ²½νλ λ° μ¬μ©λ©λλ€.
- CoroutineScope: μ½λ£¨ν΄μ μλͺ μ£ΌκΈ°λ₯Ό λͺ μμ μΌλ‘ κ΄λ¦¬νκ³ μ μ΄νλ λ° μ¬μ©λ©λλ€.
- ViewModelScope: Android ViewModelμ λ΄μ₯λ μ€μ½νλ‘, ViewModelμ μλͺ μ£ΌκΈ°μ μλμΌλ‘ μ°κ²°λ©λλ€.
κΈ°λ³Έμ μΌλ‘ withContextλ μμ μ μ€ν 컨ν μ€νΈλ₯Ό λ³κ²½νλ λ°, CoroutineScopeμ ViewModelScopeλ μ½λ£¨ν΄μ μλͺ μ£ΌκΈ°λ₯Ό κ΄λ¦¬νλ λ° μ΄μ μ λ‘λλ€.
1. withContext
- μ€λͺ : withContextλ μ½λ£¨ν΄μ μ€ν 컨ν μ€νΈλ₯Ό λ³κ²½νλ λ° μ¬μ©λ©λλ€. μ£Όλ‘ λ€λ₯Έ Dispatcherλ‘ μ½λ£¨ν΄μ μ΄λνμ¬ μμ μ μ€νν λ μ¬μ©λ©λλ€.
suspend fun fetchDataFromDatabase(): Data {
return withContext(Dispatchers.IO) {
// DBλ‘λΆν° λ°μ΄ν°λ₯Ό κ°μ Έμ΄
database.getData()
}
}
2. CoroutineScope
- μ€λͺ : CoroutineScopeλ μ½λ£¨ν΄μ μλͺ μ£ΌκΈ°λ₯Ό μ μ΄νκ³ κ΄λ¦¬νλ λ° μ¬μ©λ©λλ€. νΉμ μ€μ½ν λ΄μμ μμλ μ½λ£¨ν΄μ ν΄λΉ μ€μ½νμ μλͺ μ£ΌκΈ°μ μ’ μλ©λλ€.
- μμ: Activityλ Fragmentμ μλͺ μ£ΌκΈ°μ μ½λ£¨ν΄μ μλͺ μ£ΌκΈ°λ₯Ό λκΈ°ννκ³ μ ν λ μ¬μ©λ©λλ€.
class MyActivity : AppCompatActivity(), CoroutineScope by CoroutineScope(Dispatchers.Main) {
override fun onDestroy() {
super.onDestroy()
cancel() // λͺ¨λ μ½λ£¨ν΄ μ·¨μ
}
fun loadData() {
launch {
// λ°μ΄ν° λ‘λ© λ‘μ§
}
}
}
3. ViewModelScope
- μ€λͺ : ViewModelScopeλ Androidμ ViewModelκ³Ό μ°κ²°λ μ½λ£¨ν΄ μ€μ½νμ λλ€. ViewModelμ΄ ν΄λ¦¬μ΄λ λ ν΄λΉ μ€μ½ν λ΄μ λͺ¨λ μ½λ£¨ν΄λ μλμΌλ‘ μ·¨μλ©λλ€. μ΄λ₯Ό ν΅ν΄ λ©λͺ¨λ¦¬ λμλ νμ μλ μμ μ μ€νμ λ°©μ§ν μ μμ΅λλ€.
- μμ: ViewModel λ΄μμ λ°μ΄ν°λ₯Ό λ‘λν λ μ¬μ©λ©λλ€.
- kotlinCopy code class MyViewModel : ViewModel() { fun loadData() { viewModelScope.launch { // λ°μ΄ν° λ‘λ© λ‘μ§ } } }
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
// λ°μ΄ν° λ‘λ© λ‘μ§
}
}
}
async await
asyncμ awaitλ μ½λ£¨ν΄μμ λΉλκΈ° μμ μ κ²°κ³Όλ₯Ό κ°μ Έμ€κΈ° μν΄ μ¬μ©λλ ν¨ν΄μ λλ€. μ£Όλ‘ μ¬λ¬ λΉλκΈ° μμ μ λμμ μμνκ³ κ·Έ κ²°κ³Όλ₯Ό λͺ¨μμ μ²λ¦¬ν λ μ μ©ν©λλ€.
- async: μ΄ ν¨μλ μ£Όλ‘ κ²°κ³Όλ₯Ό λ°ννλ λΉλκΈ° μμ μ μμνκΈ° μν΄ μ¬μ©λ©λλ€. asyncλ‘ μμλ μ½λ£¨ν΄μ Deferred κ°μ²΄λ₯Ό λ°ννλ©°, μ΄ κ°μ²΄λ₯Ό ν΅ν΄ λμ€μ κ²°κ³Όλ₯Ό κ°μ Έμ¬ μ μμ΅λλ€.
- await: Deferred κ°μ²΄μμ μ€μ κ²°κ³Όλ₯Ό κ°μ Έμ€κΈ° μν΄ μ¬μ©λλ ν¨μμ λλ€. μ΄ ν¨μλ ν΄λΉ λΉλκΈ° μμ μ΄ μλ£λ λκΉμ§ νμ¬μ μ½λ£¨ν΄ μ€νμ μΌμ μ€λ¨ν©λλ€.
μ¬μ© μ:
suspend fun fetchData(): Deferred<String> = coroutineScope {
async {
delay(1000) // 1μ΄ λμ μ§μ°
"Data Fetched"
}
}
suspend fun main() {
val deferredData: Deferred<String> = fetchData()
println("Doing some other work...")
val data: String = deferredData.await() // μ¬κΈ°μμ μ½λ£¨ν΄μ fetchDataμ κ²°κ³Όλ₯Ό κΈ°λ€λ¦½λλ€.
println(data) // "Data Fetched"λ₯Ό μΆλ ₯ν©λλ€.
}
OnCleared
ViewModel λ΄λΆμμ μ½λ£¨ν΄μ μ¬μ©ν λ onClearedλ₯Ό μ€λ²λΌμ΄λν΄μΌ ν©λλ€.
- μμ ν΄μ : ViewModelμ΄ λ μ΄μ νμνμ§ μμ λ, μ¦ κ΄λ ¨λ UI μ»΄ν¬λνΈ(μ: μ‘ν°λΉν°λ νλκ·Έλ¨ΌνΈ)κ° νκ΄΄λμμ λ onClearedλ νΈμΆλ©λλ€. μ΄λ μ€ν μ€μΈ μ½λ£¨ν΄μ μ·¨μνμ¬ λΆνμν μμ μ μ€λ¨νκ³ κ΄λ ¨ μμμ ν΄μ νκΈ° μν΄ onClearedλ₯Ό μ€λ²λΌμ΄λν©λλ€.
- λ©λͺ¨λ¦¬ λμ λ°©μ§: μ½λ£¨ν΄μ΄ κ³μ μ€νλλ©΄μ ViewModelμ΄ νκ΄΄λ νμλ μ°Έμ‘°κ° μ μ§λ κ²½μ° λ©λͺ¨λ¦¬ λμκ° λ°μν μ μμ΅λλ€. μ½λ£¨ν΄μ μ μ ν μ·¨μν¨μΌλ‘μ¨ μ΄λ¬ν λμλ₯Ό λ°©μ§ν μ μμ΅λλ€.
⇒ ViewModelμμ μ΄λ¬ν λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν ViewModelScopeλ₯Ό μ¬μ©ν©λλ€.
Structured Concurrency
μ½λ£¨ν΄μ Structured Concurrency, Unstructured Concurrency λ κ°μ§λ‘ μ΄λ£¨μ΄μ Έ μμ΅λλ€.
κ·Έ μ€ Unstructured Concurrencyλ₯Ό μ°λ©΄ μμΈκ° μΌμ΄λλ κ²½μ°κ° μμ΄ Structured Concurrencyλ₯Ό μ¨μΌν©λλ€.μ΄ λ¬Έμ λ CoroutineScopeλ₯Ό μ¬μ©νλ©΄ ν΄κ²°ν μ μμ΅λλ€.
Structured Concurrencyλ₯Ό μ¬μ©νλ©΄ μ½λ£¨ν΄μ΄ μλμΌλ‘ μ·¨μλμ΄ μμΈ μ²λ¦¬κ° λμ± μμ νκ² μ΄λ£¨μ΄μ§λλ€. CoroutineScopeλ₯Ό μ¬μ©νμ¬ μ μ ν CoroutineContextλ₯Ό μ€μ νκ³ , launchλ asyncλ₯Ό μ¬μ©νμ¬ μ½λ£¨ν΄μ μ€ννλ κ²μ΄ μ’μ΅λλ€. Unstructured Concurrencyλ₯Ό μ¬μ©νλ κ²½μ° μμΈ μ²λ¦¬λ₯Ό μν μΆκ°μ μΈ μ½λκ° νμνκΈ° λλ¬Έμ μ£Όμν΄μΌ ν©λλ€.
π©π» μ½λ£¨ν΄ μμ½!
μ§μ νλ νλ νΌκ·Έλ§λ‘ μμ½ν λ΄μ©μ λλ€! π«
π©π» λ§μΉλ©°
μ΄λ² κΈμ μ΄μΌκΈ°κ° λ°©λν΄μ λ°©μ λμμ΅λλ€...πͺ«
μμ μ§μμ κΉμ΄μ§μλ‘ μ΄λ €μ΄ κ±° κ°μ΅λλ€...!
μ€λ ν루 λͺ¨λ ν볡ν μκ° λ³΄λ΄μ ¨μΌλ©΄ μ’κ² μ΅λλ€ π
λ€μ κΈλ κΈ°λ λΆνλλ¦¬κ² μ΅λλ€:)π€
κ°μ¬ν©λλ€!!
β¬οΈ μ§κΈ κ΅¬κΈ νλ μ΄ μ€ν μ΄μμ ν°λ λ€μ΄ λ°κΈ°
https://play.google.com/store/apps/details?id=com.team7.tikkle&hl=ko-KR
π© Contact : wjdcogus6@gmail.com
π² SNS : https://www.instagram.com/chaeehyuny/