본문 바로가기

문법

[Swift] Error Handling[1편] 알아보자!

- Reference

- https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html#ID514

 

Error Handling — The Swift Programming Language (Swift 5.6)

Error Handling Error handling is the process of responding to and recovering from error conditions in your program. Swift provides first-class support for throwing, catching, propagating, and manipulating recoverable errors at runtime. Some operations aren

docs.swift.org

 

# 옵셔널과 에러핸들링의 차이점?

옵셔널은 값이 없으면 nil을 반환한다.
에러핸들링은 값이 없을때 뿐만아니라 여러개의 다양한 상황을 핸들링 할 수있다. 예시) 자판기에 돈이없거나, 재고가 없거나, 돈이 부족할때 등 상황에 맞는 부분에서 에러를 던지고 그에 맞는 에러메세지를 출력하거나 할 수 있다.
어떻게보면 디테일한 에러처리를 하는게 에러핸들링이 될수 있다고 생각했다.

# 에러 핸들링이란 ?

오류 처리는 프로그램의 오류 조건에 응답하고 복구하는 프로세스입니다.

Swift는 런타임에 복구 가능한 오류를 throw, catch, 전파 및 조작하기 위한 최고 수준을 지원한다.
옵셔널은 값이 없을을 나타내는 데 사용되지만 사용이 실패할 때 코드가 그에 따라 응답할 수 있도록 실패원인을 이해하는 것이 종종 유용하다.

에러 핸들링을 사용하는 상황이 언제있을까 ??

예를들어 디스크의 파일에서 데이터를 읽고 처리하는 방법을 생각해보자. 이 작업이 실패할 수 있는 방법들이 존재한다.

  1. 지정된 경로에 존재하지 않는 파일, 읽기 권한이 없는 파일이거나
  2. 호환되는 형식으로 인코딩 되지않는 파일 있을때

Swift 열거형은 관련 오류 조건 그룹을 모델링하는 데 특히 적합하며

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

오류는 문제가 발생했을때 오류를 던질 수 있다. 오류를 던기기위해 throw문을 사용한다. 예를들어
다음코드는 자판기에 5개의 추가동전이 필요함을 나타내는 오류를 발생시킨다.

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

# 오류 처리

오류가 발생하면 주변코드 일부가 오류처리를 담당해야한다.

문제를 수정하거나
다른 접근 방식을 시도하거나

사용자에게 오류를 알리는 등의 작업을 수행해야한다.

# 스위프트에서 오류처리하는 방법에는 4가지방법이 존재한다

- 함수에서 해당 함수를 호출하는 코드로 에러를 전파하는 방법

- do catch문으로 처리

- 오류를 선택적 값으로 처리하거나

- 오류가 발생하지 않을 것이라고 주장한다.

 

함수에서 오류가 발생하면 프로그램의 흐름이 변경되므로 코드에서 오류가 발생할 수 있는 위치를 빠르게 식별하는것이 중요하다.

NOTE

스위프트에 에러동작은 다른언어들과 크게 다르지않다. 그런데 다른점이 대부분 언어들은 오프젝트 C를 포함해서
계산비용이 많이 들어가는 콜스텍 처리를 사용하는데 스위프트는 그것이 포함되지있지 않다. 따라서 throw문의
성능 특성은 return문의 성능 특성과 비슷하다.

오류처리를 할 함수 작성

func canThrowErrors() throws -> String {
    return "오류를 던지는 함수입니다."
}

func cannotThrowErrors() -> String {
    return "오류를 던지지않는 함수입니다."
}
 

throw하는 함수만 오류를 전파할 수 있다.
던지지 않는 함수 내에서 발생한 모든 오류는 함수 내에서 처리되어야 한다.

아래 예시코드로 알아보자

요청한 항목을 사용할 수 없거나
재고가 없거나
현재 예치된 금액을 초과하는 비용이 있는 경우:

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

// throw 문은 즉시 프로그램 제어를 전송하므로 이러한 모든 요구 사항이 충족되는 경우에만 항목이 판매됩니다.

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

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8

do {
    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
    print("Success! JMT!")
} 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)")
}