본문 바로가기
iOS/Swift

Swift-10. 오류 처리 - throw, rethrows, defer

by 헤콩 2023. 2. 24.
반응형

** edwith - Swift 프로그래밍 입문 강의 참고 **

 

 

# 오류 발생 #

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

- Error 프로토콜은 사실 상 요구사항이 없는 빈 프로토콜일 뿐이지만, 오류를 표현하기 위한 타입(주로 열거형)은 이 프로토콜을 채택합니다.

 

[ 오류 표현 ]

- Error 프로토콜과 (주로) 열거형을 통해서 오류를 표현

/* 자판기 동작 오류의 종류를 표현한 VendingMachineError 열거형 */
enum VendingMachineError: Error {
    case invalidInput
    case insufficientFunds(moneyNeeded: Int)
    case outOfStock
}

 

[ 함수에서 발생한 오류 던지기(throw) ]

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 {
        // 원하는 아이템의 수량이 0 이하로 잘못 입력되었으면 오류 발생 //
        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 VendingMahineError.invalidInput {
    print("입력이 잘못되었습니다")
} catch VendingMahineError.insufficientFunds(let moneyNeeded) {
    print("\(moneyNeeded)원이 부족합니다")
} catch VendingMahineError.outOfStock {
    print("수량이 부족합니다")
}

/* 출력
"입력이 잘못되었습니다"
*/

 

[ do - catch 에서 switch 사용 ]

- 위와 다를 바 없음

do {
    try machine.receiveMoney(300)
} catch {
    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 {
    result = try machine.vend(numberOfItems: 4)
} // catch 생략

 

[ try? ]

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

- 정상동작 후, 옵셔널 타입으로 정상 반환값을 돌려 받아요.

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


result = try? machine.vend(numberOfItems: 2)
result // nil

 

[ try! ]

- 오류가 발생하지 않을 것이라는 강력한 확신을 전달해주어, 정상동작 후에 바로 결과값을 돌려받아요.

- 오류가 발생하면 런타임 오류 -> 애플리케이션 동작 중지

result = try! machine.vend(numberOfItems: 1)
result // 1개 제공함

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

 

 


# 그 외 오류 처리 (rethrows, defer) #

[ rethrows ]

- 최소 하나 이상의 오류 발생 가능한 함수를 매개변수로 전달 받아야 사용 가능

- 부모클래스의 rethrows메서드를 자식클래스의 throws메서드로 오버라이드 불가능

- 부모클래스의 throws메서드를 자식클래스의 rethrows메서드로 오버라이드 가능

- 프로토콜의 throw요구 함수를 rethrows를 사용하여 부합시키지 못합니다.

- 프로토콜의 rethrows요구 함수를 throw를 사용하여 부합시킬 수 있습니다.

func authenticateuser(method: () throws -> Bool) rethrows {
    try method()
    print("Success!")
}

 

[ defer ]

- 현재 코드 블록을 나가기 전에 꼭 실행되어야 하는 코드가 실행되도록 보장해줍니다.

- 함수, 메서드, 반복문, 조건문 등등 코드 블록 어디에서든 사용 가능

- defer 구문 내부에서는 break, return 등과 같은 빠져나가는 코드 작성 X

- 여러 개의 defer 구문이 하나의 블록 내부에 속해 있다면, 맨 마지막에 작성된 구문부터 역순으로 수행됩니다.

func printStringNumbers() {
    defer { print("1") }
    defer { print("2") }
    defer { print("3") }
    print("4")
}
/* 출력
4321
*/

 

반응형

댓글