๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Kotlin

[Kotlin] Coroutines - suspend ํ‚ค์›Œ๋“œ

interfacer_han 2024. 2. 14. 10:10

๋ณธ ๊ฒŒ์‹œ๊ธ€์˜ Coroutine ๊ฐœ๋…์€ Android ๋‚ด์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์„ ์ „์ œ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋‹ค.
 

#1 ์ด์ „ ๊ธ€

 

[Kotlin] Coroutines - ๋™๊ธฐ ์ฝ”๋“œ, ๋น„๋™๊ธฐ ์ฝ”๋“œ

#1 ๋™๊ธฐ ์ฝ”๋“œ vs ๋น„๋™๊ธฐ ์ฝ”๋“œ #1-1 ๊ตฌ๋ถ„ํ•˜๊ธฐ ์ฝ”๋ฃจํ‹ด์„ ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„  ๋จผ์ €, '๋™๊ธฐ ์ฝ”๋“œ'์™€ '๋น„๋™๊ธฐ ์ฝ”๋“œ(= ์ฝ”๋ฃจํ‹ด ์ฝ”๋“œ)'๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ตฌ๋ถ„ํ•  ์ค„ ์•Œ์•„์•ผ ํ•œ๋‹ค. ๋‘˜์„ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ธฐ์ค€์€ ์‰ฝ๊ฒŒ ๋ง

kenel.tistory.com

ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์„ ์ฝ์–ด์•ผ ๋ณธ ๊ฒŒ์‹œ๊ธ€์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.
 

#2 suspend ํ‚ค์›Œ๋“œ

#2-1 suspend์™€ resume

๋น„๋™๊ธฐ ์ฝ”๋“œ, ์ฆ‰ ์ฝ”๋ฃจํ‹ด์€ suspend(๊ธฐ๋‹ค๋ฆผ)์ด ํ•„์š”ํ•œ ์ฝ”๋“œ๋‹ค. ๊ธฐ๋‹ค๋ฆผ์ด ์žˆ๋‹ค๋Š” ๊ฑด resume(๋‹ค์‹œ ์›€์ง์ž„, ์žฌ๊ฐœ)๋„ ์žˆ๋‹ค๋Š” ๋ง์ด๋‹ค. ์ฝ”๋ฃจํ‹ด์„ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ(suspend) ๋งŒ๋“ค๋ฉด, ํ•ด๋‹น ์ฝ”๋ฃจํ‹ด์˜ ์Šคํƒ ํ”„๋ ˆ์ž„์ด ๋ณต์‚ฌ๋˜์–ด ๋ณ„๋„๋กœ ์ €์žฅ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ์ฝ”๋ฃจํ‹ด์„ ์žฌ๊ฐœ(resume)์‹œํ‚ค๋ฉด, ์Šคํƒ ํ”„๋ ˆ์ž„์„ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ๊ทธ ์Šคํƒ ํ”„๋ ˆ์ž„์ด ์ €์žฅ๋˜์—ˆ๋˜ ๊ณณ์—์„œ ์ฝ”๋ฃจํ‹ด์ด ๋‹ค์‹œ ์‹œ์ž‘๋œ๋‹ค. 
 

#2-2 ๊ธฐ๋ณธ Suspending Function

์Šค๋ ˆ๋“œ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜ ์ฆ‰ 'Suspending Function'์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

coroutineScope()
supervisorScope()
delay()
join()
await()
receive()
withContext()
withTimeout()
withTimeoutOrNull()

์ด ํ•จ์ˆ˜๋“ค์€ ์ฝ”๋ฃจํ‹ด ๊ธฐ๋ณธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(kotlinx.coroutines)์˜ Suspending Function๋“ค์ด๋‹ค. ์ด ๊ธฐ๋ณธ ํ•จ์ˆ˜๋“ค์€ #2-3์™€ ๋‹ฌ๋ฆฌ ์•”์‹œ์ ์œผ๋กœ suspend ํ‚ค์›Œ๋“œ๊ฐ€ ์ ์šฉ๋˜์–ด์ ธ ์žˆ๋‹ค. ์—ฌ๋‹ด์œผ๋กœ, Room์ด๋‚˜ Retrofit๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„  ๊ธฐ๋ณธ Suspending Fuction๋“ค์„ ์ถ”๊ฐ€๋กœ ์ œ๊ณตํ•ด์ค€๋‹ค๊ณ  ํ•œ๋‹ค.

