안녕하세요! 피피아노입니다🎵
이번 포스팅에서는 Swiftdml Combine 프레임워크에서 제공하는 Operator(연산자)에 대해서 알아보려고 합니다.
그럼 바로 시작해보겠습니다.
Combine 프레임워크의 Operator
Combine은 비동기 프로그래밍을 위한 Swift의 선언적 프레임워크입니다. 이는 비동기 작업을 더욱 쉽게 처리하도록 도와주며, 코드의 가독성과 유지보수성을 높여줍니다. Combine에 대한 내용이 더 궁금하다면 여기를 참고해주세요!
Combine은 다양한 Operator를 제공하며, 이 Oprator들은 비동기 이벤트를 처리하는데 매우 중요한 역할을 합니다. 각 Oprator는 고유한 기능을 가지고 있는데 각 Oprator에 대해 살펴보겠습니다.
Operator 종류
연산자의 종류는 많지만 제가 공부한 연산자 위주로 정리를 해보려고 합니다. 우선 연산자는 변환 연산자와 필터 연산자, 조합 연산자로 나뉘는데, 변환 연산자로는 Map, TryMap, FlatMap이 존재하고 필터 연산자는 Filter, CompactMap이 있고 마지막으로 조합 연산자로는 Merge, CombineLatest, Zip이 존재합니다.
그럼 이제 각 연산자들의 특징과 간단한 사용 방법에 관한 소스 코드에 대해서 알아보겠습니다.
변환 연산자
Map Operator
Map Operator는 Transform 클로저를 이용해서 Publisher에서 방출하는 이벤트의 값을 변환하는 역할을 합니다. 이 Operator는 각 이벤트를 처리하고, 그 결과를 새로운 이벤트로 반환합니다.
let numbers = [1, 2, 3].publisher
numbers
.map { $0 * 2 }
.sink { print("받은 값: \($0)") }
// 출력: 받은 값: 2, 받은 값: 4, 받은 값: 6
TryMap Operator
TryMap Operator는 Map 연산자와 비슷한 역할을 합니다. 하지만 차이점이 있다면, TryMap Operator는 변환 과정에서 에러가 발생할 수 있는 경우에 사용됩니다.
다시 말해, TryMap Operator는 Map 연산자와 마찬가지로 Publisher에서 방출하는 이벤트의 값을 변환하는 역할을 하지만, 이 과정에서 에러가 발생할 수 있음을 명시적으로 표현합니다. 이 연산자를 사용하면 변환 과정에서 발생하는 에러를 캐치하고, 이를 다음 연산자로 전달할 수 있습니다.
예를 들어, String 값을 Int 값으로 변환하는 작업을 수행할 때, TryMap Operator를 사용하면 변환 과정에서 숫자로 변환할 수 없는 문자열이 발생했을 때 이를 에러로 처리하고 이를 다음 연산자로 전달할 수 있습니다.
let strings = ["1", "2", "a", "3"].publisher
strings
.tryMap { str -> Int in
guard let number = Int(str) else {
throw CustomError.invalidNumber
}
return number
}
.sink(receiveCompletion: { _ in }, receiveValue: { print($0) })
// 출력: 1, 2, 에러 발생: invalidNumber
FlatMap Operator
FlatMap Operator는 Publisher에서 방출하는 이벤트를 다른 Publisher로 변환하는 역할을 합니다. 이 연산자는 Map 연산자와 비슷하지만, 반환 값이 Publisher인 경우에 사용됩니다.
이 연산자는 각 이벤트를 새로운 Publisher로 변환하고, 이 새로운 Publisher가 방출하는 모든 이벤트를 단일 Publisher로 결합하여 방출합니다. 즉, 여러 개의 Publisher를 하나의 Publisher로 "평탄화"하는 역할을 합니다.
이 연산자는 주로 비동기 작업의 체인을 생성할 때 사용됩니다. 예를 들어, 네트워크 요청의 결과에 따라 다른 네트워크 요청을 수행해야 하는 경우, FlatMap 연산자를 사용하면 이러한 작업을 간결하게 표현할 수 있습니다.
let numbers1 = [1, 2, 3].publisher
let numbers2 = [4, 5, 6].publisher
numbers1
.flatMap { _ in numbers2 }
.sink { print("받은 값: \($0)") }
// 출력: 받은 값: 4, 받은 값: 5, 받은 값: 6, 받은 값: 4, 받은 값: 5, 받은 값: 6, 받은 값: 4, 받은 값: 5, 받은 값: 6
필터 연산자
Filter Operator
Filter Operator는 조건에 맞는 이벤트만을 선택하여 전달하는 역할을 합니다. 이 연산자는 각 이벤트를 검사하고 주어진 클로저가 true를 반환하는 이벤트만을 전달합니다.
이 연산자는 원하지 않는 이벤트를 제거하거나, 특정 조건에 맞는 이벤트만을 선택할 때 유용합니다. 예를 들어, 짝수만을 선택하거나 특정 범위의 값만을 선택하는 등의 작업을 수행할 수 있습니다.
let numbers = [1, 2, 3, 4, 5].publisher
numbers
.filter { $0 % 2 == 0 }
.sink { print("받은 값: \($0)") }
// 출력: 받은 값: 2, 받은 값: 4
CompactMap Operator
CompactMap Operator는 Map 연산자와 비슷한 기능을 하지만, nil 값을 제거하는 특성이 있습니다. 이는 Publisher에서 방출하는 이벤트를 다른 값으로 변환하는 역할을 하며, 반환 값이 nil인 경우 해당 이벤트를 제거하게 됩니다. 즉, CompactMap은 변환과 필터링을 동시에 수행하는 연산자라고 볼 수 있습니다.
이 연산자는 주로 옵셔널 값을 다룰 때됩니다. Map 연산자를 사용하면 변환 과정에서 nil이 발생할 경우 옵셔널 값을 그대로 전달하게 되지만, CompactMap 연산자를 사용하면 이러한 nil 값이 제거되어 결과적으로 옵셔널이 아닌 값을 얻을 수 있습니다.
따라서, CompactMap 연산자는 옵셔널 값을 안전하게 처리하면서 동시에 원치 않는 이벤트를 제거하는데 유용합니다.
예를 들어, String 값을 Int 값으로 변환하는 작업을 수행할 때, CompactMap 연산자를 사용하면 숫자로 변환할 수 없는 문자열 이벤트는 제거되고, 숫자로 변환된 이벤트만을 전달받을 수 있습니다.
let strings = ["1", "2", "a", "3", "b"].publisher
strings
.compactMap { Int($0) }
.sink { print("받은 값: \($0)") }
// 출력: 받은 값: 1, 받은 값: 2, 받은 값: 3
조합 연산자
Merge Operator
Merge Operator는 여러 개의 Publisher들의 이벤트를 하나로 합치는 역할을 합니다. 이 연산자를 사용하면 여러 비동기 작업의 결과를 하나의 스트림으로 관리할 수 있습니다.
Merge Operator는 입력으로 받는 Publisher들이 각각 이벤트를 방출하면 이를 하나의 스트림으로 합쳐서 방출합니다. 이때 각 Publisher의 이벤트는 동시에 처리되며, 이벤트는 각 Publisher에서 방출된 순서대로 처리됩니다.
예를 들어, 두 개의 비동기 작업이 각각 다른 시간에 완료되더라도, Merge Operator를 사용하면 두 작업의 결과를 하나의 스트림으로 관리할 수 있습니다. 이렇게 하면 각각의 작업을 따로 처리하지 않고, 하나의 스트림에서 모든 작업의 결과를 관리할 수 있어 코드의 가독성과 유지보수성을 높일 수 있습니다.
let numbers1 = [1, 2, 3].publisher
let numbers2 = [4, 5, 6].publisher
Publishers.Merge(numbers1, numbers2)
.sink { print("받은 값: \($0)") }
// 출력: 받은 값: 1, 받은 값: 2, 받은 값: 3, 받은 값: 4, 받은 값: 5, 받은 값: 6
CombineLatest Operator
CombineLatest Operator는 두 개 이상의 Publisher들의 이벤트를 합쳐서 전달하는 역할을 합니다. 이 연산자는 각 Publisher들의 최신 이벤트를 받아 새로운 이벤트를 생성하고 이를 방출합니다.
이 연산자는 여러 비동기 작업의 결과를 동시에 처리해야 할 때 유용합니다. 예를 들어, 두 개의 네트워크 요청이 동시에 완료되면 그 결과를 합쳐서 한 번에 처리해야 하는 경우 CombineLatest 연산자를 사용할 수 있습니다.
let numbers1 = [1, 2, 3].publisher
let numbers2 = [4, 5, 6].publisher
numbers1.combineLatest(numbers2)
.sink { print("받은 값: \($0)") }
// 출력: 받은 값: (1, 4), 받은 값: (2, 5), 받은 값: (3, 6)
Zip Operator
Zip Operator는 두 개 이상의 Publisher들의 이벤트를 쌍으로 묶는 역할을 합니다. 이 Operator는 서로 다른 Publisher들의 이벤트를 동시에 처리해야 하는 경우에 유용합니다.
예를 들어, 두 개의 비동기 작업이 각각 다른 시간에 완료되더라도, Zip Operator를 사용하면 두 작업이 모두 완료될 때까지 기다린 후, 해당 결과를 쌍으로 처리할 수 있습니다.
let numbers1 = [1, 2, 3].publisher
let numbers2 = [4, 5, 6].publisher
numbers1.zip(numbers2)
.sink { print("받은 값: \($0)") }
// 출력: 받은 값: (1, 4), 받은 값: (2, 5), 받은 값: (3, 6)
지금까지 Combine 프레임워크의 다양한 연산자들에 대해 알아보았습니다. 변환 연산자, 필터 연산자, 조합 연산자 등을 활용하면 비동기 작업을 효과적으로 관리하고 처리할 수 있습니다. 이들 연산자들은 각자의 특성과 사용 방법을 이해하고 적절히 활용함으로써 복잡한 비동기 작업도 간결하고 이해하기 쉬운 코드로 표현할 수 있습니다.
이해가 안 가는 부분이나 추가로 궁금한 점이 있다면 언제든지 질문해주세요:)
감사합니다.
잘못된 내용이 있거나 더 좋은 내용 피드백은 언제나 환영합니다!
궁금하신 부분은 댓글로 질문 부탁드립니다!
'Apple > Swift' 카테고리의 다른 글
[Swift] 초기화(Initializers) 알아보기 (2) - 구조체, Memberwise, 클래스의 초기화, 지정/편의 초기화 (0) | 2024.02.04 |
---|---|
[Swift] 초기화(Initializers) 알아보기 (1) - 초기화의 개념과 사용 방법, 규칙 (0) | 2024.01.29 |
[Swift] Combine 개념과 사용방법 이해하기 (0) | 2024.01.17 |
[Swift] Generic과 Type 이해하기 (2) | 2024.01.09 |
[Swift] Generic 이해하기 (0) | 2024.01.04 |