Swift

[Swift] 에러처리 전략

devharrry 2020. 2. 23. 01:27

안녕하세요. iOS앱을 개발하면서 Swift로 에러처리에 대한 글을 작성해보려고 합니다. 

 

에러처리를 적절하게 선택하면 코드 품질이 향상되고 프로그래머 의도가 명확해집니다. 

그래서 언제 무엇을 사용해야하는지를 간단하게 적어보겠습니다. 

 

에러는 크게 두 가지 범주로 나눌 수 있습니다. 

 

프로그래머가 제어할 수 없는 런타임에 대한 에러(Optional, throw) - recoverable

프로그래머의 실수에 대한 에러 (Assertion, FatalError) - non-recoverable

 

1)Optional 

옵셔널은 값이 있을 경우, 없으 경우에 대한 결과를 나타냅니다.

try? 표현식을 사용합니다.

 

언제?

- 에러가 간단할 때 

 

예제

func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction()

let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}

 

2)Throwing an Error

Throw(던지다) 즉, 에러를 던지는 것을 의미합니다. 잠재적인 에러 처리를 위해 호출부에서 do, try, catch문을 이용하여 작성합니다. 

 

언제?

- 메소드 내에서 오류를 로컬로 처리할 수 없을 때

- 오류가 동기적으로 전파되어야될 때 

- 로직 플로우가 변경되어야 될 때 

- 한 곳에서 여러 오류처리가 필요할 때 

 

예제 - 던지는 함수를 사용하여 오류 전달 및 do, try, catch 사용하기

enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}

struct Item {
    var price: Int
    var count: Int
}

class VendingMachine {
    var inventory = [
        "Candy Bar": Item(price: 12, count: 7),
        "Chips": Item(price: 10, count: 4),
        "Pretzels": Item(price: 7, count: 11)
    ]
    var coinsDeposited = 0

    func vend(itemNamed name: String) throws {
        guard let item = inventory[name] else {
            throw VendingMachineError.invalidSelection
        }

        guard item.count > 0 else {
            throw VendingMachineError.outOfStock
        }

        guard item.price <= coinsDeposited else {
            throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
        }

        coinsDeposited -= item.price

        var newItem = item
        newItem.count -= 1
        inventory[name] = newItem

        print("Dispensing \(name)")
    }
}

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8

do {
    try vendingMachine.vend(itemNamed: "Pretzels")
} catch VendingMachineError.invalidSelection {
    //
} catch VendingMachineError.insufficientFunds(_) {
    //
} catch VendingMachineError.outOfStock {
    //
}

 

3)Assertion

코드가 실행될 때 반드시 만족해야하는 조건을 코드 상에 명시해 놓는 것입니다. 

 

- assert(), assertionFailure()

디버깅 모드에서만 작동하고 디폴트는 크래시를 발생시킵니다. (런타임 강력 경고)

(assert를 많이 사용해도 실제 프로덕션 앱의 성능에 영향을 끼치지 않습니다!)

 

언제?

- 프로그래머 실수를 추적하는데 적합. 

 

예제

var someInt: Int = 0

assert(someInt == 0, "someInt != 0")

// 조건이 충족되지 않는다면 실행되지 않음.
print("someInt == 0")

 

- precondition(), preconditionFailure()

assert()와 차이점은 디버그/릴리즈 빌드 모두 검증된다는 점입니다. 조건을 만족하지 못하면 다음 플로우가 진행되지 않습니다. 

 

언제? 

- 외부 요인에 대한 에러 

 

예제

var someInt: Int = 0

precondition(someInt == 0, "someInt != 0")

// 조건이 충족되지 않는다면 실행되지 않음.
print("someInt == 0")

 

4)Fatal Error 

호출 즉시 크래시를 발생시킵니다. (프로세스를 죽임)

 

언제?

- 에러가 치명적일 때 

- 메소드에서 리턴할 것이 없을 때

예제

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TableViewCell else {
        fatalError("The dequeued cell is not an instance of TableViewCell.")
    }
    return cell
}

 

reference

https://jusung.gitbook.io/the-swift-language-guide/language-guide/17-error-handling

'Swift' 카테고리의 다른 글

[Swift] rethrows란  (0) 2020.02.23
[Swift] Extension이란  (0) 2020.01.05
[Swift] SwiftLint 적용하기  (0) 2020.01.05
[Swift] Extension(확장)으로 코드 가독성 올리기  (0) 2020.01.05
[Swift] 구조체란 무엇인가?  (0) 2020.01.03