with ChatGPT 시리즈는 ChatGPT의 내용과 개인의 생각을 토대로 학습해 보는 컨텐츠입니다.
Kotlin은 Java와 마찬가지로 예외 기반 (exception-based) 오류 처리를 기본으로 채택하고 있습니다.
그러나 Kotlin은 함수형 스타일의 예외 처리 방식인 runCatching, Result, recoverCatching 등을 통해 보다 표현력 있고 선언적인 코드 작성을 가능하게 합니다.
🔹 1. 기본 예외 처리 - try-catch-finally
fun divide(a: Int, b: Int): Int {
return try {
a / b
} catch (e: ArithmeticException) {
println("0으로 나눌 수 없습니다.")
-1
} finally {
println("계산 종료")
}
}
- Java와 동일하게 동작
- 명령형 스타일
- 가독성이 떨어지고 중첩될 경우 유지보수가 어려움
🔹 2. 함수형 스타일: runCatching
val result = runCatching {
10 / 0
}
result
.onSuccess { println("성공: $it") }
.onFailure { println("실패: ${it.message}") }
- 내부적으로 try-catch를 감싸고 Result<T> 객체를 반환
- 후속 처리 (onSuccess, onFailure, getOrElse, recover) 등을 체이닝 방식으로 지원
🔹 3. runCatching vs try-catch 비교
항목 | try-catch | runCatching |
스타일 | 명령형 | 함수형 (표현식) |
반환값 | 명시적으로 없음 | Result<T> |
후속 처리 | 직접 분기 처리 | 체이닝 방식 (.onSuccess, .recover) |
중첩 흐름 | 복잡하고 늘어짐 | 간결하고 선언적 |
실무 사용 | 파일 I/O, 트랜잭션 등 | API 호출, 계산식, 데이터 파싱 등 |
🔹 4. 실무 예시: API 호출과 기본값 제공
val response = runCatching {
apiClient.fetchUser()
}.getOrElse {
User.guest()
}
- 실패하면 예외를 던지지 않고 기본값 반환
- getOrElse는 Result.failure일 경우 인자로 받은 람다의 결과를 대신 사용함
🔹 5. 결과 조합: map, getOrElse 활용
val name = runCatching { getUserName() }
.map { it.uppercase() }
.getOrElse { "Unknown" }
- 성공 결과를 변형 (map)
- 실패 시 기본값 (getOrElse) 제공
🔹 6. 예외 복구: recover, recoverCatching
✅ recover: 실패 시 기본값으로 복구
val result = runCatching {
"123a".toInt()
}.recover {
0
}
println(result.getOrThrow()) // 출력: 0
- recover는 실패한 경우 기본값으로 대체
- 복구 블록에서 예외가 발생하면 기존 예외를 유지함
✅ recoverCatching: 복구 블록도 실패할 수 있을 때
val result = runCatching {
"abc".toInt()
}.recoverCatching {
fetchDefaultFromServer() // 이 함수도 실패할 수 있음
}
result
.onSuccess { println("복구 성공: $it") }
.onFailure { println("복구 실패: ${it.message}") }
fun fetchDefaultFromServer(): Int {
throw IllegalStateException("서버 응답 실패")
}
- recoverCatching은 복구 블록에서 예외 발생 시 그 예외로 Result.failure 전환
- 복구 시도마저 실패할 수 있는 복잡한 흐름에서 안전한 선택
✅ 요약: recover vs recoverCatching
항목 | recover | recoverCatching |
복구 방식 | 실패 시 값으로 대체 | 실패 시 값으로 대체 |
복구 블록에서 예외 발생 시 | 기존 예외 유지 | 새 예외로 덮어씀 |
사용 추천 상황 | 간단한 기본값 대체 | 복구 로직도 실패할 수 있을 때 |
🚀 최종 정리
항목 | 설명 |
try-catch | Java와 동일한 명령형 예외 처리 방식 |
runCatching | 예외를 감싼 Result<T> 반환, 함수형 방식 |
onSuccess, onFailure | 분기 처리 체이닝 |
recover, recoverCatching | 실패 시 기본값 or 복구 시도 |
getOrElse, getOrNull, getOrThrow | 안전한 결과 추출 도구 |
Kotlin의 예외 처리는 단순한 오류 포착을 넘어서, 예외를 "데이터처럼 다루는 방식"을 지향합니다.
실무에서 안정성 높은 API, DSL, 비즈니스 로직을 설계할 때 큰 장점이 됩니다.
🤔 추가로 생각해 볼 질문들
- runCatching 내부 구현은 어떻게 생겼을까?
- Result 타입은 어떤 상황에서 활용되며, 어떻게 커스터마이징 할 수 있을까?
- recover와 recoverCatching은 코루틴 환경에서도 동일하게 동작할까?
- runCatching을 여러 개 중첩했을 때 어떻게 흐름을 제어할 수 있을까?
- Java에서 Kotlin의 Result와 비슷한 구조를 만들 수 있을까?