Coroutine - CoroutineContext 구조, 접근, 제거


이 포스트에서는 Job, CoroutineDispatcher, CoroutineName 이 CoroutineContext 의 구성 요소라는 것을 이해하고, CoroutineContext 의 구성 요소를 결합하거나 분리하는 방법에 대해 알아본다.

대표적인 코루틴 빌더 함수인 launch() 와 async() 의 시그니처를 보자.

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

public fun <T> CoroutineScope.async(
  context: CoroutineContext = EmptyCoroutineContext,
  start: CoroutineStart = CoroutineStart.DEFAULT,
  block: suspend CoroutineScope.() -> T
): Deferred<T>

두 함수 모두 context, start, block 매개변수를 갖는다.

  • context
    • CoroutineContext
  • start
    • CoroutineStart
  • block
    • launch(): Unit 을 반환하는 람다식
    • async(): 제네릭 타입 T 를 반환하는 람다식

2.2. CoroutineName 을 사용하여 코루틴에 이름 추가 에서 context 자리에 CoroutineName 을 사용하였고, 4.1. launch() 의 파라미터로 CoroutineDispatcher 사용 에서는 context 자리에 CoroutineDispatcher 객체가 사용되었다.

CoroutineName 과 CoroutineDispatcher 는 각각 다른 목적을 가지지만 공통적으로 CoroutineContext 의 구성 요소라는 점에서 launch() 나 withContext() 등의 코루틴 빌더에서 context 파라미터로 함께 사용될 수 있다.

launch(CoroutineName("MyCoroutine"))
launch(Dispatchers.IO)
launch(Dispatchers.Default + CoroutineName("IOCoroutine")) // 이름과 디스패처를 함께 지정하여 코루틴의 실행환경과 디버깅 편의성을 동시에 챙김

CoroutineName 은 코루틴에 식별용 이름을 부여할 수 있고, CoroutineDispatcher 는 코루틴이 실행될 스레드/스레드풀을 결정하며 이 둘 모두 CoroutineContext 를 구성하는 요소이므로 launch(), async(), withContext() 등에서 context 인자로 직접 전달할 수 있다.

CoroutineContext 는 코루틴을 실행하는 실행 환경을 설정하고 관리하는 인터페이스로 CoroutineDispatcher, CoroutineName, Job 등의 객체를 조합하여 코루틴이 어떤 스레드에서 실행될지, 어떤 이름으로 구분될지, 어떤 생명 주기를 가질지를 설정할 수 있다.
즉, 코루틴의 실행과 관련된 모든 설정은 CoroutineContext 를 통해 이루어지며, 이는 코루틴의 생성, 실행, 취소 등 전반적인 제어에 관여한다.

이 포스트에서는 Coroutine 객체를 사용하여 코루틴의 실행 환경을 설정하고 관리하는 방법에 대해 알아본다.

  • CoroutineContext 구성 요소
  • CoroutineContext 구성 방법
  • CoroutineContext 구성 요소에 접근
  • CoroutineContext 구성 요소 제거

소스는 github 에 있습니다.




개발 환경

  • mac os
  • openjdk 17.0.14

build.gradle.kts

plugins {
  kotlin("jvm") version "2.1.10"
  application
}

group = "com.assu.study"
version = "0.0.1-SNAPSHOT"

repositories {
  mavenCentral()
}

dependencies {
//    implementation("org.jetbrains.kotlin:kotlin-stdlib:2.1.10")
  // 코루틴 라이브러리
  implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
  // JUnit5 테스트 API(테스트 코드 작성용)
  testImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
  // JUnit5 테스트 엔진(테스트 실행 시 필요)
  testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.12.0")
  // 코루틴 테스트 라이브러리
  testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.1")
}

// JUnit5 를 사용하기 위한 옵션
tasks.test {
  useJUnitPlatform()
}

kotlin {
  jvmToolchain(17)
}

application {
  mainClass.set("MainKt")
}

gradle.properties

kotlin.code.style=official

1. CoroutineContext 구성 요소

