[Swift] Result Type


Result Type


Result 타입

  • 에러가 발생하는 경우, 에러를 따로 외부로 던지는 것이 아니라
  • 리턴 타입 자체를 Result Type(2가지를 다 담을 수 있는)으로 구현해서
  • 함수 실행의 성공과 실패의 정보를 함께 담아서 리턴

Result 타입을 사용하는 이유

throwing 함수

  • throwing 함수는 어떤 에러를 던지는 지 알기 어려움
  • do-catch 문만 본다면 어떤 상황에 어떤 에러를 던지는지 더 이해하기에 어려움이 있음

Result 타입

  • 반면 Result 타입은 에러 형식이 선언되고, 결과를 성공과 실패로 나누어 처리한다는 점에서 가독성이 더 좋음
  • 기존의 Objective-C적인 코드를 보다 Swift스럽게 만들어 줌

Result 타입의 내부 구조

@frozen enum Result<Success, Failure> where Failure : Error
  • Result 타입은 열거형으로 구현되어 있음
    • case success(연관값)
    • case failure(연관값)
  • Failure가 Error 프로토콜을 따르는 타입만 가능하도록 제약을 둔 제네릭 형식의 열거형 타입

기존 에러 처리 과정과 Result 타입을 활용한 에러 처리

  • Result 타입은 성공 / 실패의 경우를 깔끔하게 처리가 가능한 타입
  • 기존의 에러 처리 패턴을 완전히 대체하려는 목적이 아니라 개발자에게 에러 처리에 대한 다양한 처리 방법에 대한 옵션을 제공

기존 에러 처리 과정 3단계

// 1. 에러 정의 (어떤 에러가 발생할지 경우를 미리 정의)
enum HeightError: Error {    //에러 프로토콜 채택 (약속)
    case maxHeight
    case minHeight
}

// 2. throwing 함수 정의
func checkingHeight(height: Int) throws -> Bool {
    // 에러를 던잘수 있는 함수 타입 
    if height > 190 {
        throw HeightError.maxHeight
    } else if height < 130 {
        throw HeightError.minHeight
    } else {
        if height >= 160 {
            return true
        } else {
            return false
        }
    }
}

// 3. do-catch를 통한 에러 처리
do {
    let _ = try checkingHeight(height: 200)
    print("놀이기구 타는 것 가능")
} catch {
    print("놀이기구 타는 것 불가능")
}
  • 에러 타입 정의
    • Error 프로토콜을 채택하면 정의한 에러 타입 열거형이 스위프트의 에러 프로토콜 내에 통합됨
  • throwing 함수 정의
    • 에러 발생 가능한 상황에 throw 키워드로 에러를 던질(throwing) 수 있도록 정의
  • 에러 처리
    • 정식적인 처리 방법 : do-catch 블럭에서 처리

Result 타입을 활용한 에러의 처리

// 1. 에러 정의 (어떤 에러가 발생할지 경우를 미리 정의)
enum HeightError: Error {    //에러 프로토콜 채택 (약속)
    case maxHeight
    case minHeight
}

// 2. Result 타입의 함수 정의
func resultTypeCheckingHeight(height: Int) -> Result<Bool, HeightError> {
    // Result 타입에는 성공/실패했을 경우에 대한 정보가 다 들어있음
    if height > 190 {
        return Result.failure(HeightError.maxHeight)
    } else if height < 130 {
        return Result.failure(HeightError.minHeight)
    } else {
        if height >= 160 {
            return Result.success(true)
        } else {
            return Result.success(false)
        }
    }
}

// 리턴값을 받아서
let result = resultTypeCheckingHeight(height: 200)

// 3. switch문을 통한 결과 처리
switch result {
case .success(let data):
    print("결과값은 \(data)입니다.")
case .failure(let error):
    print(error)
}
  • 에러 타입 정의
    • Error 프로토콜을 채택하면 정의한 에러 타입 열거형이 스위프트의 에러 프로토콜 내에 통합됨
  • Result 타입 함수 정의
    • 함수의 내부 정의에서 정상적인 경우 .success(연관값) 리턴, 비정상 적인 경우(에러) .failure(연관값-에러) 리턴 하도록 정의
  • 결과 처리
    • 하나의 열거형 타입을 처리하는 (일반적인) 방식

네트워킹 코드에서 Result 타입의 활용

enum NetworkError: Error {
    case someError
}
// 1. Result 타입 사용 전

// 튜플타입을 활용, 데이터 전달
func performRequest(with url: String, completion: @escaping (Data?, NetworkError?) -> Void) {
    
    guard let url = URL(string: url) else { return }
    
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        if error != nil {
            print(error!)  // 에러가 발생했음을 출력
            completion(nil, .someError)  // 에러가 발생했으니, nil 전달
            return
        }
        
        guard let safeData = data else {
            completion(nil, .someError)  // 안전하게 옵셔널 바인딩을 하지 못했으니, 데이터는 nil 전달
            return
        }
        completion(safeData, nil)
    }.resume()
}

performRequest(with: "주소") { data, error in
    // 데이터를 받아서 처리
    if error != nil {
        print(error!)
    }

    // 데이터 처리 관련 코드

}

// 2. Result 타입 사용 후

func performRequest2(with urlString: String, completion: @escaping (Result<Data,NetworkError>) -> Void) {
    
    guard let url = URL(string: urlString) else { return }
    
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        if error != nil {
            print(error!)  // 에러가 발생했음을 출력
            completion(.failure(.someError))  // 실패 케이스 전달
            return
        }
        
        guard let safeData = data else {
            completion(.failure(.someError))  // 실패 케이스 전달
            return
        }
    
        completion(.success(safeData))  // 성공 케이스 전달
    }.resume()
}

performRequest2(with: "주소") { result in
    switch result {
    case .failure(let error):
        print(error)
    case .success(let data):

        // 데이터 처리 관련 코드

        break
    }
}

참고

Ellen Swift Class






© 2021. by hminkim