Coroutine ‐ Exception Handling - thought-corner/Backend-PlayGround GitHub Wiki
예외 전파
- 코루틴 실행 도중 예외가 밣생하면 예외가 발생한 코루틴이 취소되고 예외가 부모 코루틴으로 전파된다.
- 이 때, 만약 예외를 전파받은 부모 코루틴도 예외를 적절히 처리하지 않는다면 취소되고, 그 상위의 코루틴으로 예외가 전파된다.
- 코루틴이 예외를 전파받아 상위 코루틴으로 전파 후 취소되면 취소가 해당 코루틴의 하위에 있는 자식 코루틴들에게 전파된다.
예외 전파 제한
- 코루틴의 구조화를 깨면 예외 전파를 제한할 수 있다.
- 다음과 같이
Job객체를 새로 만들어 구조화를 깨고 싶은 코루틴에 연결하면 구조화가 깨진다.
- 하지만 실제로는 위와 같이 사용을 하진 않는다.
- 코루틴의 구조화가 깨지면 예외 전파 뿐만 아니라 취소 전파도 제한된다. 이는 비동기 작업의 안정성이 깨진다.
SupervisorJob 객체는 자식 코루틴으로부터 예외를 전파 받지 않는 특수한 Job 객체이다. 이 객체는 예외 전파를 받지 않아 자식 코루틴에게서 예외가 발생하더라도 취소되지 않는다.
SupervisorJob 객체는 자식 코루틴에게서 발생한 예외가 다른 자식 코루틴에게 영향을 미치지 못하게 만드는데 사용된다. SupervisorJob 객체는 CoroutineScope 생성함수와 자주 사용된다.
public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent)
- 단일 코루틴 빌더 함수의 context 인자로
SupervisorJob 객체를 넘기고 그 하위에 자식 코루틴들을 생성할 경우 SupervisorJob 객체는 아무런 역할도 하지 못한다.
supervisorScope() 함수는 SupervisorJob 객체를 가진 CoroutineScope 객체를 생성한다.
supervisorScope() 함수를 통해 생성된 SupervisorJob 객체는 supervisorScope() 함수를 호출한 코루틴을 부모로 가진다.
supervisorScope() 함수를 통해 생성된 SupervisorJob 객체는 코드가 모두 실행되고 나서 자식 코루틴의 실행도 완료되면 자동으로 완료된다. 복잡한 설정 없이 구조화를 꺠지 않고 예외 전파를 제한할 수 있다.
예외 처리 1 - CoroutineExceptionHandler 사용해 예외 처리 하기
CoroutineExceptionHandler는 CoroutineContext 구성 요소 중 하나이다.
CoroutineExceptionHandler는 처리되지 않은 예외만 처리한다.
CoroutineExceptionHandler는 launch() 코루틴으로 시작되는 코루틴 계층의 공통 예외 처리기로 동작하는 구성요소이다.
- 예외를 로깅하거나 오류 메시지를 표시하기 위해 구조화된 코루틴들에 공통으로 동작하는 예외 처리기를 설정해야 하는 경우 사용된다.
예외 처리 2 - try ~ catch문을 사용한 예외 처리
try ~ catch문을 사용하면 코루틴에서 발생한 예외를 처리할 수 있다.
- 그러나 다음과 같이 코루틴 빌더 함수에
try ~ catch문을 사용하면 예외를 잡지 못한다.
예외 처리 3 - async 예외 처리
async() 코루틴 빌더 함수는 코루틴의 결과값을 Deferred 객체에 감싸고, await() 함수 호출 시점에 결과값을 노출한다.
- 만약 코루틴 실행 도중에 예외가 발생해 결과값이 없다면
Deferred에 대한 await() 함수 호출 시 예외가 노출된다.
async() 코루틴 빌더 함수 사용 시 await() 함수 호출부에서만 예외 처리를 하는 경우가 많은데 async() 코루틴 빌더 함수도 launch() 코루틴 빌더 함수와 마찬가지로 예외가 발생하면 부모 코루틴으로 예외를 전파한다.
전파되지 않는 예외
CancellationException은 부모 코루틴으로 전파되지 않는다.
CancellationException은 코루틴을 취소하기 위한 특별한 예외이기 때문이다.
@SinceKotlin("1.4")
public expect open class CancellationException : IllegalStateException {
public constructor()
public constructor(message: String?)
}
/**
* Creates an instance of [CancellationException] with the given [message] and [cause].
*/
@SinceKotlin("1.4")
public expect fun CancellationException(message: String?, cause: Throwable?): CancellationException
/**
* Creates an instance of [CancellationException] with the given [cause].
*/
@SinceKotlin("1.4")
public expect fun CancellationException(cause: Throwable?): CancellationException
CancellationException은 Exception으로 예외를 처리해선 안 된다.