[Swift๐Ÿฆฉ] #17 ์—๋Ÿฌ ํ•ธ๋“ค๋ง โญ๏ธ

๋˜์ƒยท2022๋…„ 4์›” 4์ผ
0

iOS

๋ชฉ๋ก ๋ณด๊ธฐ
34/47
post-thumbnail
  • Error Handling : ํ”„๋กœ๊ทธ๋žจ์˜ ์—๋Ÿฌ ์กฐ๊ฑด์—์„œ ์—๋Ÿฌ์— ์‘๋‹ตํ•˜๊ณ  ๋ณต๊ตฌํ•˜๋Š” ํ”„๋กœ์„ธ์Šค.
  • ์ž‘์—…์ด ์‹คํŒจํ•˜๋Š” ๊ฒฝ์šฐ ๊ทธ์— ๋”ฐ๋ฅธ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค„ ์ˆ˜ ์žˆ๋„๋ก!
  • ex) ๋””์Šคํฌ ํŒŒ์ผ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๋Š”๋ฐ, ์ง€์ •๋œ ํŒŒ์ผ์ด ์—†๋‹ค๋ฉด...

1. ์—๋Ÿฌ ํ‘œํ˜„๊ณผ ๋˜์ง€๊ธฐ

  • Swift ์—์„œ Error ๋Š” Error Protocol ์„ ๋”ฐ๋ฅด๋Š” ํƒ€์ž…์˜ ๊ฐ’์ด๋‹ค.
  • ์ด ํ”„๋กœํ† ์ฝœ์„ ์ด์šฉํ•ด์„œ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • enum ์„ ์ด์šฉํ•ด์„œ ์—๋Ÿฌ์˜ ์ผ€์ด์Šค๋ฅผ ์ชผ๊ฐœ๊ณ , ๊ฐ ์—๋Ÿฌ๋ฅผ ์—๋Ÿฌ์— ๋งž๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•ธ๋“ค๋งํ•œ๋‹ค.
enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)



2. ์—๋Ÿฌ ํ•ธ๋“ค๋ง

  • ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ, ์ฃผ๋ณ€ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ฐฉ๋ฒ•์— ๋”ฐ๋ผ try try? try! ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉ.
  • Swift์—์„œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋Š” ๋‹ค๋ฅธ ์–ธ์–ด์™€ ๋‹ฌ๋ฆฌ ๊ณ„์‚ฐ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ํ˜ธ์ถœ ์Šคํƒ ํ•ด์ œ๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—,
  • Swift ์˜ throw ๊ตฌ๋ฌธ์˜ ์„ฑ๋Šฅ ํŠน์„ฑ์€ return ๊ตฌ๋ฌธ์˜ ์„ฑ๋Šฅ ํŠน์„ฑ๊ณผ ๋น„์Šทํ•˜๋‹ค.

1. throw - Error Propagation

  • throws ํ‚ค์›Œ๋“œ๋กœ ์—๋Ÿฌ๋ฅผ ์ „ํŒŒํ•˜์—ฌ ํ•จ์ˆ˜ ๋ฐ–์—์„œ ์ฒ˜๋ฆฌ.
  • ๋งŒ์•ฝ throws ํ•˜์ง€ ์•Š์œผ๋ฉด ํ•จ์ˆ˜ ์•ˆ์—์„œ ์ฒ˜๋ฆฌํ•ด์•ผํ•œ๋‹ค.
func canThrowErrors() throws -> String

func cannotThrowErrors() -> String
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)")
    }
}

์ด๋ ‡๊ฒŒ ์—๋Ÿฌ๋ฅผ ๋ฟœ์„ ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋Š” try ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์„œ ํ˜ธ์ถœํ•œ๋‹ค.

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

// ์‹คํŒจํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ๋ฌธ์„ init ์—์„œ ๋ถ€๋ฅด๋ฉด, init ์—ญ์‹œ throws Error.
struct PurchasedSnack {
    let name: String
    init(name: String, vendingMachine: VendingMachine) throws {
        try vendingMachine.vend(itemNamed: name)
        self.name = name
    }
}

2. do-catch

do {
    // ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ๋ฌธ
    try ํ•จ์ˆ˜
} catch pattern1 {
    // pattern1 ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ ํ–ˆ์„ ๋•Œ ์ฒ˜๋ฆฌ ๊ตฌ๋ฌธ
} catch pattern2 where condition {
    // ํŠน์ • condition์—์„œ pattern2 ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ ํ–ˆ์„ ๋•Œ ์ฒ˜๋ฆฌ ๊ตฌ๋ฌธ
} catch pattern3, pattern4, where condition {
    // ....
} catch {
    // ์œ„์—์„œ ์žกํžˆ์ง€ ์•Š์€ ๋ชจ๋“  ์—๋Ÿฌ ์žก๋Š”๋‹ค.
    // error ๋ผ๋Š” ์ง€์—ญ ์ƒ์ˆ˜๋กœ ์—๋Ÿฌ๋ฅผ ๋ฐ”์ธ๋”ฉ ํ•ด์ค€๋‹ค.
    print(error)
}
  • ๊ผญ ํ•˜๋‚˜์˜ do-catch ๊ตฌ๋ฌธ์—์„œ ์—๋Ÿฌ๋ฅผ ์žก์•„์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.
  • ์•„๋ž˜์ฒ˜๋Ÿผ ์‹ค์ œ ํ˜ธ์ถœ๋˜๋Š” ๊ณณ์—์„œ ์žก์•„์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
func nourish(with item: String) throws {
    do {
        try vendingMachine.vend(itemNamed: item)
    } catch is VendingMachineError {
        print("Couldn't buy that from the vending machine.")
    }
}

do {
    try nourish(with: "Beet-Flavored Chips")
} catch {
    print("Unexpected non-vending-machine-related error: \(error)")
}
// Prints "Couldn't buy that from the vending machine."

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

  • try? ์„ ์‚ฌ์šฉํ•ด์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  • ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋˜๋ฉด ํ‘œํ˜„์‹์˜ ๊ฐ’์ด nil ์ด ๋œ๋‹ค.
func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction()

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

// ์ด๋ ‡๊ฒŒ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ.
func fetchData() -> Data? {
    if let data = try? fetchDataFromDisk() { return data }
    if let data = try? fetchDataFromServer() { return data }
    return nil
}

4. ์—๋Ÿฌ ์ „ํŒŒ ๋น„ํ™œ์„ฑํ™”

  • ๋˜์ง€๋Š” ํ•จ์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹ค์ œ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š์„๊ฑฐ๋ผ๊ณ  ํ™•์‹ ์ด ๋“ค ๋•Œ
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
  • ํ•˜์ง€๋งŒ ๋ชจ์ข…์˜ ์ด์œ ๋กœ ํ•ด๋‹น ํŒŒ์ผ์ด ์—†๋‹ค..? ์•ฑ์ด ๋ป—๋Š”๋‹ค..



3. ์ •๋ฆฌ ์ž‘์—… defer

  • ํด๋ž˜์Šค๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ• ๋‹น๋˜๊ธฐ ์ง์ „์— ์‹คํ–‰๋˜๋Š” deinit ์ฒ˜๋Ÿผ
  • ํ˜„์žฌ ์‹คํ–‰์ค‘์ธ ์ฝ”๋“œ ๋ธ”๋Ÿญ์ด ์ข…๋ฃŒ๋˜๊ธฐ ์ง์ „์— ์‹คํ–‰๋˜๋Š” defer
  • defer ๊ตฌ๋ฌธ ์•ˆ์— deinit ์ฒ˜๋Ÿผ ํ˜„์žฌ ๊ตฌ๋ฌธ์ด ๋๋‚  ๋•Œ ์ •๋ฆฌํ•ด์•ผํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋„ฃ์–ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
    • break, return ์ฒ˜๋Ÿผ ๊ตฌ๋ฌธ ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€๊ฑฐ๋‚˜, throw ๊ฐ€ ํฌํ•จ๋˜์–ด ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์ฝ”๋“œ๋Š” ๋„ฃ์„ ์ˆ˜ ์—†๋‹ค.
  • ์—ฌ๋Ÿฌ๊ฐœ์˜ defer ๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ˆœ์„œ๋Š” ๋ฐ˜๋Œ€๋กœ ๋™์ž‘ํ•œ๋‹ค.
  • ์ค‘์ฒฉํ•ด์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ๊ฐ€์žฅ ๋ฐ”๊นฅ ๊ฒƒ์ด ๋จผ์ € ๋™์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค.
func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
            // Work with the file.
        }
        // close(file) is called here, at the end of the scope.
    }
}



์งˆ๋ฌธ

defer๋ž€ ๋ฌด์—‡์ธ์ง€ ์„ค๋ช…ํ•˜์‹œ์˜ค.

  • defer ๋ผ๋Š” ๊ฒƒ์€ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ ์‹คํ–‰๋˜๋Š” ๊ตฌ๋ฌธ์„ ๋งํ•œ๋‹ค.
  • deinit ์—์„œ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๋‚ด๋ ค๊ฐ€์•ผ ํ•˜๋Š” ๊ฒƒ์„ ์ •๋ฆฌํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ defer ์—์„œ๋„ ํ•จ์ˆ˜๊ฐ€ ๋๋‚  ๋•Œ ์ •๋ฆฌ๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๋“ค์„ ์ •๋ฆฌํ•œ๋‹ค.

defer๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ์ˆœ์„œ๋Š” ์–ด๋–ป๊ฒŒ ๋˜๊ณ , defer๊ฐ€ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๋ฅผ ์„ค๋ช…ํ•˜์‹œ์˜ค.

  • ํ•จ์ˆ˜ ๋‚ด๋ถ€ ์–ด๋””์— ์„ ์–ธํ•ด๋„ ๋งจ ๋งˆ์ง€๋ง‰์— ๋ถˆ๋ฆฌ๊ฒŒ ๋˜๊ณ ,
  • ๋งจ ์ฒ˜์Œ ์„ ์–ธํ•œ defer ๊ตฌ๋ฌธ์ด ํ•ญ์ƒ ๋งจ ๋งˆ์ง€๋ง‰์— ๋ถˆ๋ฆฌ๊ฒŒ ๋œ๋‹ค. (์—ญ์ˆœ)
  • ์ค‘์ฒฉํ•ด์„œ ์‹คํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด, ๊ฐ€์žฅ ๋ฐ”๊นฅ ๊ฒƒ์ด ๋จผ์ € ๋™์ž‘ํ•œ๋‹ค.
func deferTest() -> String {
    defer {
        print("first defer start")
        defer {
            print("second defer start")
            defer {
                print("third defer start")
                print("third defer end")
            }
            print("second defer end")
        }
        print("first defer end")
    }
    
    print("out of defer")
    
    return "str"
}


print(deferTest())

// ์‹คํ–‰ ๊ฒฐ๊ณผ
out of defer
first defer start
first defer end
second defer start
second defer end
third defer start
third defer end
str



์ถœ์ฒ˜
https://bbiguduk.gitbook.io/swift/language-guide-1/error-handling
https://babbab2.tistory.com/80

profile
0๋…„์ฐจ iOS ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€