์๋ ํ์ธ์. ์ค๋์ Swift '์๋ฌ ์ฒ๋ฆฌ(Error Handling)'์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. ์ค์ ์ฑ ๊ฐ๋ฐ ์ ๋ฐ์ํ๋ ๋ค์ํ ์์ธ ์ํฉ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ ์๋์ง, Swift์ ์๋ฌ ์ฒ๋ฆฌ ๋ฉ์ปค๋์ฆ์ ํตํด ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๐ ์๋ฌ ์ฒ๋ฆฌ๋? (Introduction to Error Handling)
์๋ฌ ์ฒ๋ฆฌ๋ ํ๋ก๊ทธ๋จ์ด ์คํ ์ค์ ๋ฐ์ํ๋ ์์์น ๋ชปํ ์ํฉ์ ๋์ํ๊ณ ๋ณต๊ตฌํ๋ ํ๋ก์ธ์ค์ ๋๋ค. Swift๋ ๋ฐํ์์ ๋ฐ์ ๊ฐ๋ฅํ ์๋ฌ๋ฅผ ๋์ง๊ณ (throw), ํฌ์ฐฉํ๊ณ (catch), ์ ํ(propagate)ํ๋ ๊ฐ๋ ฅํ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค.
์ต์ ๋์ด ๊ฐ์ ๋ถ์ฌ๋ฅผ ํํํ๋ ๋ฐฉ๋ฒ์ด๋ผ๋ฉด, ์๋ฌ ์ฒ๋ฆฌ๋ '์' ์์ ์ด ์คํจํ๋์ง์ ๋ํ ์ ๋ณด๊น์ง ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ฝ๋๋ ์ํฉ์ ๋ง๊ฒ ๋ ์ ์ ํ๊ฒ ๋์ํ ์ ์์ต๋๋ค.
์ค์ ์ฌ๋ก: ํ์ผ์ ์ฝ๋ ์์ ์์ 'ํ์ผ์ด ์กด์ฌํ์ง ์์', '๊ถํ ๋ถ์กฑ', 'ํฌ๋งท ๋ถ์ผ์น' ๋ฑ ๋ค์ํ ์ด์ ๋ก ์คํจํ ์ ์์ต๋๋ค. ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ํตํด ์ด๋ฌํ ์ํฉ์ ๊ตฌ๋ถํ์ฌ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
๐ ์๋ฌ ํํ๊ณผ ๋์ง๊ธฐ (Representing and Throwing Errors)
์๋ฌ ์ ์ํ๊ธฐ
Swift์์ ์๋ฌ๋ Error ํ๋กํ ์ฝ์ ์ค์ํ๋ ํ์ ์ผ๋ก ํํ๋ฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ด๊ฑฐํ(enum)์ ์ฌ์ฉํ์ฌ ๊ด๋ จ๋ ์๋ฌ ์กฐ๊ฑด๋ค์ ๊ทธ๋ฃนํํฉ๋๋ค:
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
ํฌ์ธํธ: ์ด๊ฑฐํ์ ์๋ฌ ์กฐ๊ฑด์ ๋ชจ๋ธ๋งํ๋๋ฐ ์ด์์ ์ด๋ฉฐ, ๊ด๋ จ ๊ฐ(associated values)์ ํตํด ์ถ๊ฐ ์ ๋ณด๋ฅผ ์ ๋ฌํ ์ ์์ต๋๋ค.
์๋ฌ ๋์ง๊ธฐ
์๋ฌ๊ฐ ๋ฐ์ํ์ ๋๋ throw ํค์๋๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฌ๋ฅผ '๋์ง๋๋ค':
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
์ด ์ฝ๋๋ "์ํ๊ธฐ์ 5์ฝ์ธ์ด ๋ ํ์ํฉ๋๋ค"๋ผ๋ ์๋ฌ๋ฅผ ๋ฐ์์ํต๋๋ค.
๐ ์๋ฌ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ (Handling Errors)
Swift์์๋ ์๋ฌ๋ฅผ ์ฒ๋ฆฌํ๋ 4๊ฐ์ง ์ฃผ์ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค:
1. ์๋ฌ ์ ํํ๊ธฐ (Propagating Errors)
ํจ์๊ฐ ๋ด๋ถ์ ์ผ๋ก ์๋ฌ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค๋ฉด, ํจ์ ์ ์ธ์ throws ํค์๋๋ฅผ ์ถ๊ฐํ์ฌ ์ด๋ฅผ ํธ์ถ์์๊ฒ ์๋ฆฝ๋๋ค:
func canThrowErrors() throws -> String
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)
}
// ํ๋งค ๊ณผ์ ์ํ...
}
throws ํจ์: ์๋ฌ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ ํจ์๋ throws ํค์๋๋ก ํ์ํ๋ฉฐ, ์ด๋ฌํ ํจ์๋ฅผ ํธ์ถํ ๋๋ try ํค์๋๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
์๋ฌ๋ฅผ ๋์ง ์ ์๋ ํจ์๋ฅผ ํธ์ถํ ๋๋ ๋ฐ๋์ try ํค์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค:
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName)
}
2. do-catch๋ก ์๋ฌ ์ฒ๋ฆฌํ๊ธฐ
do-catch ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋ฉด ์๋ฌ๋ฅผ ํฌ์ฐฉํ๊ณ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค:
do {
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
print("๊ตฌ๋งค ์ฑ๊ณต! ๋ง์๊ฒ ๋์ธ์.")
} catch VendingMachineError.invalidSelection {
print("์๋ชป๋ ์ ํ์
๋๋ค.")
} catch VendingMachineError.outOfStock {
print("ํ์ ์
๋๋ค.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("์์ก์ด ๋ถ์กฑํฉ๋๋ค. \(coinsNeeded)์ฝ์ธ์ ๋ ๋ฃ์ด์ฃผ์ธ์.")
} catch {
print("์์์น ๋ชปํ ์๋ฌ: \(error).")
}
์บ์น ํจํด: catch ๋ค์ ํน์ ์๋ฌ ํจํด์ ์ง์ ํ๊ฑฐ๋, ํจํด ์์ด ๋ชจ๋ ์๋ฌ๋ฅผ ํฌ์ฐฉํ ์ ์์ต๋๋ค.
์ฌ๋ฌ ์๋ฌ๋ฅผ ํ๊บผ๋ฒ์ ์ฒ๋ฆฌํ๊ณ ์ถ๋ค๋ฉด ์ฝค๋ง๋ก ๊ตฌ๋ถํ์ฌ ์ง์ ํ ์๋ ์์ต๋๋ค:
catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
print("์ ํ์ด ์๋ชป๋์๊ฑฐ๋, ํ์ ์ด๊ฑฐ๋, ์์ก์ด ๋ถ์กฑํฉ๋๋ค.")
}
3. ์ต์ ๋ ๊ฐ์ผ๋ก ์๋ฌ ๋ณํํ๊ธฐ
try?๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ฌ๋ฅผ ์ต์ ๋ ๊ฐ์ผ๋ก ๋ณํํ ์ ์์ต๋๋ค. ํจ์๊ฐ ์๋ฌ๋ฅผ ๋์ง๋ฉด ๊ฒฐ๊ณผ๋ nil์ด ๋ฉ๋๋ค:
// ์๋ฌ ๋ฐ์ ์ x๋ nil์ด ๋ฉ๋๋ค
let x = try? someThrowingFunction()
// ์ ์ฝ๋๋ ๋ค์๊ณผ ๋์ผํฉ๋๋ค
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
์ค์ฉ์ ์ฌ์ฉ๋ฒ: try?๋ ์ฌ๋ฌ ์ ๊ทผ ๋ฐฉ์์ ์๋ํ๋ฉด์ ์ฒซ ๋ฒ์งธ ์ฑ๊ณตํ ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ฉํ๊ณ ์ถ์ ๋ ์ ์ฉํฉ๋๋ค:
func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}
4. ์๋ฌ ์ ํ ๋นํ์ฑํํ๊ธฐ
ํจ์๊ฐ ์ค์ ๋ก ์๋ฌ๋ฅผ ๋์ง์ง ์์ ๊ฒ์ด๋ผ๊ณ ํ์ ํ๋ค๋ฉด try!๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฌ ์ ํ๋ฅผ ๋นํ์ฑํํ ์ ์์ต๋๋ค:
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
์ฃผ์: try!๋ ๋ง์ฝ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ๋ฐํ์ ํฌ๋์๋ฅผ ๋ฐ์์ํต๋๋ค. ์ ๋์ ์ผ๋ก ํ์ ํ ์ ์๋ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ์ธ์.
๐ ์๋ฌ ํ์ ์ง์ (Specifying Error Types)
Swift 5.7๋ถํฐ๋ ํจ์๊ฐ ๋์ง ์ ์๋ ์๋ฌ์ ํ์ ์ ๋ช ์์ ์ผ๋ก ์ง์ ํ ์ ์์ต๋๋ค:
enum StatisticsError: Error {
case noRatings
case invalidRating(Int)
}
func summarize(_ ratings: [Int]) throws(StatisticsError) {
guard !ratings.isEmpty else { throw .noRatings }
var counts = [1: 0, 2: 0, 3: 0]
for rating in ratings {
guard rating > 0 && rating <= 3 else { throw .invalidRating(rating) }
counts[rating]! += 1
}
print("*", counts[1]!, "-- **", counts[2]!, "-- ***", counts[3]!)
}
ํ์ ์ด ์ง์ ๋ ๋์ง๊ธฐ(Typed Throws): ํจ์๊ฐ ๋์ง ์ ์๋ ์๋ฌ ํ์ ์ throws(ํ์ ์ด๋ฆ)์ผ๋ก ๋ช ์ํ๋ฉด ์ปดํ์ผ๋ฌ๊ฐ ํ์ ์ฒดํฌ๋ฅผ ์ํํ ์ ์์ต๋๋ค.
ํ์ ์ด ์ง์ ๋ ์๋ฌ๋ฅผ ํฌ์ฐฉํ ๋๋ ํด๋น ์๋ฌ ํ์ ์ ๋ง๋ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํฉ๋๋ค:
do throws(StatisticsError) {
try summarize(ratings)
} catch {
switch error {
case .noRatings:
print("ํ์ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค")
case .invalidRating(let rating):
print("์ ํจํ์ง ์์ ํ์ : \(rating)")
}
}
์ด์ : ์๋ฌ ํ์ ์ ๋ช ์ํ๋ฉด ์ฝ๋์ ์์ ์ฑ์ด ๋์์ง๊ณ , ๋ชจ๋ ์๋ฌ ์ผ์ด์ค๋ฅผ ์ฒ๋ฆฌํ๋์ง ์ปดํ์ผ๋ฌ๊ฐ ํ์ธํ ์ ์์ต๋๋ค.
๐ ์ ๋ฆฌ ์์ ์ง์ (Cleanup Actions with defer)
defer ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋ฉด ํ์ฌ ์ฝ๋ ๋ธ๋ก์ด ์ข ๋ฃ๋ ๋ ์คํ๋ ์ฝ๋๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค. ์ด๋ ํจ์๊ฐ ์ ์์ ์ผ๋ก ์ข ๋ฃ๋๋ , ์๋ฌ๊ฐ ๋ฐ์ํ๋ ์๊ด์์ด ์คํ๋ฉ๋๋ค:
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file) // ํจ์ ์ข
๋ฃ ์ ํญ์ ํ์ผ์ ๋ซ์ต๋๋ค
}
while let line = try file.readline() {
// ํ์ผ ์ฒ๋ฆฌ ์ฝ๋...
}
}
}
defer์ ์คํ ์์: ์ฌ๋ฌ defer ๋ธ๋ก์ด ์๋ค๋ฉด ๋ง์ง๋ง์ ์ ์๋ ๊ฒ๋ถํฐ ์ญ์์ผ๋ก ์คํ๋ฉ๋๋ค.
defer๋ ๋ค์๊ณผ ๊ฐ์ ์ํฉ์์ ์ ์ฉํฉ๋๋ค:
- ํ์ผ ํธ๋ค, ๋คํธ์ํฌ ์ฐ๊ฒฐ ๋ฑ์ ๋ฆฌ์์ค ํด์
- ์ ๊ธ(lock) ํด์
- ์์ ์ํ ๋ณต์
๐ ์ค์ ์์ : ์ํ๊ธฐ ์๋ฎฌ๋ ์ด์
๋ค์์ ์ง๊ธ๊น์ง ๋ฐฐ์ด ์๋ฌ ์ฒ๋ฆฌ ๊ธฐ๋ฒ์ ์ข ํฉ์ ์ผ๋ก ํ์ฉํ ์ํ๊ธฐ ์์ ์ ๋๋ค:
// ์ํ ๊ตฌ์กฐ์ฒด
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("\(name) ์ํ์ด ๋์์ต๋๋ค")
}
}
// ์ฌ์ฉ ์์
let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels",
]
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
print("๊ตฌ๋งค ์ฑ๊ณต!")
} catch VendingMachineError.invalidSelection {
print("์ ํํ ์ํ์ด ์์ต๋๋ค.")
} catch VendingMachineError.outOfStock {
print("ํ์ ์
๋๋ค.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("์์ก์ด ๋ถ์กฑํฉ๋๋ค. \(coinsNeeded)์ฝ์ธ์ ๋ ๋ฃ์ด์ฃผ์ธ์.")
} catch {
print("์์์น ๋ชปํ ์๋ฌ: \(error)")
}
๐ ์ ๋ฆฌ: Swift์ ์๋ฌ ์ฒ๋ฆฌ ์ฒ ํ
Swift์ ์๋ฌ ์ฒ๋ฆฌ ์์คํ ์ ๋ค์๊ณผ ๊ฐ์ ์ฒ ํ์ ๋ฐํ์ผ๋ก ํฉ๋๋ค:
- ๋ช ์์ฑ: ์๋ฌ๋ฅผ ๋์ง ์ ์๋ ํจ์๋ throws๋ก ๋ช ํํ ํ์๋ฉ๋๋ค.
- ํ์ ์์ ์ฑ: ์๋ฌ๋ ํ์ ์์คํ ์ ์ผ๋ถ๋ก, ์ปดํ์ผ ํ์์ ๋ง์ ๋ฌธ์ ๋ฅผ ์ก์๋ผ ์ ์์ต๋๋ค.
- ๋น์ฉ ํจ์จ์ฑ: Swift์ ์๋ฌ ์ฒ๋ฆฌ๋ ์์ธ ์ฒ๋ฆฌ์ ๋ฌ๋ฆฌ ์คํ ๋๊ฐ๊ธฐ(stack unwinding)๊ฐ ์์ด ์ฑ๋ฅ์ ์ด์ ์ด ์์ต๋๋ค.
- ํตํฉ์ฑ: ์ต์ ๋, ํ์ ์์คํ ๋ฑ Swift์ ๋ค๋ฅธ ๊ธฐ๋ฅ๊ณผ ์์ฐ์ค๋ฝ๊ฒ ํตํฉ๋ฉ๋๋ค.
์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ ํ์ฉํ๋ฉด ๋ ์์ ์ ์ด๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ผ๋ฉฐ, ์์์น ๋ชปํ ์ํฉ์์๋ ๋์ํ ์ ์์ต๋๋ค.
'๐ฅ Bread Basics > Swift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Swift ๊ณต์ ๋ฌธ์ ์ ๋ฆฌ - ํ์ ์บ์คํ (Type Casting) (0) | 2025.04.12 |
---|---|
Obj-C ๊ณต์ ๋ฌธ์ ์ ๋ฆฌ - ๊ฐ์ฒด, ํด๋์ค, ๋ฉ์์ง (0) | 2025.04.11 |
Swift ๊ณต์ ๋ฌธ์ ์ ๋ฆฌ - ๋งคํฌ๋ก (Macros) (0) | 2025.04.11 |
Swift ๊ณต์ ๋ฌธ์ ์ ๋ฆฌ - ๋์์ฑ (Concurrency) (0) | 2025.04.11 |
Swift ๊ณต์ ๋ฌธ์ ์ ๋ฆฌ - ์ต์ ๋ ์ฒด์ด๋ (Optional Chaining) (0) | 2025.04.11 |
Swift ๊ณต์ ๋ฌธ์ ์ ๋ฆฌ - ์ด๊ธฐํ ํด์ (Deinitialization) (1) | 2025.04.11 |
Swift ๊ณต์ ๋ฌธ์ ์ ๋ฆฌ - ์ด๊ธฐํ (Initialization) (0) | 2025.04.11 |
Swift ๊ณต์ ๋ฌธ์ ์ ๋ฆฌ - ์์ (Inheritance) (0) | 2025.04.11 |