๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป ๊ฐœ๋ฐœ์ž ์ด์•ผ๊ธฐ/์•ˆ๋“œ๋กœ์ด๋“œ ์‚ฝ์งˆ๊ธฐ

Kotlin์˜ Coroutine, ์ฝ”๋ฃจํ‹ด #1

by ์ •์„ ํ•œ 2023. 1. 31.
728x90
๋ฐ˜์‘ํ˜•

Co-Routine, Co(์ ‘/ํ•จ๊ป˜, ๋™์‹œ์—) ์ฝ”ํ‹€๋ฆฐ์˜ ์ฝ”๋ฃจํ‹ด์€ ๋ฌด์—‡์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  ์™œ ์‚ฌ์šฉ๋˜๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐ€์ด๋“œ ๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•˜๋Š” ๊ฒฝ์šฐ, Coroutine์„ ์‚ฌ์šฉํ•˜๋„๋ก ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณต์‹๋ฌธ์„œ์—์„œ๋Š” suspendable computations๋ผ๋Š” ํ‘œํ˜„์„ ํ†ตํ•˜์—ฌ Coroutine์— ๋Œ€ํ•œ ์„ค๋ช…์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ค‘๋‹จ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ณ„์‚ฐ ์ •๋„๋กœ ํ•ด์„ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์€๋ฐ, ์ด ํ‘œํ˜„ ์ž์ฒด๊ฐ€ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๊ฐ€์žฅ ์ ์ ˆํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ญ๋‹ˆ๋‹ค.

์ƒ๊ธฐ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ์ฝ”๋ฃจํ‹ด์€ ํ”„๋กœ์„ธ์Šค์˜ ์‹œ์ž‘ ์ดํ›„ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ค‘๋‹จ๋˜์—ˆ๋‹ค๊ฐ€ ๊ทธ ์ดํ›„ ์ค‘๋‹จ๋œ ๊ทธ ์‹œ์ ์— ๋‹ค์‹œ ์žฌ๊ฐœ๋  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ”„๋กœ์„ธ์Šค์˜ ์ค‘๊ฐ„์— ๋‚˜๊ฐ€๊ณ  ๋‹ค์‹œ ๋“ค์–ด์˜ฌ ๋•Œ, return๋ฌธ์ด ์—†์–ด๋„ ์–ธ์ œ๋“ ์ง€ ๋‚˜๊ฐ€๊ณ  ๋‹ค์‹œ ํ•ด๋‹น ์ง€์ ์œผ๋กœ ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด๋Ÿฐ ์ฝ”๋ฃจํ‹ด์€ Thread์—์„œ ์‹คํ–‰๋˜๋Š”๋ฐ, ํ•˜๋‚˜์˜ Thread ๋‚ด์—์„œ ์—ฌ๋Ÿฌ Coroutine๋“ค์ด ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๊ณ  Main Thread ์ด์™ธ์— Sub Thread๊ฐ€ ์กด์žฌํ•˜์—ฌ ์ž‘์—…์ด Non-blocking ํ•œ ํ˜•ํƒœ๋กœ ์—ฌ๋Ÿฌ Coroutine์ด ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฐธ๊ณ ์ž๋ฃŒ : ํƒ€ ๋ธ”๋กœ๊ทธ์— ์ด Thread ์ž‘์—…์— ๋Œ€ํ•œ ๋‚ด์šฉ์ด ์ž˜ ์ •๋ฆฌ ๋˜์–ด ์žˆ์–ด ์ฒจ๋ถ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค. / ์ดํ•ด๊ฐ€ ์‰ฝ๊ฒŒ ๋˜๋Š” ๊ธ€์ž…๋‹ˆ๋‹ค.

์•„์ฃผ ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋“ค์„ ํ™œ์šฉํ•ด์„œ Kotlin์—์„œ์˜ Coroutine์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ฃผ๋œ ๋‚ด์šฉ์€ Kotlin Document์—์„œ ์ธ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

fun main() = runBlocking {
	// this : CoroutineScope
    launch {
        delay(1000L)
    }
}

runBlocking : ํ˜„์žฌ Thread์—์„œ Coroutine์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ Coroutine Builder์ž…๋‹ˆ๋‹ค.
์ƒˆ Coroutine์„ ์‹คํ–‰ํ•˜๊ณ  ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ํ˜„์žฌ Main Thread๋ฅผ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” Coroutine์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ๋ฅผ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

expect fun <T> runBlocking(
    context: CoroutineContext = EmptyCoroutineContext, 
    block: suspend CoroutineScope.() -> T
): T

launch : ์ž‘๋™ ์ค‘์ธ ๋‚˜๋จธ์ง€ ์ฝ”๋“œ์™€ ๋™์‹œ์— ์ƒˆ๋กœ์šด Coroutine์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
ํ˜„์žฌ Thread๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ์ƒˆ Coroutine์„ ์‹œ์ž‘ํ•˜๋ฉฐ Coroutine์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ Job์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext, 
    start: CoroutineStart = CoroutineStart.DEFAULT, 
    block: suspend CoroutineScope.() -> Unit
): Job

delay : suspend function์œผ๋กœ Thread๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ์ฃผ์–ด์ง„ ์‹œ๊ฐ„ ๋™์•ˆ Coroutine์„ ์ง€์—ฐํ•˜๊ณ  ์ง€์ •ํ•œ ์‹œ๊ฐ„ ํ›„์— ๋‹ค์‹œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

suspend fun delay ( timeMillis : Long )

fun main() = runBlocking {
    doWorld()
}

suspend fun doWorld() = coroutineScope {
    launch {
        delay(1000L)
    }
}