์—ฌ๋‹ด์œผ๋กœ, runBlocking์€ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ(Suspend) ๋งŒ๋“ค์ง€ ์•Š๊ณ , ๋ฉˆ์ถ”๊ฒŒ(Block) ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋ฏ€๋กœ, ์œ„ ๋ช…๋‹จ์— ์—†๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ ์ „์—, runBlocking์€ ์• ์ดˆ์— ๋™๊ธฐ ์ฝ”๋“œ๋ผ์„œ ๋”๋”์šฑ ๋ช…๋‹จ์— ์˜ค๋ฅผ ์ˆ˜ ์—†๋‹ค.
 

#2-3 ์‚ฌ์šฉ์ž ์ •์˜ Suspending Function

import kotlinx.coroutines.*

suspend fun suspendingFunction() {
    println("suspendingFunction() called")
    delay(1000) // 1์ดˆ ๋Œ€๊ธฐ
    
}

fun main() {
    println("Start")
    
    runBlocking {
        suspendFunction()
    }
    
    println("End")
}

/* ์ถœ๋ ฅ ๊ฒฐ๊ณผ
Start
suspendingFunction() called
End
*/

์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜๋ฅผ Suspending Function์œผ๋กœ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, #2-2์™€ ๋‹ฌ๋ฆฌ ๋ช…์‹œ์ ์œผ๋กœ suspend ํ‚ค์›Œ๋“œ๋ฅผ ์ ์–ด์•ผ ํ•œ๋‹ค. ํ•จ์ˆ˜ ํ‚ค์›Œ๋“œ์ธ fun ์•ž์— suspend ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์ฃผ๋ฉด ๋œ๋‹ค.
 

#2-4 Suspending Function์˜ ์ œ์•ฝ

#2-2 ๋˜๋Š” #2-3์˜ Suspending Function์€ ๋น„๋™๊ธฐ ์ฝ”๋“œ์˜ ์˜์—ญ(์ด ๋งํฌ์˜ #2 ์ฐธ์กฐ)์ด ๋˜๋ฉฐ, ๊ทธ ์˜์—ญ ์•ˆ์—์„œ Suspend Function์„ ๋น„๋กฏํ•œ ์ฝ”๋ฃจํ‹ด API ์ฝ”๋“œ๋ฅผ ๋งˆ์Œ๊ป ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์ œ์•ฝ๋„ ์ƒ๊ธด๋‹ค. ์•ž์œผ๋กœ ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ๋™๊ธฐ ์ฝ”๋“œ์˜ ์˜์—ญ(์ด ๋งํฌ์˜ #2 ์ฐธ์กฐ)์—์„œ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค. ๋™๊ธฐ ์ฝ”๋“œ์™€ ๋น„๋™๊ธฐ ์ฝ”๋“œ์˜ ์˜์—ญ์„ ๊ตฌ๋ถ„ํ•˜๋Š” ํฌ์žฅ์ง€ ์—ญํ• ์ธ CoroutineScope ๋“ฑ์„ ์ œ์™ธํ•˜๋ฉด, Suspend ํ•จ์ˆ˜๋Š” ์˜ค์ง Suspend ํ•จ์ˆ˜ ๋‚ด์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

#3 suspend ํ‚ค์›Œ๋“œ์˜ ์ž‘๋™ ๊ธฐ์ œ

#3-1 ์ƒ˜ํ”Œ ์ฝ”ํ‹€๋ฆฐ ํด๋ž˜์Šค

// package com.example.coroutinesbasics

import kotlinx.coroutines.delay

class SuspendPractice {
    fun normalFunction() {
        println("This is normalFunction")
    }

    suspend fun suspendingFunction() {
        println("This is suspendingFunction")
        delay(1000)
    }
}

์ด ์ฝ”ํ‹€๋ฆฐ ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•ด suspend ํ‚ค์›Œ๋“œ๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋‚ด๋ถ€์ ์ธ ๊ธฐ์ œ๋ฅผ ์‚ดํŽด๋ณธ๋‹ค.
 

