때때로 method 내에서 정상 로직 진행에 실패한 경우에도 data를 상위 메서드에 전달해야 하는 경우가 있다. (e.g., 추가인증필요, 기처리)

기본적으로 throw Exception을 return 처럼 data 반환 용도로 사용하는 것은 불가능 하기 때문에, return 기반으로 접근해야 한다.

 

직접 return 타입 정의해 사용하기

data class FooResult(
    val code: Code,
    val field1OnlyIfSuccess,
    val field2OnlyIfSuccess,
    val field3OnlyIfFailure
} {
    enum Code { SUCCESS, FAILURE }
 }

 

Result<T, R> 타입

  • R 타입에 code 없고 성공+실패 data field만 존재하는 경우 유용.
  • 보편적으로 많이 쓰이기 때문에 정의한 것이지 없어도 되는 타입. (각자 FooResult 정의해서 사용해도 된다)
Result<T, R> 타입
{
    "code" : T
    "message" : {String}
    "data" : R
}
enum FooCode { SUCCESS, FAILURE }
data class FooData(
    val field1OnlyIfSuccess,
    val field2OnlyIfSuccess,
    val field3OnlyIfFailure
}

Result<FooCode, FooData>

 

Result<T, R>과 rust, kotlin의 Result<T, E> 와는 다르다.
E는 Exception 타입을 의미하고, R은 data의 타입이다.
애초에 Result<T, E>는 callee 측에서 '반환타입을 Result<T, E>로 해서 반환하겠습니다' 해도 의미가 없고 (믿을 수 없으니)
caller가 호출 할 때 '결과를 Result<T, E>로 감싸서 받겠습니다' 해야 의미가 있다.

 

code, data를 함께 반환한다고 해서 caller에서 catch를 안해도 되는 것은 아니다.

caller는 callee를 믿지 않는 것이 좋다 (어차피 catch 해야 한다)  

따라서 정상 실패가 아닌 모든 예상치 못한 에러 (e.g., 파라미터 에러나 WebClient 에러)까지 (code, data)로 감싸서 반환 해서는 안된다. 

이런 예상치 못한 에러는 그냥 throw하게끔 두는 것이 낫다.