Image
Coroutines/Coroutine Basics

[Coroutine] 9. Coroutine Job에서 Exception이 발생했을 때 Exception Handling을 하는 방법

그림1. CoroutineException 발생

이번 글에서는 Job의 Exception을 Handling하는 방법을 살펴볼 것이다.

 

Exception을 Handling하는 방법은

  • invokeOnCompletion을 이용한 방법과
  • CoroutineExceptionHandler 을 이용하는 방법

이 있다. 먼저 invokeOnCompletion을 사용하는 방법을 살펴보자.

 

invokeOnCompletion을 이용한 Exception Handling

앞서 7번 글에서 다웠던 invokeOnCompletion을 코루틴 내부에서 애러가 발생했을 때도 사용할 수 있다.

아래와 같이 invokeOnCompletion을 사용해 Exception을 Handling할 수 있다.

fun main() = runBlocking<Unit> {
    val job = launch(Dispatchers.IO) { 
        throw IllegalArgumentException()
    }

    job.invokeOnCompletion { cause: Throwable? ->
        println(cause) 
    }

    job.start()
}

// java.lang.IllegalArgumentException 출력

하지만, 이 방법은 완료가 되었을 때 호출되는 람다식에서 핸들링 하는 것이다. 애러가 발생해 완료되든 Job이 모두 수행되어 완료되든 해당 람다식이 호출되는 것이므로, 애러를 핸들링 하는 부분을 분리하기 위해 조금 더 General한 방법이 필요하다.

 

그 방법이 바로 CoroutineExceptionHandler을 이용한 방법이다.

 

 

CoroutineExceptionHandler 이용하기

CoroutineExceptionHandler은 코루틴(Job) 내부에서 오류가 발생했을 때 애러를 처리할 수 있는 CoroutineContext이다. CoroutineContext에 대해서는 안다루었으므로, 그냥 간단히 생각해서 애러가 발생했을 때 실행되는 람다식이라고 생각하면 편하다.

 

아래와 같은 코드를 수행해보자.

fun main() = runBlocking<Unit> {
    val exceptionHandler = CoroutineExceptionHandler { _, exception ->
        println("CoroutineExceptionHandler : $exception")
    }

    val job = CoroutineScope(Dispatchers.IO + exceptionHandler).launch {
        throw IllegalArgumentException()
    }.join()
}

exceptionHandler는 CoroutineExceptionHandler을 구현하며 exception이 왔을 때 받은 exception을 출력해준다. 

따라서 위의 코드를 수행하면 아래와 같은 결과가 나온다.

CoroutineExceptionHandler : java.lang.IllegalArgumentException

Process finished with exit code 0

 

CoroutineExceptionHandler는 여러 Job에 붙일 수도 있다. 예를 들어 아래와 같은 코드를 수행해보자.

fun main() = runBlocking<Unit> {
  val exceptionHandler = CoroutineExceptionHandler { _, exception ->
    println("CoroutineExceptionHandler : $exception")
  }

  CoroutineScope(Dispatchers.IO + exceptionHandler + SupervisorJob()).apply {
    val job1 = launch {
      throw IllegalArgumentException()
    }.join()
    val job2 = launch {
      throw InterruptedException()
    }.join()
  }
}

그러면 출력은 다음과 같다. IllegalArgumentException과 InterruptedException이 발생한 것을 확인할 수 있다.

CoroutineExceptionHandler : java.lang.IllegalArgumentException
CoroutineExceptionHandler : java.lang.InterruptedException

Process finished with exit code 0

 

CoroutineExceptionHandler을 이용하여 애러에 맞게 처리하기

위의 코드를 조금 응용하면, when문을 이용하여 exception에 대해 타입 검사를 해서 다음과 같이 애러의 유형별로 처리하도록 만들 수도 있다.

fun main() = runBlocking<Unit> {
  val exceptionHandler = CoroutineExceptionHandler { _, exception ->
    println("CoroutineExceptionHandler : $exception")

    when(exception){
      is IllegalArgumentException -> println("More Arguement Needed To Process Job")
      is InterruptedException -> println("Job Interrupted")
    }
  }

  CoroutineScope(Dispatchers.IO + exceptionHandler + SupervisorJob()).apply {
    val job1 = launch {
      throw IllegalArgumentException()
    }.join()
    val job2 = launch {
      throw InterruptedException()
    }.join()
  }
}

 

위의 코드를 수행하면 다음의 결과가 나온다.

CoroutineExceptionHandler : java.lang.IllegalArgumentException
More Arguement Needed To Process Job
CoroutineExceptionHandler : java.lang.InterruptedException
Job Interrupted

Process finished with exit code 0

 

 

정리

Coroutine에서 공통 예외 처리기가 필요할 때는 CoroutineExceptionHandler을 이용하도록 하자.

 

 

 

반응형

 

이 글의 저작권은 '조세영의 Kotlin World' 에 있습니다. 글, 이미지 무단 재배포 및 변경을 금지합니다.

 

 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com