coroutineScope : coroutineScope๋ฅผ ๋งŒ๋“ค๊ณ  ํ•ด๋‹น ๋ฒ”์œ„์— ์ง€์ •๋œ suspend block์„ ํ˜ธ์ถœํ•œ๋‹ค. ์ œ๊ณต๋œ ๋ฒ”์œ„๋Š” ์™ธ๋ถ€๋ฒ”์œ„์—์„œ coroutineContext๋ฅผ ์ƒ์†ํ•˜์ง€๋งŒ context์˜ Job์„ ์žฌ์ •์˜ ํ•œ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ํ•˜์œ„ coroutine์ด ์‹คํŒจํ•˜๋ฉด ํ•ด๋‹น ๋ฒ”์œ„ ๋˜ํ•œ ์‹คํŒจํ•˜์—ฌ ๋‚˜๋จธ์ง€ ํ•˜์œ„ ํ•ญ๋ชฉ์ด ๋ชจ๋‘ ์ทจ์†Œ๋œ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ง€์ •๋œ suspend block๊ณผ ํ•ด๋‹น block์˜ ๋ชจ๋“  ํ•˜์œ„ coroutine์ด ์™„๋ฃŒ๋˜๋Š” ์ฆ‰์‹œ ๋ฐ˜ํ™˜๋œ๋‹ค.

suspend fun <R> coroutineScope(
    block: suspend CoroutineScope.() -> R
): R

supervisorScope : Supervisor Job์œผ๋กœ coroutineScope๋ฅผ ๋งŒ๋“ค๊ณ  ์ด ๋ฒ”์œ„์—์„œ ์ง€์ •๋œ suspend block์„ ํ˜ธ์ถœํ•œ๋‹ค. ์ œ๊ณต๋œ ๋ฒ”์œ„๋Š” ์™ธ๋ถ€๋ฒ”์œ„์—์„œ coroutineContext๋ฅผ ์ƒ์†ํ•˜์ง€๋งŒ context์˜ Supervisor Job์„ ์žฌ์ •์˜ ํ•œ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ง€์ •๋œ block๊ณผ ๋ชจ๋“  ํ•˜์œ„ coroutine์ด ์™„๋ฃŒ๋˜๋Š” ์ฆ‰์‹œ ๋ฐ˜ํ™˜๋œ๋‹ค.
coroutineScope์™€์˜ ์ฐจ์ด์ 
ํ•˜์œ„ ํ•ญ๋ชฉ์˜ fail์— ๋Œ€ํ•˜์—ฌ ํ•ด๋‹น ๋ฒ”์œ„์˜ fail๋กœ ๊ฐ„์ฃผํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋‹ค๋ฅธ ํ•˜์œ„ ํ•ญ๋ชฉ์— ๋Œ€ํ•œ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ•˜์œ„ ํ•ญ๋ชฉ์˜ fail์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ์ •์ฑ…์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

suspend fun <R> supervisorScope(
    block: suspend CoroutineScope.() -> R
): R

val job = launch {
    delay(1000L)
}

job.join()

job : ๊ฐœ๋…์ ์œผ๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์„ ์˜๋ฏธํ•˜๋ฉฐ job์€ completion๊ฐ€ ๋๋‚˜๋Š” life-cycle์—์„œ ์ทจ์†Œ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. job์€ parent-child์˜ ๊ณ„์ธต์œผ๋กœ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ parent ๊ณ„์ธต์ด ์ทจ์†Œ๋˜๋ฉด child ๊ณ„์ธต๋„ ์ทจ์†Œ๋œ๋‹ค. failure๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” CancellationException์˜ ๊ฒฝ์šฐ์—๋Š” ์ฆ‰์‹œ parent ๊ณ„์ธต์ด ์ทจ์†Œ๋˜๋ฉฐ ๋‹ค๋ฅธ child ๊ณ„์ธต์˜ ํ•ญ๋ชฉ๋“ค๋„ ์ทจ์†Œ๋œ๋‹ค. ํ•ด๋‹น ๋™์ž‘์— ๋Œ€ํ•˜์—ฌ Supervisor Job์„ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ง€์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

interface Job : CoroutineContext.Element

Coroutine Job์€ coroutine builder๋ฅผ ํ†ตํ•ด ๋งŒ๋“ค์–ด์ง€๋ฉฐ ์ง€์ •๋œ ์ฝ”๋“œ ๋ธ”๋ก์„ ์‹คํ–‰์‹œํ‚ค๊ณ  ํ•ด๋‹น ๋ธ”๋ก์ด ์™„๋ฃŒ๋˜๋ฉด Job๋„ ์™„๋ฃŒ๋œ๋‹ค.
Completable Job์€ Job() ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๋กœ ์ƒ์„ฑ๋œ๋‹ค. CompletableJob.complete๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์™„๋ฃŒ๋œ๋‹ค.
Job์˜ ์‹คํ–‰์€ ๊ฒฐ๊ณผ ๊ฐ’์„ ์ƒ์„ฑํ•˜์ง€ ์•Š์œผ๋ฉฐ, ์˜ค๋กœ์ง€ side-effects๋งŒ์„ ์œ„ํ•ด ์‹œ์ž‘ํ•œ๋‹ค. Job์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ ์‹คํ–‰์€ ๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ ์ฐธ์กฐ.

wait children
    +-----+ start  +--------+ complete   +-------------+  finish  +-----------+
    | New | -----> | Active | ---------> | Completing  | -------> | Completed |
    +-----+        +--------+            +-------------+          +-----------+
                     |  cancel / fail       |
                     |     +----------------+
                     |     |
                     V     V
                 +------------+                           finish  +-----------+
                 | Cancelling | --------------------------------> | Cancelled |
                 +------------+                                   +-----------+
728x90
๋ฐ˜์‘ํ˜•