CoroutineContext 는 여러 요소들의 집합이며, 그중 코루틴 실행 시 가장 자주 사용되는 핵심 구성 요소는 다음의 4가지이다.

  • CoroutineName
    • 코루틴에 이름을 부여하여 디버깅이나 로그 분석 시 식별을 용이하게 함
  • CoroutineDispatcher
    • 코루틴이 어느 스레드 혹은 어느 스레드풀에서 실행될지 결정함
    • 대표적으로 Dispatchers.Default, Dispatchers.IO, Dispatchers.Main 등이 있음
  • Job
    • 코루틴의 생명 주기를 관리하는 핵심 요소
    • 취소나 완료 상태 추적, 자식 코루틴의 계층 구조 관리 등에 사용됨
  • CoroutineExceptionHandler
    • 코루틴 내부에서 발생한 예외를 처리하는 핸들러

이제 CoroutineContext 객체가 위의 구성 요소들을 어떻게 관리하고 사용하는지에 대해 알아본다.


2. CoroutineContext 구성

CoroutineContext 객체는 키-값의 구조로 구성 요소들을 관리한다.
각 구성 요소는 고유한 키를 가지며, 같은 키에 대해 중복된 값은 허용하지 않는다.
따라서 하나의 CoroutineContext 객체는 하나의 CoroutineName, 하나의 CoroutineDispatcher, 하나의 Job, 하나의 CoroutineExceptionHandler 만 존재할 수 있다.


2.1. CoroutineContext 구성

CoroutineContext 객체는 키-쌍으로 구성 요소를 관리하지만 키에 값을 직접 대입하는 방식이 아니라 + 연산자를 통해 여러 CoroutineContext 요소를 조합하는 방식으로 사용된다.

아래는 CoroutineDispatcher 객체인 newSingleThreadContext(“myThread”) 와 CoroutineName 객체인 CoroutineName(“myCoroutine”) 으로 구성된 CoroutineContext 객체를 만드는 예시이다.

val coroutineContext: CoroutineContext = newSingleThreadContext("myThread") + CoroutineName("myCoroutine")

이렇게 만들어진 CoroutineContext 객체는 아래와 같은 형태가 된다.

키값
CoroutineName 키CoroutineName(“myCoroutine”)
CoroutineDispatcher 키newSingleThreadContext(“myThread”)
Job 키설정되지 않음
CoroutineExceptionHandler 키설정되지 않음

만들어진 CoroutineContext 객체는 코루틴 빌더 함수의 context 인자로 넘겨 코루틴을 실행하는데 사용할 수 있다.

package chap06

import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.launch
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext

