예외처리는 프로그램에서 에러가 발생한 상황을 감지하고 이에 대응하는 과정이다. Swift에서는 런타임에 에러가 발생한 경우, 이를 처리를 지원하는 클래스를 제공한다.
*런타임 : 프로그램이 컴파일되고 실행되는 동안에는 런타임이라는 단계가 있는데 런타임에서는 프로그램이 메모리에 로드되고, 변수 및 객체가 생성되며, 명령이 실행된다. 일반적으로 런타임은 프로그램이 시작되어 종료될 때까지를 말한다.
스위프트에서 오류는 error라는 프로토콜을 준수하는 타입의 값을 통해 표현된다.
protocol Error : Sendable
프로토콜 Error에는 자체 요구 사항이 없으므로 사용자가 정의해 사용할 수 있다. 많이 사용하는 표현은 열거형을 사용하는 것이다. 아래의 예시는 열거형이 Error라는 프로토콜을 채택함으로써 오류처리를 위한 타입임을 나타내고 있다. 오류의 종류는 invalidSelection (잘못된 선택), insufficientFunds(자금 부족), outOfStock(품절)이 있다.
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
열거형으로 오류를 미리 예상한 다음 오류때문에 프로그램이 정상적으로 진행되지 않을 경우 오류를 던져주면 된다.
함수에서 오류가 발생하면 프로그램의 흐름이 변경되므로 코드에서 오류가 발생할 수 있는 위치를 빠르게 식별할 수 있는 것이 중요하다. 공식문서에 따르면 Swift에는 오류를 처리하는 방법이 4가지 있다. 함수에서 해당 함수를 호출하는 코드로 오류를 전파하거나, do-catch문을 사용하여 오류를 처리하거나, 오류를 선택적 값으로 처리하거나, 오류가 발생하지 않는다고 주장할 수 있다.
throws 와 throw함수, 메서드 또는 초기화 프로그램이 오류를 발생시킬 수 잇음을 나타내려면 throws 키워드를 사용한다. throws 키워드는 오류가 발생할 가능성이 있는 메소드 제목 옆에 써준다. 이런 함수는 호출했을 때, 동작 도중 오류가 발생하면 자신을 호출한 코드에 오류를 던져서 오류를 알릴 수 있다. throw는 오류가 발생할 구간에서 써준다.
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String
아래의 코드를 보면 VendingMachine 이라는 클래스는 오류를 던질 수 있는 vend(itemNamed name:) 메서드가 있다. 만약 메서드 내부에서 오류가 발생하면 throws 키워드를 통해 오류를 던져서 오류가 발생했다는 것을 자신을 호출한 코드에 알린다. 또한 vend(itemNamed name:)라는 메서드가 오류를 던질 가능성이 있으므로, 이 메서드를 호출하는 함수 또한 오류를 던질 수 있어야 한다. 그래서 buyFavoriteSnack() 함수도 throws 키워드를 통해 오류를 던질 수 있는 함수로 구현해야 한다.
class VendingMachine {
var inventory = [
"candy": 12,
"chips": 10
]
func vend(itemNamed name: String) throws {
guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
...
}
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName)
}
trytry 키워드는 에러가 발생할 수 있는 코드 블록을 표시한다. try로 던져진 오류를 받을 수 있다. 위의 예시에서 vend(itemNamed name:)가 오류를 던질 수 있기에 try를 사용해 오류가 발생할 수 있는 키워드임을 나타낸다. 아래에서 다루겠지만 try 키워드가 사용된 코드는 do-catch 구문 내에 있어야 한다.
try? : do-catch 구문 없이도 사용이 가능한 키워드로 반환타입이 옵셔널로, 에러가 발생하면 nil을 반환한다.try! : 이 키워드를 사용하면 에러가 발생하면 앱이 강제 종료된다. 오류가 발생하지 않는다는 보장아래 사용해야 한다.오류를 던질 수 있는 함를 호출하는 코드는 반드시 오류를 처리할 수 있는 구문을 작성해주어야 한다. 그 오류를 처리할 수 있는 구문을 작성하는 방법 중 하나가 do-catch 구문이다. 함수 등에서 오류를 던져주면 오류 발생을 전달받은 코드 블록은 do-catch 구문을 사용하여 오류를 처리한다. do절 내부의 코드에서 오류를 던지면 catch절에서 오류를 전달받아처리해준다.
do {
try 오류 발생 가능코드
오류가 발생하지 않으면 실행할 코드
} catch 오류 패턴 1 {
처리 코드
} catch 오류 패턴 2 {
처리 코드
}
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).")
}
예시의 맨 마지막 경우처럼 catch 뒤에 오류의 종류를 명시하지 않고 코드 블록을 생성하면 블록 내부에 암시적으로 error라는 이름의 지역 상수가 오류의 내용으로 들어온다.