[Swift] Error Handling

Charlie·2022년 9월 13일
0
post-custom-banner

에러 처리

스위프트에서는 런타임에 에러가 발생한 경우 에러를 처리가히 위해 에러의 발생(throwing), 감지(catching), 증식(propagating), 조작(manipulating)을 지원하는 클래스를 제공한다.

에러의 표시와 발생

Swift에서 에러는 Error 프로토콜을 따르는 타입의 값으로 표현된다. Error 프로토콜은 비어있는데, 이 프로토콜을 따르는 타입이 에러 처리를 위해 사용될 수 있다는 것을 가르킨다.
Swift의 열거형은 특히 관련된 에러를 그룸화하고 추가적인 정보를 제공하기에 적합하다.
예를들어, 게임 자판기의 동작 에러와 관련한 에러를 다음과 같이 표현할 수 있다.

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

에러를 위와 같이 선언하고 5개의 코인이 더 필요하다는 에러를 다음과 같이 발생시킬 수 있다.

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

에러 처리

에러가 발생하면 특정 코드 영역이 해당 에러를 처리하도록 해야한다. Swift에서는 4가지 방법으로 에러를 처리할 수 있다.

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

에러를 발생시키는 함수 사용하기

어떤 메소드가 에러를 발생 시킬 수 있다는 것을 알리기 위해서 다음과 같이 throw 키워드를 함수 선언부의 파라미터 뒤에 붙일 수 있다.

func canThrowErrors() throw -> String

이러한 함수를 throwing function 이라고 하고 함수 내부에서 에러를 만들어 함수가 호출된 곳으로 전달한다.

오직 throwing function만이 에러를 발생시킬 수 있다. 만약 throwing function이 아닌 함수에서 throw가 발생한다면 반드시 그 함수내에서 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)")
    }
}

vend(itemNamed:) 메소드의 구현에서 guard구문을 사용해 에러가 발생하면 함수에서 에러를 발생시키고 빠르게 함수를 탈출할 수 있도록 한다. 해당 메소등는 에러를 발생시키기 때문에 이 메소드를 호출하는 메소드는 반드시 do-catch, try?, try! 등의 구문을 사용해 에러를 처리해야 한다.

Do-catch를 이용해 에러 처리

do-catch를 이용해 에러를 처리하는 코드 블럭을 작성할 수 있다.

do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
} catch {
    statements
}

catch 구문 뒤에 어떤 에러인지 적고 그것을 어떻게 처리할지 명시할 수 있고 만약 catch 구문 뒤에 에러 종류를 명시하지 않으면 발생하는 모든 에러를 지역 상수인 error로 바인딩한다.

에러를 옵셔널 값으로 변환하기

try? 구문을 사용해 에러를 옵셔널 값으로 변환할 수 있고, 에러가 발생한다면 nil이 된다.
try?는 만약 발생하는 모든 에러를 같은 방법으로 처리하고 싶을 때 사용한다. 예를 들어, 다음 코드는 데이터를 가져오는 여러 접근 방법을 시도하는데 접근 방법이 모두 실패하면 nil을 반환한다.

func fetchData() -> Data? {
    if let data = try? fetchDataFromDisk() { return data }
    if let data = try? fetchDataFromServer() { return data }
    return nil
}

에러 발생을 중지하기

메소드에서 에러가 발생되지 않을 것이라고 확신하는 경우 try!를 사용할 수 있다.

Reference

The Swift Language Guide

profile
Hello
post-custom-banner

0개의 댓글