본문 바로가기
Swift/기본

오류처리

by 밤새는탐험가 2024. 4. 13.

스위프트에서 오류(Error)는 Error라는 프로토콜을 준수하는 타입의 값을 통해 표현된다. 

 

⭐️ Error 프로토콜은 사실상 요구사항이 없는 빈 프로토콜이다. 

⭐️ 오류를 표현하기 위한 타입 (주로 열거형)은 이 프로토콜을 채택한다. 

 

오류표현

Error 프로토콜과 열거형을 통해 오류를 표현한다. 

enum 오류종류이름: Error {
    case 종류1
    case 종류2
    case 종류3
    //...
}

 

 

⭐ HTTP 상태 코드를 참고하여 만든 예시 오류  

enum HttpError: Error {
    case badRequest
    case unauthorized
    case forbidden
    case notFound
    case requestTimeOut
    case conflict
}

 

 

함수에서 발생한 오류 던지기

자판기 동작 도중 발생한 오류를 던지는 메서드 구현한다. 

오류 발생의 여지가 있는 메서드는 throws 를 사용하여 오류를 내표하는 함수임을 표현한다. 

 

 

자판기 동작 오류의 종류 표현

enum VendingMachineError: Error {
    case invalidInput
    case insufficientFunds(moneyNeeded: Int)
    case outOfStock
}

 

 

자판기 동작 class 구현

먼저 자판기 동작에 필요한 저장 프로퍼티를 생성한다. 

  • 제품 가격 = itemPrice
  • 제품 수량 = itemCount
  • 투입금 = deposited
class VendingMachine {
    let itemPrice: Int = 100
    var itemCount: Int = 5
    var deposited: Int = 0
}

 

 

 

자판기에서 돈을 받는 메서드를 생성한다.

✅ 입력한 돈이 0이하면 오류를 던지는 구문을 guard ~ else 문으로 구현한다. 

    func receiveMoney(_ money: Int) throws {
        
        // 입력한 돈이 0이하면 오류를 던집니다
        guard money > 0 else {
            throw VendingMachineError.invalidInput
        }
        
        // 오류가 없으면 정상처리를 합니다
        self.deposited += money
        print("\(money)원 받음")
    }

 

 

자판기에서 제품을 판매하는 메서드를 생성한다. 

✅ 구매하려는 제품의 수량을 잘못 입력할 경우 오류를 던진다. 

guard numberOfItemsToVend > 0 else {
    throw VendingMachineError.invalidInput
}

 

✅ 구해하려는 수량보다 투입한 돈이 적으면 오류를 던진다.

guard numberOfItemsToVend * itemPrice <= deposited else {
    let moneyNeeded: Int
    moneyNeeded = numberOfItemsToVend * itemPrice - deposited

    throw VendingMachineError.insufficientFunds(moneyNeeded: moneyNeeded)
}

 

✅ 구매하려는 수량이 구매 가능한 수량보다 많으면 오류를 던진다.

guard itemCount >= numberOfItemsToVend else {
    throw VendingMachineError.outOfStock
}

 

오류가 없으면 정상 처리한다. 

let totalPrice = numberOfItemsToVend * itemPrice

self.deposited -= totalPrice
self.itemCount -= numberOfItemsToVend

return "\(numberOfItemsToVend)개 제공함"

 

판매 메서드 전체 코드 

func vend(numberOfItems numberOfItemsToVend: Int) throws -> String {

    guard numberOfItemsToVend > 0 else {
        throw VendingMachineError.invalidInput
    }

    guard numberOfItemsToVend * itemPrice <= deposited else {
        let moneyNeeded: Int
        moneyNeeded = numberOfItemsToVend * itemPrice - deposited

        throw VendingMachineError.insufficientFunds(moneyNeeded: moneyNeeded)
    }

    guard itemCount >= numberOfItemsToVend else {
        throw VendingMachineError.outOfStock
    }

    let totalPrice = numberOfItemsToVend * itemPrice

    self.deposited -= totalPrice
    self.itemCount -= numberOfItemsToVend

    return "\(numberOfItemsToVend)개 제공함"
}

 

 

자판기 인스턴스 및 판매 결과를 전달받을 변수 선언한다.

// 자판기 인스턴스
let machine: VendingMachine = VendingMachine()

// 판매 결과를 전달받을 변수
var result: String?

 

 

오류처리 

오류를 던질 수 있지만, 던져지는 오류를 어떤 식으로 처리할 지에 대한 코드를 작성해야 한다. 

 

오류발생의 여지가 있는 throws 함수는 try 를 사용해서 호출한다. 

 

do - catch 구문 

오류 발생의 여지가 있는 throws 함수(메서드)는 do - catch 구문을 활용해서 오류 발생에 대비한다. 

 

✅ 정석

- catch 구문 안에 오류를 처리한다. 

do {

    try machine.receiveMoney(0)
    
} catch VendingMachineError.invalidInput {

    print("입력이 잘못되었습니다")
    
} catch VendingMachineError.insufficientFunds(let moneyNeeded) {

    print("\(moneyNeeded)원이 부족합니다")
    
} catch VendingMachineError.outOfStock {

    print("수량이 부족합니다")
    
} // 입력이 잘못되었습니다

 

 

✅ switch 구문을 이용하여 1개의 catch 블럭에서 오류를 분류할 수 있다. 

do {
    try machine.receiveMoney(300)
} catch /*(let error)*/ {
    
    switch error {
    case VendingMachineError.invalidInput:
        print("입력이 잘못되었습니다")
    case VendingMachineError.insufficientFunds(let moneyNeeded):
        print("\(moneyNeeded)원이 부족합니다")
    case VendingMachineError.outOfStock:
        print("수량이 부족합니다")
    default:
        print("알수없는 오류 \(error)")
    }
} // 300원 받음

 

 

✅ 케이스별로 오류 처리 할 필요가 없으면 catch 구문 내부를 간략화도 가능하다. 

do {
    result = try machine.vend(numberOfItems: 4)
} catch {
    print(error)
} // insufficientFunds(100)

 

 

✅ 또는 do 구문만 써도 무방한다. 

do {
    result = try machine.vend(numberOfItems: 4)
}

 

 

 

try? 와 try!

 

try?

별도의 오류처리 결과를 통보받지 않고, 오류가 발생했으면 결과를 nil 로 받을 수 있다.

정상 동작 경우에는 옵셔널 타입으로 결과를 받는다. 

result = try? machine.vend(numberOfItems: 2)
result // Optional("2개 제공함")

 

 

try!

오류가 발생하지 않을 것이라는 확신이 들 때 try!를 사용한다.

정상 동작인 경우 결과값을 옵셔널 타입이 아닌 상태로 받지만, 오류가 발생함면 런타임 오류가 발생하며 앱의 동작이 멈춘다.

result = try! machine.vend(numberOfItems: 1)
런타임 오류 발생!

 

 

'Swift > 기본' 카테고리의 다른 글

제네릭  (0) 2024.04.15
고차함수  (0) 2024.04.15
익스텐션  (0) 2024.04.12
프로토콜  (0) 2024.04.11
assert와 guard  (0) 2024.04.09