#3-2 Bytecode ์ƒ์„ฑ

suspend ํ‚ค์›Œ๋“œ์˜ ์ž‘๋™ ๊ธฐ์ œ๋ฅผ ์‚ดํŽด๋ณด๊ธฐ ์œ„ํ•ด์„œ, ๋จผ์ € #3-1์˜ ์ƒ˜ํ”Œ ์ฝ”ํ‹€๋ฆฐ ํด๋ž˜์Šค๊ฐ€ Bytecode๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ๋นŒ๋“œ์‹œํ‚จ๋‹ค. [Build] - [Rebuild Project]๋ฅผ ํด๋ฆญํ•œ๋‹ค.
 

[Tools] - [Kotlin] - [Show Kotlin Bytecode]๋กœ ์ƒ์„ฑํ–ˆ๋˜ Kotlin Bytecode๋ฅผ ๋ณธ๋‹ค
 

#3-3 Decompile

#3-2๋ฅผ ํ†ตํ•ด ์—ฐ Kotlin Bytecode๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. Decompile ๋ฒ„ํŠผ์„ ํด๋ฆญํ•œ๋‹ค.
 

// package com.example.coroutinesbasics;

import kotlin.Metadata;
import kotlin.Unit;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlinx.coroutines.DelayKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Metadata(
   mv = {1, 9, 0},
   k = 1,
   d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\b\u0003\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004J\u0011\u0010\u0005\u001a\u00020\u0004H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0006\u0082\u0002\u0004\n\u0002\b\u0019¨\u0006\u0007"},
   d2 = {"Lcom/example/coroutinesbasics/SuspendPractice;", "", "()V", "normalFunction", "", "suspendingFunction", "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", "app_debug"}
)
public final class SuspendPractice {
   public final void normalFunction() {
      String var1 = "This is normalFunction";
      System.out.println(var1);
   }

   @Nullable
   public final Object suspendingFunction(@NotNull Continuation $completion) {
      String var2 = "This is suspendingFunction";
      System.out.println(var2);
      Object var10000 = DelayKt.delay(1000L, $completion);
      return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : Unit.INSTANCE;
   }
}

Decompile ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์œ„์™€ ๊ฐ™์€ SuspendPractice.decompiled.java ํŒŒ์ผ์ด ๋งŒ๋“ค์–ด์ง„๋‹ค. ์ด ํŒŒ์ผ์€ ์šฐ๋ฆฌ๊ฐ€ SuspendPractice.kt๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑํ–ˆ๋˜ ์ฝ”ํ‹€๋ฆฐ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ Java ์–ธ์–ด๋กœ ํ•ด์„(Interpretation)ํ•œ ๊ฒƒ์ด๋‹ค. suspend ํ•จ์ˆ˜์— Continuation ํƒ€์ž…์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋“ค์–ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ์ด ๋ณด์ธ๋‹ค. Continuation ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์ฝ”๋ฃจํ‹ด ์ฝ”๋“œ๊ฐ€ suspend(์ผ์‹œ ์ •์ง€)๋˜์—ˆ๋‹ค๊ฐ€ ๋‚˜์ค‘์— ๋‹ค์‹œ resume(์žฌ๊ฐœ)๋  ๋•Œ์˜ ๋™์ž‘์„ ๊ด€๋ฆฌํ•œ๋‹ค. ๋‹จ์–ด์˜ ๋œป์ฒ˜๋Ÿผ ๋ง ๊ทธ๋Œ€๋กœ ์—ฐ์†(Continuation)์˜ ๊ด€๋ฆฌ๋‹ค.

#4 ์š”์•ฝ

suspend ํ‚ค์›Œ๋“œ๋Š” ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ๋น„๋™๊ธฐ ์ฝ”๋“œ์˜ ์˜์—ญ์ž„์„ ๋ช…์‹œ์ ์œผ๋กœ ํ‘œ์‹œํ•œ๋‹ค. ํ”„๋กœ๊ทธ๋ž˜๋จธ์—๊ฒŒ ๊ทธ๋ฆฌ๊ณ  ์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒŒ.