Hi yoahn 개발블로그

[Swift] #8 오류처리 및 고차함수 본문

프로그래밍 언어/swift

[Swift] #8 오류처리 및 고차함수

hi._.0seon 2021. 1. 22. 13:55
반응형

1. 오류처리

  • 스위프트에서 오류(Error)는 Error라는 프로토콜을 준수하는 타입의 값을 통해 표현됩니다. 
  • Error 프로토콜은 사실상 요구사항이 없는 빈 프로토콜일 뿐이지만, 오류를 표현하기 위한 타입(주로 열거형)은 이 프로토콜을 채택합니다.
  • 스위프트의 열거형은 오류의 종류를 나타내기에 아주 적합한 기능입니다. 
  • 연관 값을 통해 오류에 관한 부가 정보를 제공할 수도 있습니다.

1.1 오류 표현

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

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

연관값

각 하나의 타입에 대해 인스턴스마다 다른 값을 가지게 할 때 사용

hyesunzzang.tistory.com/126

 

[Swift] Enumeration (열거형) 살펴보기

Swift - Enumeration (열거형) An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code. Swift의 열거형은 C나 C+..

hyesunzzang.tistory.com

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

- 오류 발생의 여지가 있는 함수는 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?

1.3 오류 처리

  • 오류를 던질 수도 있지만 오류가 던져지는 것에 대비하여 던져진 오류를 처리하기 위한 코드도 작성해야 합니다. 예를 들어 던져진 오류가 무엇인지 판단하여 다시 문제를 해결한다든지, 다른 방법으로 시도해 본다든지, 사용자에게 오류를 알리고 사용자에게 선택 권한을 넘겨주어 다음에 어떤 동작을 하게 할 것인지 결정하도록 유도하는 등의 코드를 작성해야 합니다.
  • 오류발생의 여지가 있는 throws 함수(메서드)는 try를 사용하여 호출해야합니다. try와 do-catch, try?와 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("수량이 부족합니다")
} // 입력이 잘못되었습니다

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원 받음

/* 1. 케이스별로 오류처리 할 필요 없는 경우 */
do {
    result = try machine.vend(numberOfItems: 4)
} catch {
    print(error)
} // insufficientFunds(100)

/* 2. do 만 써도 무방 */
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

2. try!

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

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

*더 알아보기

- rethrows

- defer

2. 고차 함수

  • 고차 함수(Higher-order function)는 '다른 함수를 전달인자로 받거나 함수실행의 결과를 함수로 반환하는 함수'를 뜻합니다
  • 스위프트의 함수(클로저)는 일급시민(일급객체)이기 때문에 함수의 전달인자로 전달할 수 있으며, 함수의 결과값으로 반환할 수 있습니다
  • 이번 파트에서는 스위프트 표준라이브러리에서 제공하는 유용한 고차함수[map, filter, reduce]에 대해 알아봅니다
  • map, filter, reduce 함수는 스위프트 표준 라이브러리의 컨테이너 타입(컬렉션 타입: Array, Set, Dictionary 등)에 구현되어 있습니다

2.1 map

  • map함수는 컨테이너 내부의 기존 데이터를 변형(transform)하여 새로운 컨테이너를 생성합니다.
// 변형하고자 하는 numbers와 변형 결과를 받을 doubledNumbers, strings

let numbers: [Int] = [0, 1, 2, 3, 4]
var doubledNumbers: [Int]
var strings: [String]

// numbers의 각 요소를 2배하여 새로운 배열 반환
doubledNumbers = numbers.map({ (number: Int) -> Int in
    return number * 2
})

// numbers의 각 요소를 문자열로 변환하여 새로운 배열 반환
strings = numbers.map({ (number: Int) -> String in
    return "\(number)"
})

print(doubledNumbers) // [0, 2, 4, 6, 8]
print(strings) // ["0", "1", "2", "3", "4"]

// 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저
doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers) // [0, 2, 4, 6, 8]

2.2 filter

  • filter함수는 컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출합니다.
// numbers의 요소 중 짝수를 걸러내어 새로운 배열로 반환
let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in
    return number % 2 == 0
}
print(evenNumbers) // [0, 2, 4]

// 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저
let oddNumbers: [Int] = numbers.filter {
    $0 % 2 != 0
}
print(oddNumbers) // [1, 3]

2.3 reduce

  • reduce함수는 컨테이너 내부의 콘텐츠를 하나로 통합합니다.
// 통합하고자 하는 someNumbers
let someNumbers: [Int] = [2, 8, 15]

// 초깃값이 0이고 someNumbers 내부의 모든 값을 더합니다.
let sum: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in
    //print("\(first) + \(second)") //어떻게 동작하는지 확인해보세요
    return first + second
})

print(sum)  // 25

// 초깃값이 0이고 someNumbers 내부의 모든 값을 뺍니다.
var subtract: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in
    //print("\(first) - \(second)") //어떻게 동작하는지 확인해보세요
    return first - second
})

print(subtract) // -25

// 초깃값이 3이고 someNumbers 내부의 모든 값을 더합니다.
let sumFromThree = someNumbers.reduce(3) { $0 + $1 }

print(sumFromThree) // 28

*flatmap

 

  • 제네릭(Generics)
  • 서브스크립트(Subscript)
  • 접근수준(Access Control)
  • ARC(Automatic Reference Counting)
  • 중첩타입(Nested Types)
  • 사용자정의 연산자(Custom Operators)
  • 오류 처리(Error Handling)
  • 불명확 타입(Opaque Types)
  • 프로토콜 지향 프로그래밍(Protocol Oriented Programming)
반응형
Comments