본문 바로가기

Kotlin

[Kotlin] runCatching과 Result<T>

powered by pixabay

안녕하세요.

오늘은 코틀린의 runCatching과 Result 타입에 대해 알아보도록 하겠습니다.


📌 에러 처리

코틀린에서 에러 처리는 자바와 비슷하게 할 수 있습니다

  • try-catch를 사용한 에러 처리

추가로, 코틀린은 함수형 스타일의 프로그래밍을 지원합니다

이와 같이 사용할 수 있는 문법으로 runCatching이 있습니다


📌 runCatching

Result<T> 타입으로 성공/실패를 포장해줍니다

  • 예외가 발생하면 → Result.failure(exception)
  • 성공하면 → Result.success(value)

 

✅ When?

아래 경우일 때 runCatching을 사용하면 좋습니다

  • 연산의 결과를 체이닝하고, 각 단계에서의 예외를 일관되게 처리하고 싶을 때
  • 함수형 프로그래밍 패러다임을 따르는 코드베이스에서 작업할 때
  • 예외를 값으로 취급하여 이후 로직에서 유연하게 처리하고 싶을 때

 

✅ Code

fun riskyOperation(): String {
    if (Math.random() > 0.5) {
        throw RuntimeException("Something went wrong!")
    }
    return "Success!"
}

fun main() {
    val result = runCatching { riskyOperation() }

    result
        .onSuccess { println("결과: $it") }
        .onFailure { println("예외 발생: ${it.message}") }
}

 

✅ 내부 구현

runCatching의 내부 구현을 보면 아래와 같이 구성되어 있습니다

  • try-catch와 Result의 조합으로 구성되어 있습니다
@InlineOnly
@SinceKotlin("1.3")
public inline fun <R> runCatching(block: () -> R): Result<R> {
    return try {
        Result.success(block())
    } catch (e: Throwable) {
        Result.failure(e)
    }
}

📌 Result<T>

Result<T>의 실제 구현은 해당 링크를 들어가면 확인할 수 있습니다

https://github.com/JetBrains/kotlin/blob/whyoleg/dokka2-sync-stdlib/libraries/stdlib/src/kotlin/util/Result.kt#L22

 

kotlin/libraries/stdlib/src/kotlin/util/Result.kt at whyoleg/dokka2-sync-stdlib · JetBrains/kotlin

The Kotlin Programming Language. . Contribute to JetBrains/kotlin development by creating an account on GitHub.

github.com

 

복잡하게 구현되어 있지만, 이렇게 생각하면 쉽습니다

sealed class Result<out T> {
    class Success<T>(val value: T) : Result<T>()
    class Failure(val exception: Throwable) : Result<Nothing>()
}
  • T타입의 결과가 있을 수도 있고,
  • Throwable 예외가 있을 수도 있는 구조 입니다

 

🛠️ Result<T>를 다루는 다양한 방식

Result<T>와 함께 쓸 수 있는 다양한 함수들을 알아보겠습니다

 

간단한 runCatching 예제를 만들고, 이를 다양한 방식으로 활용해보겠습니다

fun divide(a: Int, b: Int) = runCatching { a / b }

 

✅ getOrNull()

성공하면 value, 실패하면 null을 반환합니다

fun main() {
    println(divide(10, 2).getOrNull()) // 5
    println(divide(10, 0).getOrNull()) // null
}

 

✅ getOrElse { }

실패했을 때 기본값을 지정할 수 있습니다

fun main() {
    println(divide(10, 2).getOrElse { -1 } ) // 5
    println(divide(10, 0).getOrElse { -1 } ) // -1
}

 

✅ onSuccess / onFailure

성공, 실패에 따른 분기 처리가 가능합니다

체이닝 방식으로 에러 핸들링을 지원합니다

fun main() {
    divide(10, 0)
        .onSuccess { println("Result: $it") }
        .onFailure { println("Error: $it") } 
    // Error: java.lang.ArithmeticException: / by zero
}

 

✅ map

성공 값을 조작하고 싶을 때 사용합니다

fun main() {
    println(divide(10, 2)
        .map { it * 2 }
        .getOrElse { -1 }
    ) // 10

    println(divide(10, 0)
        .map { it * 2 }
        .getOrElse { -1 }
    ) // -1
}

 

✅ recover

실패 시 대체 값을 제공합니다

  • Success<T> 타입으로 반환합니다
fun main() {
    println(divide(10, 2)
        .recover { 100 }
    ) // Success(5)
    
    println(divide(10, 0)
        .recover { 100 }
    ) // Success(100)
}

 

✅ recoverCatching

예외 중첩 처리가 가능합니다

fun functionA(): Result<String> {
    return Result.failure(Exception("A에서 실패!"))
}

fun functionB(): Result<String> {
    return Result.failure(Exception("B에서도 실패!"))
}

fun functionC(): Result<String> {
    return Result.success("C에서 가져온 값")
}

fun main() {
    val result = functionA()
        .recoverCatching { functionB().getOrThrow() }  // A가 실패하면 B 실행
        .recoverCatching { functionC().getOrThrow() }  // B도 실패하면 C 실행

    println(result.getOrElse { "모든 시도 실패" })  // 출력: "C에서 가져온 값"
}

 

감사합니다

'Kotlin' 카테고리의 다른 글

[Kotlin] Contract  (0) 2025.04.03
[Kotlin] Builder Pattern 대체하기  (0) 2025.01.13
[Kotlin] Generic (3) - 그 외  (2) 2024.12.19
[Kotlin] Generic (2) - 변성  (0) 2024.12.10
[Kotlin] Generic (1) - 제네릭?  (0) 2024.12.10