Old_SWIFT(221012)/기본이야기

Result 타입에 대하여...

KataRN 2022. 10. 5. 16:19
반응형

안녕하세요.

 

KataRN입니다.

 

오늘은 Result 타입에 대해 알아보겠습니다.

 

Result 타입이 나오게 된 이유를 설명하기에 앞서 Error타입을 선행학습하시면 도움이 될것 같습니다.

https://katarnios.tistory.com/80

 

Error타입부터 try, do, catch까지...(모든건 Result 타입을 위해)

안녕하세요. KataRN입니다. 오늘은 Error 처리부터 try, do, catch에 대하여 알아보겠습니다. (Result타입에 대해 글을 쓰기전에 선행학습이 필요하다 판단하여 글을 쓰게되었습니다.) Swift에서 Error는 Erro

katarnios.tistory.com

 

 

작업(Task) 중에는 실패할 수 있는 작업이 있습니다. 디스크에 파일을 쓰거나, API를 호출해 네트워크를 통해 데이터를 가져온다거나, 특정 URL에 있는 데이터를 불러오는 작업이 이 경우에 해당합니다. 이런 실패 가능한 작업을 처리하기 위해 Swift에서는(4.2 이하 버전) do,try, catch,throws 문법을 제공했습니다. 이 방법은 에러를 런타임에 동기로 자동으로 처리할 수 있게 해주었습니다.

하지만 이 방법으로는 발생 가능한 여러가지 예외 상황에 대해 대처하기 어려운 단점이 있었습니다.

 

Swift 5.0에서는 Result 타입을 표준 라이브러리에 도입하였습니다.

Result 타입은 비동기 API와 같은 복잡한 코드에서 에러를 보다 간단하고 명확하게 처리할 수 있게 도와줍니다.​

Swift의 Result 타입은 success와 failure 두 가지 case가 있는 enum입니다. 둘 다 제네릭을 사용하여 구현되므로 개발자가 정한 타입의 연관값을 가질 수 있습니다. 그러나 failure의 연관값은 Swift의 Error 타입을 채택한 타입이어야 합니다.

 

정리하면 Error타입만으로는 부족해서 개선되어 나온것이 Result 타입이라는 것이죠.

 

그러면 비교해서 확인해볼게요.

Error타입의 예제입니다.(위의 링크에 있어요~)

enum DivisionError: Error {
  case dividedByZero
}

func division(numerator: Int, denominator: Int) throws {
  if denominator == 0 {
    throw DivisionError.dividedByZero
  } else {
    let result = numerator / denominator
    print(result)
  }
}

do {
  try division(numerator: 10, denominator: 0)
  print("Valid Division")
}

catch DivisionError.dividedByZero {
  print("Error: Denominator cannot be 0")
}

 

Result타입을 적용하였습니다.

enum DivisionError: Error {
  case dividedByZero
}

func division(numerator: Int, denominator: Int) -> Result<Int, DivisionError> {
  if denominator == 0 {
      return .failure(.dividedByZero)
  } else {
    let result = numerator / denominator
      return .success(result)
  }
}

let check = division(numerator: 10, denominator: 5)
switch check {
case .success(let result):
    print(result)
case .failure(let error):
    print(error)
}

 

이처럼 throws 키워드를 선언하고 Error를 던졌던걸 Result 타입으로 리턴하도록 변경하였습니다.

예제에서는 Result<Int, DivisionError>로 성공했을 경우의 값, 실패했을 경우 Error 열거형으로 반환됩니다.

 

Result 타입을 실제로 사용하기 전에 알아야 할 세 가지 내용이 있습니다.

 

1. get()

이 메서드는 성공한 값이 있으면 반환하고 없으면 에러를 throw합니다.

if let result = try? isOrderable.get() {
    print(result)
}

//isOrderable는 Result<Bool, McDonaldOrderError>으로 리턴한다고 가정합니다.

이처럼 get()메서드를 이용하여 성공하게되면 값을 프린트하고 실패하게되면 nil처리 되어 프린트가 안될것입니다.

 

 

2. Result 타입에는 throwing closure를 받는 초기자가 있습니다. 클로저가 값을 성공적으로 반환하면 success case에서 그 값을 연관값으로 저장하고 그렇지 않으면 throw 된 에러를 failure case의 연관값으로 저장합니다.
예를 들어 다음 코드를 실행하면, 성공일 경우 someFile의 내용이 success의 연관값으로 담겨 result에 저장되고 실패일 경우 에러가 failure의 연관값으로 담겨 result에 저장됩니다.
let result = Result { try String(contentsOfFile: someFile) }

 

3. 개발자가 정의한 특정 Error enum을 사용하는 대신 Swift의 Error 프로토콜을 사용할 수도 있습니다. Swift Evolution의 제안을 보면 Result 타입 대부분은 Swift.Error를 Error 타입의 인자로 사용할 것으로 예상됩니다. 라고 말하고 있습니다.

따라서 Result<Int, NetwordError>를 사용하는 대신 Result<Int, Error>를 사용할 수 있습니다. 이렇게 하면 타입이 지정된 throw의 타입 안전성을 잃게 되지만 그대신 다양한 Error enum을 사용할 수 있습니다. 위 제안을 따르냐 마느냐는 원하는 코딩 스타일에 따라 유연하게 선택하면 됩니다.

 

오늘도 긴글 읽어주셔서 감사합니다!

 

- 출처 : https://tech.burt.pe.kr/swift/what-new/swift-5.0/result-type

반응형