μλ νμΈμ, κ°μμ λλ€. μ€λμ μλ¬λ₯Ό νΈλ€λ§νλ λ°©λ²μ λν΄μ μμ보λλ‘ ν κ²μ. swift 곡μλ¬Έμ 17λ²μ§Έ μ±ν°μμ! μΌμ£ΌμΌμ μ½3~5κ°μ© κΎΈμ€ν μ 리νλ€λ³΄λ λ²μ¨ 17κ°λ₯Ό νλ€μ γ γ νλ©΄μ λͺ¨λ κ°λ μ΄ μλ²½νκ² μ‘νμ§ μμμ§λ§, νλνλ λͺ¨λ₯΄λ λΆλΆμ μκ²λκ³ λμ³€λ λΆλΆλ λ€μ 보κ²λλ κ² κ°μμ.
κΈ°λ³Έμ μΌλ‘ 3νλ μ λ νλ©΄, λ©΄μ λλΉκΉμ§ λκ²μ£ ?! γ γ γ γ νμ΄ν μ λλ€ λͺ¨λ!
Swift document chap.17 Error Handling 보λ¬κ°κΈ°
Error Handling
νλ‘κ·Έλ¨ μ€ν μ μλ¬κ° λ°μνλ©΄ κ·Έ μν©μ λν΄ μ μ ν μ²λ¦¬κ° νμνλ°, μ΄λ° μ²λ¦¬λ₯Ό “Error handling”μ΄λΌκ³ νλ€.
Swiftμμλ λ°νμ μλ¬κ° λ°μνλ©΄, μλ¬λ₯Ό μ²λ¦¬νκΈ° μν΄ μλ¬μ λ°μ (throwing) , κ°μ§(catching), μ ν(propagating), μ‘°μ(mainpulating)μ μ§μνλ μΌκΈ ν΄λμ€λ₯Ό μ 곡νλ€.
μ΄λ€ λͺ λ Ήμ μμ ν μ€νλλ κ²μ΄ 보μ₯λμ§ μλ κ²½μ°, μ΅μ λμ μ¬μ©νμ¬ μλ¬κ° λ°μν΄ κ°μ΄ μλ€κ³ nil λ‘ νμν μ μλ€. νμ§λ§ μ΄λ€ μ’ λ₯μ μλ¬κ° λ°μνλμ§λ μ μ μλ€. μ΄λ΄λλ ꡬ체μ μΌλ‘ λ°μν μλ¬λ₯Ό νμΈν μ μμ΄μΌ μ½λλ₯Ό μμ±νλ μ¬λμ΄ μλ¬μ κ²½μ°μ λ°λ₯Έ μ μ ν μ²λ¦¬λ₯Ό ν μ μλ€.
Swiftμμ μλ¬μ²λ¦¬λ Cocoaμ NSError ν΄λμ€μ μνΈνΈν λλ μλ¬ νΈλ€λ§ ν¨ν΄μ μ¬μ©νλ€.
μλ¬μ νμμ λ°μ
Swiftμμ μλ¬λ Error νλ‘ν μ½μ λ°λ₯΄λ νμ μ κ°μΌλ‘ ννλλ€. λΉμ΄μλ μ΄ νλ‘ν μ½μ νλ‘ν μ½μ λ°λ₯΄λ νμ μ΄ μλ¬μ²λ¦¬λ₯Ό μν΄ μ¬μ© λ μ μλ€λ κ²μ κ°λ₯΄ν¨λ€.
Swiftμ μ΄κ±°νμ νΉλ³ν μ΄λ° κ΄λ ¨λ μλ¬λ₯Ό Grouping νκ³ μΆκ°μ μΈ μ 보λ₯Ό μ 곡νκΈ°μ μ ν©νλ€.
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
μλ¬λ₯Ό λ°μμν΄μΌλ‘μ¨ λ¬΄μΈκ° κΈ°λνμ§ μμλ λμμ΄ λ°μνκ³ , μμ μ κ³μ μνν μ μλ€λ κ²μ μλ €μ€ μ μλ€. μλ¬λ₯Ό λ°μμν€κΈ° μν΄ throw ꡬ문μ μ¬μ©ν μ μλ€.
λ€μ μ½λλ ν맀기기μμ 5κ°μ μ½μΈμ΄ λ νμνλ€λ μλ¬λ₯Ό λ°μμν¨λ€.
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
μλ¬ μ²λ¦¬
μλ¬κ° λ°μνλ©΄ νΉμ μ½λμμμ΄ ν΄λΉ μλ¬λ₯Ό μ²λ¦¬νκ² ν΄μΌνλ€. λ¬Έμ λ₯Ό ν΄κ²°νκ±°λ, μ°νν μ μλ λ°©λ²μ μλνκ±°λ, μλλ©΄ μ¬μ©μμκ² μ€ν¨ μν©μ μ리λ κ²μ΄ μλ¬ μ²λ¦¬μ λ°©μμ΄ λ μ μλ€.
Swiftμμλ 4κ°μ§ μλ¬λ₯Ό μ²λ¦¬νλ λ°©λ²μ΄ μλ€.
- Error propagation (μλ¬λ₯Ό μ ν) (μλ¬κ° λ°μν ν¨μμμ 리ν΄κ°μΌλ‘ μλ¬λ₯Ό λ°ννμ¬ ν΄λΉ ν¨μλ₯Ό νΈμΆν μ½λμμ μλ¬λ₯Ό μ²λ¦¬νλλ‘ νλ λ°©λ²)
- do-catch ꡬ문μ μ¬μ©νλ λ°©λ²
- μ΅μ λ κ°μ λ°ννλ λ°©λ²
- μ€λ₯κ° λ°μνμ§ μμ κ²μ΄λΌκ³ μ μνλ λ°©λ² (assert λ₯Ό μ¬μ©νμ¬ κ°μ λ‘ ν¬λμ¬λ₯Ό λ°μμν€λ λ°©λ²)
ν¨μμμ μ€λ₯κ° λ°μνλ©΄ νλ‘κ·Έλ¨μ νλ¦μ΄ λ³κ²½λκΈ° λλ¬Έμ μ½λμμ μ€λ₯κ° λ°μν μ μλ μμΉλ₯Ό λΉ λ₯΄κ² μλ³ν μ μμ΄μΌ νλ€. μ½λμμ μ΄λ¬ν μ€λ₯λ₯Ό μλ³νλ €λ©΄, try, try?, try! ν€μλλ₯Ό μ¬μ©νλ©΄ λλ€.
1. Throw ꡬ문μ μ¬μ©ν΄μ μλ¬λ₯Ό μ ννκΈ° (propagating)
μ΄λ€ ν¨μ, λ©μλ, μμ±μκ° μλ¬λ₯Ό λ°μμν¬ μ μλ€λ κ²μ μν΄μ throw ν€μλλ₯Ό ν¨μ μ μΈλΆμ νλΌλ―Έν° λ€μ λΆμΌ μ μλ€. throw ν€μλλ‘ νμλ ν¨μλ₯Ό throwing func λΌκ³ λΆλ₯Έλ€.λ§μ½ ν¨μκ° λ¦¬ν΄κ°μ λͺ μνλ€λ©΄, throw ν€μλλ 리ν΄κ° νμ κΈ°νΈ -> μ μ μ λλ€.
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String
throwing functionμ ν¨μ λ΄λΆμμ μλ¬λ₯Ό λ§λ€μ΄μ ν¨μκ° νΈμΆν κ³³μΌλ‘ μ λ¬νλ€. throwing ν¨μλ§ μλ¬λ₯Ό μ λ¬ν μ μλ€. κ·Έλ¬λ―λ‘ throwing ν¨μκ° μλ ν¨μμμλ μλ¬λ₯Ό λ΄λΆμμ μ²λ¦¬ν΄μΌνλ€.
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)")
}
}
μ μ½λλ VendingMachine ν΄λμ€μμ λ°μν μ μλ μ€λ₯λ₯Ό μ²λ¦¬νκΈ° μν vend(itemNamed name: ) λ©μλλ₯Ό κ°μ§λ€.
ν΄λΉ λ©μλμμλ guard λ¬Έμ νμ©νμ¬ snackμ ꡬ맀νλ κ³Όμ μμ μλ¬κ° λ°μνλ©΄ ν¨μμμ μλ¬λ₯Ό λ°μμν€κ³ λΉ λ₯΄κ² ν¨μλ₯Ό νμΆ ν μ μλλ‘ νλ€ (early exit).
vend(itemNamed: ) λ©μλλ μλ¬λ₯Ό λ°μμν€κΈ° λλ¬Έμ μ΄ λ©μλλ₯Ό νΈμΆνλ λ©μλλ λ°λμ do-catch, try?, try! ꡬ문μ μ¬μ©νμ¬ μλ¬λ₯Ό μ²λ¦¬ν΄μΌνλ€.
let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels",
]
// buyFavoriteSnackν¨μλ μ£Όμ΄μ§ μ¬λμ κ°μ₯ μ’μνλ μ€λ΅μ΄ 무μμΈμ§ νμΈνκ³
// κ·Έκ²μ vend λ©μλλ₯Ό νΈμΆνμ¬ κ΅¬λ§€λ₯Ό μλνλ λμμ μν
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName)
}
buyFavoriteSnack(person:vendingMaching: ) ν¨μμμ vendingMachine μΈμ€ν΄μ€μ vend(itemNamed: ) λ©μλμμ μλ¬κ° λ°μνλ©΄ μ΄ μλ¬λ buyFavoriteSnack ν¨μμ λ΄λΆμ μ νλλ€. λ°λΌμ μ΄ ν¨μμμ ν΄λΉ λ©μλλ₯Ό μ¬μ©νλ €λ©΄ try ν€μλλ₯Ό ν¨κ» μ¬μ©ν΄μ€μΌνλ€. κ·Έλ κ² λλ©΄ μ€λ₯ λ°μ μ buyFavoriteSnack ν¨μμμ μ€λ₯κ° μ νλλ€.
struct PurchasedSnack {
let name: String
init(name: String, vendingMachine: VendingMachine) throws {
try vendingMachine.vend(itemNamed: name)
self.name = name
}
}
Throwing μμ±μλ ν¨μμμ μ€λ₯λ₯Ό λ°μνλ κ²κ³Ό κ°μ λ°©μμΌλ‘ μ€λ₯λ₯Ό μ νν μ μλ€. μλ₯Όλ€μ΄ μμ μ½λμμ PurchaseSnack ꡬ쑰체μ μμ±μλ μ΄κΈ°ν νλ‘μΈμ€μ μΌλΆλ‘ throwing ν¨μλ₯Ό νΈμΆνκ² λλλ° λ§μ½ μ€λ₯κ° λ°μνλ€λ©΄ ν΄λΉ μ€λ₯λ₯Ό νΈμΆμμκ² μ ννμ¬ μ²λ¦¬ν μ μκ² νλ€.
2. Do-catch ꡬ문μ μ΄μ©ν΄μ μλ¬λ₯Ό μ²λ¦¬νκΈ°
do-catch λ₯Ό μ΄μ©νμ¬ μλ¬λ₯Ό μ²λ¦¬ν μ μλ€. λ§μ½ μλ¬κ° do ꡬ문 μμμ λ°μνλ€λ©΄, λ°μνλ μλ¬μ μ’ λ₯λ₯Ό catch ꡬ문μΌλ‘ ꡬλΆνμ¬ μ²λ¦¬νλ κ²μ΄λ€.
do {
try expression
statements
} catch pattern 1 {
statements
} catch pattern 2 where condition {
statements
} catch {
statements
}
catch ꡬ문 λ€μ μ΄λ€ μλ¬μΈμ§ μ κ³ κ·Έκ²μ μ΄λ»κ² μ²λ¦¬ν κ²μΈμ§ λͺ μν μ μλ€. λ§μ½ catch ꡬ문λ€μ μλ¬ μ’ λ₯λ₯Ό λͺ μνμ§ μμΌλ©΄ λ°μνλ λͺ¨λ μλ¬λ₯Ό μ§μμμμΈ error λ‘ λ°μΈλ©νλ€.
λ€μ μ½λλ VendingMachineError μ΄κ±°νμ λͺ¨λ μΈκ°μ§ μλ¬ μ’ λ₯μ λν΄ μ²λ¦¬νλ μ½λμ΄λ€.
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."
μ μμ μμ buyFavoriteSnack(person: vendingMachine: ) ν¨μλ try ννμμμ νΈμΆλλ€. μλνλ©΄ μ΄ ν¨μκ° μλ¬λ₯Ό λ°μμν¬ μ μκΈ° λλ¬Έμ μλ¬κ° λ°μνμλ§μ catch ꡬ문μ μ λ¬ν΄ μ μ ν μ²λ¦¬λ₯Ό ν μ μκ² νκΈ° μν΄μλ€. λ§μ½ λ°μν μλ¬ μ’ λ₯λ₯Ό μ²λ¦¬νλ catchκ΅¬λ¬Έμ΄ μλ€λ©΄, μλ¬λ λ§μ§λ§ catch ꡬ문μ κ±Έλ¦¬κ² λμ΄ μ§μ μμ error λ‘ μ²λ¦¬ν μ μλ€.
μμ μ½λμμ μ¬μ©λ buyFavoriteSnack ν¨μλ μκΉ μ μν λ μ€λ₯λ₯Ό λ°μν μ μλ ν¨μλ‘ μ μνκΈ° λλ¬Έμ tryμ ν¨κ» μ¬μ©λμ΄μΌ νλ€. λ§μ½ μ€λ₯κ° λ°μνμ§ μλλ€λ©΄ κ·Έλ₯ μ€νλμ§λ§ μ€λ₯κ° λ°μνκ² λλ©΄ μ΄λ€ μ€λ₯κ° λ°μνλμ§μ λ°λΌ catch μ μ΄ μ νλλ€. catch μ μμ do μ μμ λ°μ κ°λ₯ν λͺ¨λ μ€λ₯λ₯Ό μ²λ¦¬ν νμλ μκ³ μ νλ§ ν΄λ λλ€. νμ§λ§ κ²°κ΅ μ νλ μ€λ₯λ λ μΈμ κ°λ μ²λ¦¬λμ΄μΌ νλ€. λ§μ½ μ€λ₯κ° μ²λ¦¬λμ§ μκ³ μ΅μμ λ²μλ‘ μ νλλ κ²½μ°μ λ°νμ μ€λ₯κ° λ°μνκ² λλ€.
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."
μ μ½λμμλ ν¨μ λ΄μμ VendinMachineError λ₯Ό μ²λ¦¬ν μ μλλ‘ μ μλμγ λ€. νμ§λ§ κ·Έ μΈμ μλ¬λ μ²λ¦¬νμ§ μκ³ κ·Έλ₯ μ νλ§ νλ€. κ·Έλ κΈ° λλ¬Έμ μ€μ λ‘ μ¬μ©ν λλ do-catch λ¬Έμ νλ λ μ¬μ©νμ¬ κ·Έ λ°μ μλ¬μ λν μ²λ¦¬λ₯Ό μνν΄μ£Όλ μ½λμ΄λ€.
func eat(item: String) throws {
do {
try vendingMachine.vend(itemNamed: item)
} catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
print("Invalid selection, out of stock, or not enough money.")
}
}
μ¬λ¬ κ°μ μ€λ₯λ₯Ό λμΌνκ² μ²λ¦¬νλ λ°©λ²μ catch λ€μ μ€λ₯μ μ’ λ₯λ₯Ό λνλΌ λ μΌν , λ‘ κ΅¬λΆνλ κ²μ΄λ€. μ μ½λμ²λΌ λ§λ€λ©΄ μΈ κ°μ§ μ€λ₯μ λν΄μ λͺ¨λ λμΌν μ²λ¦¬λ₯Ό νλ€.
3. μλ¬λ₯Ό μ΅μ λ κ°μΌλ‘ λ°ννκΈ°
try? ꡬ문μ μ¬μ©ν΄μ μλ¬λ₯Ό μ΅μ λ κ°μΌλ‘ λ°νν μ μλ€. λ§μ½ μλ¬κ° try? λ΄μμ λ°μνλ€λ©΄ κ·Έ κ°μ nil μ΄ λλ€.
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
λ§μ½ someThrowingFunctionμ΄ μλ¬λ₯Ό λ°μμν€λ©΄ xμ yλ nil μ΄λλ€. κ·Έλ μ§ μμΌλ©΄ 리ν΄κ°μ κ°λλ€. xμ yλ someThrowingFunctionμ νμ μ΄ μ΄λ»λ μκ΄μμ΄ μ΅μ λ κ°μ κ°κ² λλ€.
try?λ λ§μ½ λ°μνλ λͺ¨λ μλ¬λ₯Ό κ°μ λ°©μμΌλ‘ μ²λ¦¬νκ³ μΆμ λ μ¬μ©λλ€. λ€μ μ½λμμλ λ°μ΄ν°λ₯Ό κ°μ Έμ€λ μ¬λ¬ μ κ·Ό λ°©λ²μ μλνλλ° μ κ·Όλ°©λ²μ΄ λͺ¨λ μ€ν¨νλ©΄ nil μ λ°ννλ€.
func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}
4. μλ¬ λ°μμ μ€μ§μν€κΈ° (assert)
λ§μ½ throwing ν¨μλ λ©μλκ° λ°νμ μλ¬λ₯Ό λ°μμν€μ§ μμ κ²μ΄λΌλ νμ μ΄ λλ κ²½μ° try!λ₯Ό μ¬μ©νλ©΄ λλ€. νμ§λ§ μ΄ κ²½μ° μ€λ₯κ° λ°μνλ©΄ λ°νμ μλ¬λ₯Ό λ°μμν€λ μ£Όμνμ.
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
μ μ½λλ μ§μ λ κ²½λ‘μ μ΄λ―Έμ§ 리μμ€λ₯Ό λ‘λνλ μ½λμ΄κ³ μ¬κΈ°μ μ¬μ©λ loadImage ν¨μλ μ€λ₯λ₯Ό λ°μμν¬ μ μλ ν¨μμ΄λ€. νμ§λ§ κ°λ°μκ° μ λλ‘ λ°μνμ§ μμ κ²μ΄λΌκ³ νμ νλ©΄ μμ²λΌ try! λ₯Ό μ¬μ©νλ©΄ λλ€.
Specifying Cleanup Action
μ½λ μ€νμ΄ νμ¬ μ½λ λΈλ‘μ λ λκΈ° μ§μ μ defer λ¬Έμ μ¬μ©νμ¬ νΉμ μ½λλ₯Ό μ€νν μ μλ€. μ΄ λͺ λ Ήλ¬Έμ μ€ννλ©΄ νμ¬ μ½λ λΈλ‘μ λ λλ λ°©μμ κ΄κ³μμ΄ μνν΄μΌ νλ μμ μ μνν μ μλ€. μλ₯Ό λ€μ΄ μ€λ₯κ° λ°μνμ¬ μ’ λ£νκ±°λ return, breakμ κ°μ λͺ λ Ήλ¬Έμ μν΄ μ’ λ£λλ κ²½μ° defer λ¬Έμ μ¬μ©νμ¬ νμΌ λμ€ν¬λ¦½ν°κ° λ«νκ³ λ©λͺ¨λ¦¬ ν λΉμ μλμΌλ‘ ν΄μ ν μ μλ€.
deferλ¬Έμ νμ¬ λ²μκ° μ’ λ£λ λκΉμ§ μ€νμ μ°κΈ°νλ€. μ΄ κ΅¬λ¬Έμ defer ν€μλμ λμ€μ μ€νλ μ½λλ‘ κ΅¬μ±λλ€. λμ€μ μ€νλ μ½λμλ break, returnκ³Ό κ°μ μ μ΄λ₯Ό μ μ‘νκ±°λ μ€λ₯λ₯Ό λ°μμν€λ μ½λλ₯Ό μμ±ν μ μλ€. μ§μ°λ μμ μ μμ€ μ½λμμ μμ±λ μμμλ λ°λλ‘ μ€νλλ€. μ¦ μ²« λ²μ§Έ defer λ¬Έμ μ½λκ° λ§μ§λ§μΌλ‘ μ€νλκ³ λ λ²μ§Έ deferλ¬Έμ μ½λκ° κ·Έμ μ μ€νλλ€. μ¦ λ§μ§λ§μ ꡬνλ deferλ¬Έμ΄ μ μΌ λ¨Όμ μ€νλλ€.
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file) // blockμ΄ λλκΈ° μ§μ μ μ€ν, μ£Όλ‘ μμ ν΄μ λ μ μ§μ μ¬μ©
}
while let line = try file.readline() {
// Work with the file.
}
// close(file) is called here, at the end of the scope.
}
}
μ μμ λ defer ꡬ문μ μ΄μ©ν΄ open(:) ν¨μμ μ§μ μ΄λ£¨λ close(:) ν¨μλ₯Ό μ€ννλ€.