🐦 Swift

[Swift] 에러 처리, Result Type

dev_zoe 2023. 7. 20. 14:58
반응형

본 포스팅은 '스위프트 프로그래밍 (3판)' 도서 앨런 Swift 문법 마스터스쿨 강의를 참고하여

Swift 프로그래밍에 대해 정리하는 글입니다.

혹시 틀린 부분이 있거나 질문이 있으시다면 언제든지 댓글 달아주시면 정말 감사하겠습니다 :)


에러 처리

에러 처리는 왜 필요할까?

=> 프로그램은 완벽하게 돌아갈 수 없다. 만약에 에러에 대한 적절한 처리를 하지 않았을 경우 앱이 꺼지게 되고, 앱이 반복적으로 꺼지게 되면 사용자 경험에 상당히 악영향을 끼침

=> 따라서, 미리 발생할 수 있는 에러 케이스를 사전에 정의하여 적절한 처리를 해준다면, 최소한 앱이 꺼지는걸 방지할 수 있다.

 

Error 프로토콜

enum NetworkError: Error {
    case networkingError  // 네트워크 에러
    case dataError  // 데이터 에러
    case parseError // json parse 에러
}

- 보통 Error 프로토콜을 준수하는 enum 형식으로 에러를 표현한다.

 

do-catch

do {
	try network()
    // 정상적인 경우 코드를 하단에 작성
} catch {
	//에러일 때 실행할 코드
}

- 여기서 network() 라는, try 가 앞에 붙는 메서드는 반드시 에러를 던진다는 키워드인 throws 가 붙은 메서드여야 한다.

func network(_ search: String) throws -> Bool {

 

1) 에러 처리방법 1 : try

-> catch 문을 통해 에러별 디테일한 처리가 필요할 때

 

enum HeightError: Error {    //1) 에러 프로토콜 채택
    case maxHeight
    case minHeight
}

// 2) 에러가 발생할 수 있는 함수에 대한 정의 -> throws 키워드를 반드시 붙여야함

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
        }
    }
}


do {
    let isChecked = try checkingHeight(height: 200) // 에러를 던질 있는 함수 실행
    print("놀이기구 타는 것 가능: \(isChecked)")  // 위에서 정상적으로 넘어왔을 떄의 동작
    
} catch {
    print("놀이기구 타는 것 불가능")   // 실패했을 때의 동작
    
}

 

에러 별 세부 분기처리를 하고 싶을 때

catch HeightError.maxHeight {

} catch HeightError.minHeight {

} catch {  // 그 외의 에러 처리

}
catch  {
    if let error = error as? HeightError {    // 실제 우리가 정의한 구체적인 에러 타입이 아니고, 에러 타입(프로토콜)이 넘어올 뿐
        switch error {
        case .maxHeight:
        
        case .minHeight:
        
        }
    }
    
}

 

2) try? (Optional try)

-> 에러가 발생하는 경우 nil을 리턴하는 방식으로 에러처리 (do catch 문을 사용하지 않고 에러 처리 가능)

let isChecked = try? checkingHeight(height: 150)      // Bool?

if let result = isChecked {  // 벗겨질 경우 -> 즉 에러가 아닐 경우
    print(result)
}
func fetchData() -> Data? {
	if let data = try? fetchDataFromDisk() {
    	return data
    }
    
    if let data = try? fetchDataFromSerer() {
    	return data
    }
    
    return nil
}

 

3) try! (Forced try)

-> 에러가 발생하지 않음을 확신하는 경우(에러가 발생하면, 런타임 에러가 발생하므로 사용 지양)

 

Result

에러를 던지는 것이 아닌, 성공/실패를 담아 반환할 수 있는 타입

https://developer.apple.com/documentation/swift/result

- Result<Success, Failure> 에서 success, failure는 각각 열거형 타입이다.

 

func resultTypeCheckingHeight(height: Int) -> Result<Bool, HeightError> {
    
    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)

switch result {
case .success(let data):
    print("결과값은 \(data)입니다.")
case .failure(let error):
    print(error)
}

 

기존 do-catch 문과 다른 점은, 

1) 에러를 던질 수 있는 함수가 필요 없음 (throws 키워드 필요 X)

2) 에러 발생 시 throw를 통해 에러를 던지거나, try문이 필요 없음

 

do-catch문에 비해 가지는 이점?

1) 에러를 던지는 함수에서 에러 형식을 특정하기 어려움 (어떤 에러인지 알려면 추가적으로 시간이 소요됨)

2) 에러가 발생할 때 throw를 할 필요가 없어짐

3) catch문에서 switch 문으로 에러를 처리할 때, 에러 타입캐스팅 불필요

 

 

반응형