[Swift 정면돌파] 16. 오류 처리

H43RO·2021년 8월 9일
0

Swift 정면돌파

목록 보기
16/19
post-thumbnail

오늘은 오류처리에 관해 알아보았다. Java, Kotlin 에서 흔히 사용되는 익숙한 개념이어서 쉽게 익힐 수 있었 던 것 같다.

오류처리 개념

  • 스위프트에서 오류(Error)는 Error라는 프로토콜을 준수하는 타입의 값을 통해 표현
  • Error 프로토콜은 사실 빈 프로토콜인데, 오류를 표현하기 위한 타입은 이 프로토콜을 채택함 (국룰)
  • 스위프트의 열거형은 오류의 종류를 나타내기에 아주 적합함 (같은 오류 종류 열거)

오류 표현

→ 예시 : 자판기 동작에 있어 발생할 수 있는 오류 종류를 표현

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

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

→ 오류 발생 여지가 있는 메소드는 throws 사용하여 잠재적 오류 표현

class VendingMachine {
    let itemPrice: Int = 100
    var itemCount: Int = 5
    var deposited: Int = 0
    
    // 돈 받기 메서드
    func receiveMoney(_ money: Int) throws {
        
        // 입력한 돈이 0이하면 쓰로잉
        guard money > 0 else {
            throw VendingMachineError.invalidInput
        }
        
        // 오류가 없으면 정상처리
        self.deposited += money
        print("\(money)원 받음")
    }
    
    // 물건 팔기 메서드
    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 구문을 활용하여 오류발생에 대비
  • 모든 오류 케이스에 대응
do {
    try machine.receiveMoney(0)
} catch VendingMachineError.invalidInput {
    print("입력이 잘못되었습니다")
} catch VendingMachineError.insufficientFunds(let moneyNeeded) {
    print("\(moneyNeeded)원이 부족합니다")
} catch VendingMachineError.outOfStock {
    print("수량이 부족합니다")
}  // 입력이 잘못되었습니다

// 하나의 catch 블록에서 switch 로 오류 분기도 가능
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원 받음

// 굳이 다 오류처리 할 필요 없으면 간략화
do {
    result = try machine.vend(numberOfItems: 4)
} catch {
    print(error)
}  // insufficientFunds(100)

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

try?try!

  1. **try?**
  • 오류가 발생했으면 결과값을 nil로 돌려받을 수 있음
  • 정상 동작 후에는 옵셔널 타입으로 정상 반환값을 돌려 받음 (바인딩 필요)
result = try? machine.vend(numberOfItems: 2)
result  // Optional("2개 제공함")

result = try? machine.vend(numberOfItems: 2)
result  // nil
  1. **try!**
  • 오류가 발생하지 않을 것이라는 강력한 확신을 가질 때 try!를 사용
  • 오류가 발생하면 런타임 오류가 발생 (애플리케이션 강종)
result = try! machine.vend(numberOfItems: 1)
result  // 1개 제공함

// result = try! machine.vend(numberOfItems: 1)
// 런타임 오류 발생
profile
어려울수록 기본에 미치고 열광하라

0개의 댓글