모종닷컴

CompletableFuture 예외 핸들링 3가지 방법 본문

Programming/JAVA

CompletableFuture 예외 핸들링 3가지 방법

모종 2022. 9. 24. 23:56
반응형

CompletableFuture의 예외를 핸들링하는 3가지 handle, whenComplete, excetpionally

CompletableFuture의 예외를 처리하는 방법에 대해 알아보려고 합니다.

여러 가지 방법들이 존재하지만 이번 포스팅에서 다룰 방법들은 자바에서 제공하는 기본 메서드들을 설명하려고 합니다. 이에는 handle(), whenComplete(), exceptionally() 세 가지 메서드가 존재합니다. 처음에 봤을 때 뭐가 다른 거지 하고 굉장히 헷갈려서 이번 기회에 글로 조금 정리해보려고 합니다. 

handle

먼저 handle 메서드를 보도록 하겠습니다.

public <U> CompletableFuture<U> handle(
    BiFunction<? super T, Throwable, ? extends U> fn) {
    return uniHandleStage(null, fn);
}

handle 메서드에 BiFunction 함수형 인터페이스를 넘겨주어야 하네요. BiFunction 설명을 보면 아래 사진과 같이 되어있습니다.

T, U는 실행할 함수의 인자이고 R은 실행할 함수의 리턴 타입입니다. 이해되었다면 다시 handle 메서드를 살펴보죠. handle 메서드에서는 CompletableFuture의 실행된 결과(T)와 예외(Throwable)를 인수로 하는 함수 작성이 가능한 모양입니다. 예시 코드를 보면서 이해해보죠.

@Test
fun `handle`() {
    val future = CompletableFuture.supplyAsync {
        2 / 0 // Division by zero
        return@supplyAsync "success"
    }.handle { t, u ->
        when {
            u != null -> 400
            t == "success" -> 200
            else -> 500
        }
    }
    println(future.get())
}

코드를 실행하면 400 숫자가 출력됩니다. 만약 예외를 발생시키는 코드를 삭제하면 200 숫자가 출력이 됩니다. 정리하자면 실행된 결과와 예외 모두 접근가능하며, 핸들링 함수를 통해서 다른 타입의 결과를 반환할 수 있습니다.

whenComplete

public CompletableFuture<T> whenComplete(
    BiConsumer<? super T, ? super Throwable> action) {
    return uniWhenCompleteStage(null, action);
}

BiConsumer 함수형 인터페이스가 있네요. handle과 동일하게 결과와 예외에 대해 접근가능합니다. 다른 점이라면 결과를 변환할 수는 없습니다. 따라서 예외나 결과는 다음 스테이지에 그대로 전달이 됩니다. 코드를 보면서 이해해보시죠.

@Test
fun `whenComplete`() {
    val future = CompletableFuture.supplyAsync {
        2 / 0 // Division by zero
        return@supplyAsync "success"
    }.whenComplete { t, u ->
        if(u != null) println("예외 발생 ${u.message}")
        else println("결과 : $t")
    }
    try {
        println("result : ${future.get()}")  // exception
    } catch (e: Exception) {
        println(e)
    }
}

실행하면 try-catch에서 예외를 처리하고 있음을 볼 수 있습니다. 예외가 발생해도 회복을 할 수가 없네요.. 따라서 whenComplete 뒤에 예외를 핸들링하는 스테이지를 추가해주지 않으면 안 됩니다. 

exceptionally

public CompletableFuture<T> exceptionally(
    Function<Throwable, ? extends T> fn) {
    return uniExceptionallyStage(fn);
}

 

Function 함수형 인터페이스입니다. Throwable 타입의 인자를 받아서 T 타입을 반환해야 합니다. 지금까지 봐온 handle이나 whenComplete와 다르게 예외에만 접근이 가능하네요. 예시를 보도록 하겠습니다.

@Test
fun `exceptionally`() {
    val future = CompletableFuture.supplyAsync {
        2 / 0 // Division by zero
        return@supplyAsync "success"
    }.exceptionally { t -> // t is wrapped with CompletionException.
        if(t.cause is ArithmeticException) "Known error" else "Unknown error"
    }

    println(future.get())
}

whenComplete와 다르게 예외 발생시 커버가 가능합니다.

비교해보자

마지막으로 3가지를 표로 정리해보겠습니다.

  handle whenComplete exceptionally
정상적인 결과에 접근가능? yes yes no
예외에 접근가능? yes yes yes
예외 발생시 커버가능? yes no yes
결과 타입을 변환가능? yes no no
정상적인 결과일 때 트리거? yes yes no
예외가 났을 때 트리거? yes yes yes

여기까지 읽어주셔서 감사합니다. 즐거운 주말 보내세요.

반응형

'Programming > JAVA' 카테고리의 다른 글

[JVM] 메모리 누수 현상 재현하고 이를 모니터링해보자.  (1) 2023.05.21
final, finally, finalize  (0) 2018.11.20
Comparison with Lambda  (0) 2018.10.29
자바 8 - Map  (0) 2018.10.19
캡슐화, 추상화, 인터페이스  (0) 2018.10.10