Swift ์—๋Ÿฌ ์ฒ˜๋ฆฌ(Error Handling)
iOS ๐Ÿ”ฅ/Swift

Swift ์—๋Ÿฌ ์ฒ˜๋ฆฌ(Error Handling)

ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰์‹œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ทธ ์ƒํ™ฉ์— ๋Œ€ํ•œ ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์„ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

Swift์—์„œ๋Š” ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ ๊ทธ๊ฒƒ์˜ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ์—๋Ÿฌ์˜ ๋ฐœ์ƒ(Throwing), ๊ฐ์ง€(Cactching), ์ฆ์‹(Propagating), ์กฐ์ž‘(manipulating)์„ ์ง€์›ํ•˜๋Š” ์ผ๊ธ‰ ๊ฐ์ฒด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Swift์—์„œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋Š” Cocoa์˜ NSError ํด๋ž˜์Šค์™€ ์ƒํ˜ธ ํ˜ธํ™˜๋˜๋Š” Error Handling Pattern์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์—๋Ÿฌ์˜ ํ‘œ์‹œ์™€ ๋ฐœ์ƒ

Swift์—์„œ ์—๋Ÿฌ๋Š” Error ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋Š” ํƒ€์ž…์˜ ๊ฐ’์œผ๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.
์ด ์ค‘์—์„œ ์—ด๊ฑฐํ˜•์€ ์ด๋Ÿฐ ๊ด€๋ จ๋œ ์—๋Ÿฌ๋ฅผ ๊ทธ๋ฃนํ™”(Grouping)ํ•˜๊ณ  ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๊ธฐ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

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

throw VendingMachineError.insufficientFunds(coinsNeeded: 5) // Error๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” Throw ๊ตฌ๋ฌธ

์—๋Ÿฌ ์ฒ˜๋ฆฌ

Swift์—์„œ๋Š” 4๊ฐ€์ง€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.
*1. Error๋ฅผ Returnํ•˜์—ฌ ํ˜ธ์ถœํ•œ ํ•จ์ˆ˜๋กœ๋ถ€ํ„ฐ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ• *
*2. do catch ๊ตฌ๋ฌธ *
*3. ์˜ต์…”๋„ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ• *
*4. assert๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ•์ œ ํฌ๋ž˜์‰ฌ๋ฅผ ๋ฐœ์ƒ *

์—๋Ÿฌ ๋ฐœ์ƒ ํ•จ์ˆ˜ ์‚ฌ์šฉํ•˜๊ธฐ

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ํ•จ์ˆ˜๋ฅผ throwing function์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.
'->' ํ‚ค์›Œ๋“œ ์ „์— throws ํ‚ค์›Œ๋“œ๋ฅผ ์ ์Šต๋‹ˆ๋‹ค.

func canThrowErrors() throws -> String

func cannotThrowErrors() -> String

์˜ค์ง throwing function๋งŒ์ด ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ throwing function์ด ์•„๋‹Œ ํ•จ์ˆ˜์—์„œ throw๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ ๊ทธ ํ•จ์ˆ˜๋‚ด์—์„œ throw์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. throw ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด ํ•จ์ˆ˜๋ฅผ ํƒˆ์ถœํ•ฉ๋‹ˆ๋‹ค.(early exit)
thorwing function์„ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋Š” ๋ฐ˜๋“œ์‹œ do-catch, try?, try! ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•ด ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Do-Catch๋กœ ์—๋Ÿฌ ์ฒ˜๋ฆฌํ•˜๊ธฐ

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

buyFavoriteSnack()๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” throw function์ž…๋‹ˆ๋‹ค.
์—ฌ๊ธฐ์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, catch ๊ตฌ๋ฌธ์— ์ „๋‹ฌํ•ด ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ๋งŒ์•ฝ ๋ฐœ์ƒํ•œ ์ข…๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” catch ๊ตฌ๋ฌธ์ด ์—†๋‹ค๋ฉด ๋งˆ์ง€๋ง‰ catch ๊ตฌ๋ฌธ์—์„œ ๊ฑธ๋ฆฌ๊ฒŒ ๋  ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์˜ˆ์ œ๋กœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฒ˜๋ฆฌํ• ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

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."

์—๋Ÿฌ๋ฅผ ์˜ต์…”๋„ ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction()

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

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด x, y๋Š” nil ์ด ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ๋ฆฌํ„ด ๊ฐ’์„ ๊ฐ–์Šต๋‹ˆ๋‹ค. ์ฆ‰ x, y๋Š” ์˜ต์…”๋„ Int ์ž๋ฃŒํ˜•์„ ๊ฐ–์Šต๋‹ˆ๋‹ค.

์—๋Ÿฌ ๋ฐœ์ƒ์„ ์ค‘์ง€ํ•˜๊ธฐ

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๊ณ  ํ™•์‹ ํ•˜๋Š” ๊ฒฝ์šฐ try! ๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ˜น์€ runtime assertion์„ ์‚ฌ์šฉํ•ด ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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