Swift Language

Swift Language - 16 - 에러 처리 (Error Handling)

xnoag 2023. 2. 2. 17:27

프로그램 실행시 에러가 발생하면 그 상황에 대해 적절한 처리가 필요합니다. 이 과정을 에러 처리라고 부릅니다.

 

 

에러의 표시와 발생(Representing and Throwing Errors)

// Swift에서 에러는 Error 프로토콜을 따르는 타입의 값으로 표현됩니다. 
enum VendingMachineError: Error {
     case invalidSelection
     case insufficientFunds(coinsNeeded: Int)
     case outOfStock
}


/* 에러를 발생시킴으로써 무언가 기대하지 않았던 동작이 발생했고 작업을 계속 수행할 수 없다는 것을 알려줄 수 있습니다. 에러를 발생시키기 위해 throw 구문을 사용할 수 있습니다. 
예를들어, 다음 코드는 판매 기기에서 5개의 코인이 더 필요하다는 에러를 발생시킵니다. */
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

 

에러 처리(Handling Errors)

Swift에서 4가지 에러를 처리 방법이 있습니다.

  1. 에러가 발생한 함수에서 리턴값으로 에러를 반환해 해당 함수를 호출한 코드에서 에러를 처리하도록 하는 방법
  2. do-catch 구문을 사용하는 방법
  3. 옵셔널 값을 반환하는 방법
  4. assert를 사용해 강제로 크래쉬를 발생시키는 방법

 

에러를 발생시키는 함수 사용하기(Propagating Errors Using Throwing Fuctions)

// 어떤 함수, 메소드 혹은 초기자가 에러를 발생 시킬 수 있다는 것을 알리기 위해서 throw 키워드를 함수 선언부의 파라미터 뒤에 붙일 수 있습니다.
func canThrowErrors() throws -> String

func cannotThrowErrors() -> String


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


let favoriteSnacks = [
    "Alice": "Chips",
    "Bob": "Licorice",
    "Eve": "Pretzels",
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
     let snackName = favoriteSnacks[person] ?? "Candy Bar"
     try vendingMachine.vend(itemNamed: snackName)
}


struct PurchasedSnack {
    let name: String
    init(name: String, vendingMachine: VendingMachine) throws {
        try vendingMachine.vend(itemNamed: name)
        self.name = name
    }
}

 

Do-Catch를 이용해 에러를 처리하기(Handling Error Using Do-Catch)

// 에러가 do 구문 안에서 발생한다면 발생하는 에러의 종류를 catch 구문으로 구분해 처리할 수 있습니다.
do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
} catch {
    statements
}


var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
    print("Success! Yum.")
} catch VendingMachineError.invalidSelection {
    print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
    print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
    print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
    print("Unexpected error: \(error).")
}
// Prints "Insufficient funds. Please insert an additional 2 coins."


func nourish(with item: String) throws {
    do {
        try vendingMachine.vend(itemNamed: item)
    } catch is VendingMachineError {    // 모든 VendingMachineError 구분을 위해 is를 사용
        print("Invalid selection, out of stock, or not enough money.")
    }
}

do {
    try nourish(with: "Beet-Flavored Chips")
} catch {
    print("Unexpected non-vending-machine-related error: \(error)")
      // 여기에서 처럼 catch를 그냥 if-else에서 else 같이 사용 가능
}
// Prints "Invalid selection, out of stock, or not enough money."

 

에러를 옵셔널 값으로 변환하기 (Converting Errors to Optional Values)

// try? 구문을 사용해 에러를 옵셔널 값으로 변환할 수 있습니다. 만약 에러가 try? 표현 내에서 발생한다면, 그 표현의 값은 nil이 됩니다. 
func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction()

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


// 다음 코드는 데이터를 가져오는 여러 접근 방법을 시도하는데 접근 방법이 모두 실패하면 nil을 반환 합니다.
func fetchData() -> Data? {
    if let data = try? fetchDataFromDisk() { return data }
    if let data = try? fetchDataFromServer() { return data }
    return nil
}

 

에러 발생을 중지하기 (Disabling Error Propagation)

/* 함수나 메소드에서 에러가 발생되지 않을 것이라고 확신하는 경우 try!를 사용할 수 있습니다. 
혹은 runtime assertion을 사용해 에러가 발생하지 않도록 할 수 있습니다. */

 

 

 

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