fun main() = runBlocking<Unit> {
    val myCoroutineContext: CoroutineContext = newSingleThreadContext("myThread") + CoroutineName("myCoroutine")

    launch(context = myCoroutineContext) {
        println("[${Thread.currentThread().name}] 실행")
    }
}
[myThread @myCoroutine#2] 실행

구성 요소가 없는 CoroutineContext 는 EmptyCoroutineContext 를 통해 만들 수 있다.

import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext

val emptyCoroutineContext: CoroutineContext = EmptyCoroutineContext

2.2. CoroutineContext 구성 요소 덮어씌우기

만일 같은 키를 가진 요소를 추가하면 기존값이 새로운 값으로 덮어쓰기 된다.

val context = CoroutineName("First") + CoroutineName("Second")

위 코드에서 context 에는 First 가 무시되고, Second 라는 이름만 남게 된다.

이러한 구조 덕분에 CoroutineContext 는 불변성과 타입 안전성을 유지하면서도 유연하게 여러 요소를 조합할 수 있다.

아래 코드를 보자.

package chap06

import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.launch
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext

fun main() = runBlocking<Unit> {
    val myCoroutineContext: CoroutineContext = newSingleThreadContext("myThread") + CoroutineName("myCoroutine")
    val newMyCoroutineContext: CoroutineContext = myCoroutineContext + CoroutineName("newMyCoroutine")

    launch(context = newMyCoroutineContext) {
        println("[${Thread.currentThread().name}] 실행")
    }
}
[myThread @newMyCoroutine#2] 실행

2.3. CoroutineContext 에 Job 생성하여 추가

Job 객체를 직접 생성해 추가하면 코루틴의 구조화가 깨지기 때문에(= 부모-자식 관계가 끊어짐) 새로운 Job 객체를 생성하여 CoroutineContext 객체에 추가하는 것은 주의가 필요함

Job 객체는 기본적으로 launch() 나 runBlocking() 과 같은 코루틴 빌더 함수를 통해 자동으로 생성되지만, Job() 을 호출해 생성할 수도 있다.

package chap06

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext

fun main() = runBlocking<Unit> {
    val myJob = Job()
    val coroutineContext: CoroutineContext = Dispatchers.IO + myJob

    launch(context = coroutineContext) {
        println("[${Thread.currentThread().name}] 실행")
    }
}

위 코드는 아무것도 출력되지 않는다.
이유는 runBlocking() 은 내부의 모든 자식 코루틴이 자신의 자식일 때만 끝날 때까지 기다려준다. 하지만 위의 Job() 을 통해 만들어진 myJob 은 runBlocking() 의 자식이 아니라 독립적인 Job 이다.
즉, launch(context = Dispatchers.IO + Job()) 은 runBlocking 과는 무관한 별도의 코루틴에서 실행되며, runBlocking 은 해당 Job 의 완료를 기다려주지 않기 때문에 launch() 가 실행되기 전에 main() 이 종료되어 버린다.


3. CoroutineContext 구성 요소 접근

CoroutineContext 객체의 구성 요소에 접근하기 위해서는 각 구성 요소가 가진 고유한 키가 필요하다.
여기서는 각 구성 요소의 키를 얻는 방법에 대해 알아본다.


3.1. CoroutineContext 구성 요소의 키

CoroutineContext 는 여러 구성 요소들의 모음이며, 각 구성 요소는 고유한 키(CoroutineContext.Key<T>) 를 통해 관리된다.
이 Key 는 중복을 허용하지 않으며, 특정 구성 요소를 CoroutineContext 에서 가져올 때 사용하는 식별자 역할을 한다.

CoroutineName, Job, CoroutineDispatcher, CoroutineExceptionHandler 등의 코루틴 구성 요소들은 자신의 내부에 Key 를 동반 객체(companion object)(= 싱글톤 객체로 구현) 로 선언하여 자신을 CoroutineContext 에 등록하거나 검색할 수 있도록 한다.

아래는 CoroutineName 클래스 구현체이다.

public data class CoroutineName(
    val name: String
) : AbstractCoroutineContextElement(CoroutineName) {
    public companion object Key : CoroutineContext.Key<CoroutineName>

    // ...
}

위 구조의 핵심 포인트는 아래와 같다.

  • CoroutineName 클래스는 AbstractCoroutineContextElement 를 상속함
  • 생성자에서 CoroutineName 키를 부모 클래스에 전달
  • 동반 객체 Key 는 CoroutineContext.Key<CoroutineName> 를 구현한 싱글톤

이 덕분에 CoroutineContext[CoroutineName] 의 형태로 손쉽게 CoroutineName 값을 가져올 수 있다.

생성자에서 CoroutineName 키를 부모 클래스에 전달 (Click!)

생성자에서 CoroutineName 키를 부모 클래스에 전달 이라는 부분을 보자.

위 코드의 AbstractCoroutineContextElement(CoroutineName) 에서 CoroutineName 은 동반 객체 companion object Key 의 이름이 생략된 표현이다.

public companion object Key : CoroutineContext.Key<CoroutineName>

이렇게 동반 객체에 이름을 Key 라고 명시했어도, 클래스 내부에서는 CoroutineName 이라는 클래스 이름으로도 접근할 수 있다.

쉽게 이해하기 위해 아래 예시를 보자.

class MyClass {
  companion object MyCompanion {
    val x = 10
  }
}

fun main() {
  println(MyClass.MyCompanion.x)  // 명시적 접근
  println(MyClass.x)              // 클래스 이름으로도 접근 가능 (동반 객체 이름 생략 가능)
}

즉, 동반 객체의 이름이 Key 여도 클래스 이름으로 접근할 수 있다.

따라서 AbstractCoroutineContextElement(CoroutineName) 에서 괄호 안의 CoroutineName 은 타입이 아니라 동반 객체 CoroutineName.Key 를 의미한다.
이는 코틀린 문법상 클래스 내부에서는 동반 객체를 클래스 이름으로도 자동 참조할 수 있기 때문에 가능한 표현이다.

좀 더 상세한 내용은 3.2.2. 구성 요소 자체를 키로 사용하여 구성 요소에 접근: CoroutineName 을 참고하세요.


다른 구성 요소들도 동일한 패턴을 따른다.

CoroutineContext 구성 요소내부에 선언된 키
CoroutineNameCoroutineName.Key
JobJob.Key
CoroutineDispatcherCoroutineDispatcher.Key
CoroutineExceptionHandlerCoroutineExceptionHandler.Key

예시로 Job 인터페이스의 선언도 아래와 같다.

public interface Job : CoroutineContext.Element {
    public companion object Key : CoroutineContext.Key<Job>
    
    // ...
}

CoroutineDispatcher.Key 는 코틀린 버전 2.1.10 기준으로 아직 실험 중인 API 이며, 사용할 경우 아래처럼 명시적으로 opt-in 해야 한다.

@OptIn(ExperimentalStdlibApi::class)
val dispatcherKey = CoroutineDispatcher.Key

Opt-in

명시적으로 동의하거나 참여 의사를 표시해야만 어떤 서비스나 기능이 활성화되는 방식
즉, 기본적으로는 꺼져 있거나 제외된 상태이고, 사용자가 자발적으로 ‘예’ 라고 선택해야만 참여되는 방식

실험적 API 는 향후 변경될 가능성이 있으므로, 안정적인 코드에서는 사용을 피하는 것이 좋다.
따라서 실험적인 API 를 피하려면 직접 dispatcher 객체의 key 프로퍼티를 사용하면 된다.

package chap06

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext

fun main() = runBlocking<Unit> {
  val dispatcherKey1: CoroutineContext.Key<*> = Dispatchers.IO.key
  val dispatcherKey2: CoroutineContext.Key<*> = Dispatchers.Default.key

  println("${dispatcherKey1 === dispatcherKey2}") // true
}

Dispatchers.IO.key 와 Dispatchers.Default.key 는 서로 다른 디스패처이지만, 그들이 가진 .key 는 모두 동일한 CoroutineDispatcher.Key 객체를 가리킨다.
이유는 간단하다. CoroutineDispatcher 라는 타입의 Context 요소는 모두 하나의 고유한 키로 식별되기 때문이다.

CoroutineContext 입장에서 Dispatcher.IO, Dispatcher.Default, Dispatchers.Unconfined 등은 모두 같은 타입(CoroutineDispatcher) 를 공유하므로, 그 키도 동일한 객체를 참조한다.

즉, 서로 다른 디스패처라도 같은 CoroutineDispatcher.Key 를 사용하므로 .key 는 항상 동일한 객체를 반환한다.


3.2. 키를 사용하여 CoroutineContext 구성 요소에 접근

3.2.1. 싱글톤 키를 사용하여 CoroutineContext 구성 요소에 접근: CoroutineName.Key

CoroutineName.Key 를 사용하여 CoroutineContext 객체의 CoroutineName 구성 요소에 접근하는 예시이다.

package chap06

import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
  val myCoroutineContext = CoroutineName("MyCoroutine") + Dispatchers.IO

  // myCoroutineContext 에 대해 get() 의 인자로 CoroutineName.Key 를 넘김으로써
  // myCoroutineContext 를 구성하는 CoroutineName 객체만 가져옴
  val nameFromContext1 = myCoroutineContext.get(CoroutineName.Key)

  // get() 은 연산자 함수(operator fun) 이므로 대괄호로 대체 가능
  val nameFromContext2 = myCoroutineContext[CoroutineName.Key]

  println(nameFromContext1)
  println(nameFromContext2)
}
CoroutineName(MyCoroutine)
CoroutineName(MyCoroutine)

3.2.2. 구성 요소 자체를 키로 사용하여 구성 요소에 접근: CoroutineName

CoroutineContext 의 주요 구성 요소인 CoroutineName, Job, CoroutineDispatcher, CoroutineExceptionHandler, 이들은 모두 클래스 내부에 companion object 로 CoroutineContext.Key<T> 를 구현한 Key 객체를 가지고 있다.
덕분에 context.get(CoroutineName.Key) 처럼 명시적으로 .Key 를 지정하지 않아도, context.get(CoroutineName) 처럼 클래스 자체를 키로 사용할 수 있다.

package chap06

import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    val myCoroutineContext = CoroutineName("MyCoroutine") + Dispatchers.IO

  // 내부적으로 CoroutineName.Key 사용
    val nameFromContext1 = myCoroutineContext.get(CoroutineName) // .Key 제거

    // get() 은 연산자 함수(operator fun) 이므로 대괄호로 대체 가능
    val nameFromContext2 = myCoroutineContext[CoroutineName] // .Key 제거

    println(nameFromContext1)
    println(nameFromContext2)
}

위 코드에서는 CoroutineName.Key 를 키로 사용하는 대신 .Key 를 제거한 CoroutineName 클래스를 키로 사용했지만 결과는 동일하다.

코틀린에서는 CoroutineContext.Element 를 구현한 클래스가 내부에 다음처럼 Key 를 정의해두는 방식이 일반적이다.

public companion object Key : CoroutineContext.Key<CoroutineName>

이렇게 정의된 키를 클래스 이름으로도 접근이 가능하기 때문에 CoroutineName 자체를 키로 사용할 수 있는 것이다.
즉, 아래 두 표현은 완전히 동일한 의미이다.

context.get(CoroutineName) == context.get(CoroutineName.Key)
context[CoroutineName] == context[CoroutineName.Key]

그 이유는 키가 들어갈 자리에 CoroutineName 을 사용하면 자동으로 CoroutineName.Key 를 사용하여 연산을 처리하기 때문이다.


3.2.3. 구성 요소의 Key 프로퍼티를 사용하여 구성 요소에 접근: myCoroutineName.key

CoroutineContext 의 구성 요소 인스턴스에서 .key 프로퍼티로 접근한 키를 해당 클래스의 companion object 로 선언된 Key 객체와 동일한 참조를 가진다.

package chap06

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext

fun main() = runBlocking<Unit> {
    val myCoroutineName: CoroutineName = CoroutineName("MyCoroutine")
    val myDispatcher: CoroutineDispatcher = Dispatchers.IO
    val myCoroutineContext: CoroutineContext = myCoroutineName + myDispatcher

    println(myCoroutineContext[myCoroutineName.key])
    println(myCoroutineContext[myDispatcher.key])
}
CoroutineName(MyCoroutine)
Dispatchers.IO

위 코드에서 보면 각 구성 요소의 .key 프로퍼티를 통해 CoroutineContext 에서 해당 요소를 가져온다.

중요한 점은 구성 요소의 key 프로퍼티는 동반 객체로 선언된 Key 와 동일한 객체를 가리킨다는 것이다.
즉, 각 구성 요소는 내부적으로 companion object 로 선언된 Key 객체를 공유하고 있으며, .key 프로퍼티는 항상 이 Key 를 반환한다. 예를 들어 CoroutineName.Key 와 myCoroutineName.key 는 같은 객체를 참조하며, 모든 CoroutineName 인스턴스는 같은 Key 객체를 공유한다.

package chap06

import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.runBlocking

fun main() = runBlocking{
    val myCoroutineName: CoroutineName = CoroutineName("MyCoroutine")

    if (myCoroutineName.key === CoroutineName.Key) {
        println("myCoroutineName.key와 CoroutineName.Key 는 동일함")
    }
}
myCoroutineName.key와 CoroutineName.Key 는 동일함

이처럼 모든 CoroutineName 인스턴스는 같은 Key 객체를 공유하며, 이를 통해 CoroutineContext 내부 요소를 안전하고 효율적으로 조회할 수 있다.


4. CoroutineContext 구성 요소 제거


4.1. minusKey() 함수로 구성 요소 제거

package chap06

import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext

fun main() = runBlocking{
  val myCoroutineName = CoroutineName("myCoroutineName")
  val myDispatcher = Dispatchers.IO
  val myJob = Job()
  val myCoroutineContext: CoroutineContext = myCoroutineName + myDispatcher + myJob

  val deletedCoroutineContext = myCoroutineContext.minusKey(CoroutineName)

  println("===== deletedCoroutineContext =====")
  println(deletedCoroutineContext[CoroutineName]) // or CoroutineName.Key
  println(deletedCoroutineContext.get(Dispatchers.IO.key))
  println(deletedCoroutineContext[Job]) // or Job.Key

  println("===== myCoroutineContext =====")
  println(myCoroutineContext[CoroutineName]) // or CoroutineName.Key
  println(myCoroutineContext.get(Dispatchers.IO.key))
  println(myCoroutineContext[Job]) // or Job.Key
}

myCoroutineContext 에서 CoroutineName 이 제거되어 반환된 CoroutineContext 가 deletedCoroutineContext 에 할당된다.
주의할 점은 minusKey() 를 호출한 CoroutineContext 객체(위에서는 myCoroutineContext)는 그대로 유지된다는 점이다.

===== deletedCoroutineContext =====
null
Dispatchers.IO
JobImpl{Active}@368239c8

===== myCoroutineContext =====
CoroutineName(myCoroutineName)
Dispatchers.IO
JobImpl{Active}@368239c8

정리하며..

  • CoroutineContext 객체는 코루틴의 실행 환경을 설정하고 관리하는 객체로 CoroutineDispatcher, CoroutineName, Job, CoroutineExceptionHandler 등의 객체를 조합하여 코루틴 실행 환경을 정의함
  • CoroutineContext 의 4가지 주요 구성 요소는 아래와 같음
    • CoroutineName 객체
      • 코루틴의 이름 설정
    • CoroutineDispatcher 객체
      • 코루틴을 스레드로 보내 실행
    • Job 객체
      • 코루틴을 조작하는데 사용
    • CoroutineExceptionHandler 객체
      • 코루틴의 예외 처리
  • CoroutineContext 객체는 키-값 쌍으로 구성 요소를 관리하며, 동일한 키에 중복된 값을 허용하지 않으므로 각 구성 요소는 한 개씩만 가질 수 있음
  • 구성 요소의 동반 객체로 선언된 key 프로퍼티를 사용하여 키 값에 접근할 수 있음
    • 예) CoroutineName 의 키 값은 CoroutineName.Key 를 통해 접근할 수 있음
  • 키를 연산자 함수인 get() 과 함께 사용하여 CoroutineContext 객체에 설정된 구성 요소에 접근할 수 있음
    • 예) myCoroutineContext 의 CoroutineName 구성 요소에 접근 시 myCoroutineContext.get(CoroutineName.Key) 로 접근 가능
  • get() 연산자는 대괄호 [] 로 대체할 수 있음
    • 예) myCoroutineContext.get(CoroutineName.Key) 이것은 myCoroutineContext[CoroutineName.Key] 로 대체 가능
  • CoroutineName, CoroutineDispatcher, Job, CoroutineExceptionHandler 는 companion object 인 Key 를 통해 CoroutineContext.Key 를 구현하기 때문에 그 자체를 키로 사용할 수 있음
    • 예) myCoroutineContext[CoroutineName.Key] 이것은 myCoroutineContext[CoroutineName] 로 대체 가능
  • CoroutineContext 객체에 minusKey() 함수를 사용하면 CoroutineContext 객체에서 특정 구성 요소를 제거한 객체를 반환받음

참고 사이트 & 함께 보면 좋은 사이트

본 포스트는 조세영 저자의 코틀린 코루틴의 정석을 기반으로 스터디하며 정리한 내용들입니다.






© 2020.08. by assu10

Powered by assu10