[Swift 기초] 오류처리

2022. 4. 11. 16:15Swift 기초 정리

728x90

//공부 기록용 포스팅입니다. 틀린 부분이 있을 경우 댓글로 알려주시면 감사합니다! 😎

 

1. 오류 표현

enum 오류종류이름: Error {
	case 종류1
	case 종류2
	case 종류3
	//...
}
  • Error 프로토콜과 주로 열거형을 통해 오류를 표현한다.
  • Error 프로토콜
    • 내부적으로는 비어있는 프로토콜
    • Error를 구현한다는 의미가 중요한 프로토콜
    • Error 프로토콜을 열거형에 추가한 후 열거형 내에 오류들을 정의

1-1. 예시

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

1-2. 함수에서 발생한 오류 던지기

  • 특정한 함수 안에서 오류가 발생한 곳에(= 자신을 호출한 곳에) 오류를 던저준다 : throws 키워드 사용
  • 오류가 발생의 여지가 있는 메서드는 throws를 사용하여 오류를 내포하는 함수임을 표시
    • 함수 선언문의 () 뒤에, 반환타입 앞에 throws 키워드 삽입
    • throws가 없으면 일반 함수
    • 내부에서 오류를 던질 때는 throw 키워드 사용
class VendingMachine {
    let itemPrice: Int = 100
    var itemCount: Int = 5
    var deposited: Int = 0
    
    // 돈 받기 메서드
    //throws: 오류가 발생할 여지가 있음
    func receiveMoney(_ money: Int) throws {
        // 입력한 돈이 0이하면 오류를 던집니음
        //guard문을 사용하여 빠른 종료 유도
        guard money > 0 else {
            throw VendingMachineError.invalidInput
        }
        
        // 오류가 없으면(= guard문을 통과하게 되면) 정상처리를 합니다
        self.deposited += money
        print("\\(money)원 받음")
    }
    
    // 물건 팔기 메서드
    //throws: 오류가 발생할 수 있다를 표시, 리턴 타입을 뒤에 표기
    func vend(numberOfItems numberOfItemsToVend: Int) throws -> String {
        //여러가지 오류사항이 발생할 수 있다.
        // 원하는 아이템의 수량이 잘못 입력되었으면 오류를 던집니다
        guard numberOfItemsToVend > 0 else {
            //String을 리턴하지 않고 throws로 함수를 끝내버림: 함수를 끝내면서 오류를 던져버림
            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 -= numberOfItemsToVen
        
        return "\\(numberOfItemsToVend)개 제공함"
    }
}

// 자판기 인스턴스
let machine: VendingMachine = VendingMachine()
// 판매 결과를 전달받을 변수
var result: String?
  • guard문을 통해서 빠른 종료를 유도할 수 있다. guard문을 통과해서 오류가 없다면 정상 동작 처리
  • 하나의 메서드에 여러 오류가 발생할 여지가 있다 → guard문으로 각각 오류 표현
func vend(numberOfItems numberOfItemsToVend: Int) throws -> String {
  • throws 뒤에 리턴타입을 표기
    • 오류가 발생하면 리턴타입을 리턴하지 않고 throws로 함수를 끝낸다: 함수를 끝내면서 throws를 던짐

 

2. 오류처리

  • 오류를 던질 수도 있지만 오류가 던져지는 것에 대비하여 던져진 오류를 처리하기 위한 코드도 작성해야 한다.
    • 던져진 오류가 무엇인지 판단해서 다시 문제를 해결한다
    • 다른 방법으로 시도한다
    • 사용자에게 오류를 알리고 사용자에게 선택 권한을 넘겨주어 다음에 어떤 동작을 하게 할 것인지 결정하도록 유도
  • 오류 발생의 여지가 있는 throws 함수(메서드)는 try를 사용하여 호출해야 한다.
  • try, try?, try!
  • Java의 try-catch 구문을 사용해서 런타임시의 오류를 처리

2-1. do-catch

do{
  try expression
  statments
} catch pattern1 {
  statments
} catch pattern2 where condition {
  statements
} catch {
  statements
}
  • 오류발생의 여지가 있는 메서드는 try를 사용해야 하는데, 이 때 do-catch 구문을 함께 사용
  • try는 오류처리 함수 호출, catch는 이를 처리
  • 와일드 카드 catch문을 사용하면 이전의 catch에서 잡지 못한 error를 처리
do {
    try machine.receiveMoney(0)
} catch VendingMachineError.invalidInput {
    print("입력이 잘못되었습니다")
} catch VendingMachineError.insufficientFunds(let moneyNeeded) {
    print("\\(moneyNeeded)원이 부족합니다")
} catch VendingMachineError.outOfStock {
    print("수량이 부족합니다")
} catch {
	print("와일드 카드 catch문: 이전에 잡지 못한 에러들을 처리")
// 입력이 잘못되었습니다

2-1-1. do-catch + switch

  • 표현만 다를 뿐 동일하게 동작
  • swich에서 사용하는 error는 catch구문에서 암시적으로 인식해서 전달
//catch를 많이 쓰기 싫다면 하나의 catch 블럭에서 swift구문으로 오류 분류
//던져진 error를 받아서 처리
//error 변수명은 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원 받음

2-1-2. catch 구문 내부 간략화

  • 케이스별로 오류처리를 할 필요가 없을 때
do {
    result = try machine.vend(numberOfItems: 4)
} catch {
    print(error)
} // insufficientFunds(100)

2-1-3. 오류처리필요가 없을 때

  • do 구문만 사용
do {
    result = try machine.vend(numberOfItems: 4)
}

2-2. try

  • 별도의 오류처리 결과를 통보받지 않고
    • 오류가 발생했으면 결과값을 nil로 돌려 받음
    • 정상동작 후에는 옵셔널 타입으로 정상 반환값을 돌려 받음
  • do-catch 사용하지 않고 try?만 사용
//정상동작
result = try? machine.vend(numberOfItems: 2)
result  //Optional("2개 제공함")

//오류발생
result = try? machine.vend(numberOfItems: -3)
result  //nil

2-3. try!

  • 오류가 발생하지 않을 것이라는 강력한 확신을 가질 때 사용
    • 정상동작 후에 바로 옵셔널이 아닌 일반 타입의 결과값을 돌려 받음
    • 오류가 발생하면 런타임 오류가 발생하여 애플리케이션 동작 중지
//정상동작
result = try! machine.vend(numberOfItems: 1)
result  //2개 제공함

//오류발생
//VendingMachineError.invalidInput
result = try! machine.vend(numberOfItems: -1)

 

 

참고하였습니다. 감사합니다.

https://yagom.net/courses/swift-basic/lessons/오류처리/

https://www.inflearn.com/course/창원대학교-박동규-교수의-swift-기초-강좌/lecture/1905?tab=curriculum 

 

728x90

'Swift 기초 정리' 카테고리의 다른 글

[Swift 기초] 접근제어  (0) 2022.04.13
[Swift 코테 기초] 고차함수  (0) 2022.04.12
[Swift 기초] 익스텐션  (0) 2022.04.05
[Swift 기초] 프로토콜  (0) 2022.04.05
[Swift 기초] assert와 guard  (0) 2022.04.05