์ด์ ๊น์ง ์ปด๋ฐ์ธ ํ๋ ์์ํฌ๋ฅผ ์ด์ฉํ๋ฉด์ ํด์๋ ์๋ฌ์ฒ๋ฆฌ๋ค์ ๋ชจ๋ ์ ์คํธ๋ฆผ์์ ์๋ฌ๋ฅผ ๋์ ธ์ฃผ๋ฉด ๋ค์ด์คํธ์์์ .sink ๋ฅผ ํตํด ๋ฐ๊ธฐ๋ง ํ๋ ์๋์ ์ธ ๋ฐฉ๋ฒ์ด์๋ค. ํ์ง๋ง ๋ง์ฝ ์๋ฌ๋ฅผ throwํ๋ ๋ฉ์๋๋ฅผ sink๋ฐ๊ธฐ์ ์ ์ฐ๊ณ sink์์ catchํ๊ณ ์ถ๋ค๋ฉด? ํน์ OOP๊ฐ๋ ๊ณผ ํจ๊ป ์ปด๋ฐ์ธ์ ์ฐ๋ฉด์ ์ธํฐํ์ด์ค๋ก๋ถํฐ ๋์จ Stream์ ๋ฐ์ ๋ ๋ณดํต ์ถ์ํ๋ ์๋ฌ๋ฅผ ๋ฐ๊ธฐ ๋๋ฌธ์ sink๋ก ๋ฐ๊ธฐ์ ์ ๊ตฌ์ฒด ์๋ฌํ์ ์ผ๋ก ๋ฐ๊ฟ์ผํ๋ค๋ฉด? .tryMap ์ฐ์ฐ์๋ฅผ ํตํด ์๋ฌ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค.
enum APIError: Error {
case networkError
case dataCorrupted
case invalidFormat
}
struct User {
let id: Int
let name: String
}
// ์์ JSON ๋ฐ์ดํฐ
let jsonData = """
{
"id": 123,
"name": "Alice"
}
""".data(using: .utf8)!
// ์ถ์ํ๋ ์๋ฌ๋ฅผ ์ฒ๋ฆฌํ๊ณ ๋ฐ์ดํฐ๋ฅผ ํ์ฑํ๋ ํจ์
func fetchData() -> AnyPublisher<Data, Error> {
// API์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์ํฉ์ ์๋ฎฌ๋ ์ด์
Just(jsonData)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
}
private func parseUser(from data: Data) throws -> User {
guard let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []),
let dictionary = jsonObject as? [String: Any],
let id = dictionary["id"] as? Int,
let name = dictionary["nnnaaammme"] as? String else { // ์ผ๋ถ๋ฌ ์๋ฌ๋ฐ์
throw APIError.dataCorrupted
}
return User(id: id, name: name)
}
// ๋ค์ด์คํธ๋ฆผ์์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
func handleData() {
var subscriptions = Set<AnyCancellable>()
fetchData()
.tryMap { data -> User in
try parseUser(from: data)
}
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Data processing completed successfully.")
case .failure(let error):
print("An error occurred: \(error)")
}
}, receiveValue: { user in
print("Received user: \(user)")
})
.store(in: &subscriptions)
}
handleData()
.tryMap ์ ํ์ฉํ ์์ ์ฝ๋์ด๋ค. ์ดํด๋ณด๋ฉด fetachData() ๋ฉ์๋๋ AnyPublisher<Data, Error> ๋ผ๋ ์ถ์ํ๋ Error ํ์ ์ ๋์ ธ์ค๋ค. ๋ค์ด์คํธ๋ฆผ์์๋ ์ ์คํธ๋ฆผ์์ ๋์ ธ์ฃผ๋ Error๋ฅผ ๊ตฌ์ฒดํํด์ ์ฒ๋ฆฌํด์ผํ๋ค. ์ด๋ parseUser() ๋ฉ์๋๋ ๊ตฌ์ฒดํ๋ ์๋ฌ๋ฅผ throwํ๊ณ ์๋ค. ์ด๋ ๊ฒ ์ถ์ํ๋ ์๋ฌ์ ๊ตฌ์ฒดํ๋ ์๋ฌ์ฒ๋ฆฌ์ ๊ฐ๊ต์ญํ ๋ฐ throw ๋ฉ์๋๋ฅผ sink๋ก ๋ฐ๊ธฐ์ ์ ์ฒ๋ฆฌํ ์ ์๋ ์ฐ์ฐ์๊ฐ ๋ฐ๋ก tryMap ์ฐ์ฐ์์ด๋ค. ์๋น์ ์ ์ฅ์์ sink๋ก ์ ์คํธ๋ฆผ ๋ฐธ๋ฅ๋ก ๋ฐ๊ธฐ์ ์ ๋จผ์ tryMap์ ํตํด ์๋ฌ๋ฅผ ๊ตฌ์ฒดํํด์ ๋์ ธ์ค๋ค sink๋ฅผ ํตํ ๋ค์ด์คํธ๋ฆผ์์ ํจ๊ณผ์ ์ผ๋ก ์๋ฌ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค.
์์ tryMap์ upstream์์ ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ํน์ ํ ํ์์ผ๋ก ๋ณํํ๋ ค๊ณ ์๋ํ ๋ ์ฌ์ฉ๋๋ค๋ฉด mapError๋ upstream์์ ๋ฐ์ํ ์๋ฌ๋ฅผ ๋ค์ด์คํธ๋ฆผ์์ ๋ค๋ฃฐ ์ ์๋ ๋ค๋ฅธ ํํ์ ์๋ฌ๋ก ๋งคํ(๋ณํ)ํ ๋ ์ฌ์ฉ๋๋ค. tryMap์ด ์๋ฌ๋ฅผ "๋ฐ๊พธ๋" ๋๋์ด์๋ค๋ฉด mapError๋ ๋ง๊ทธ๋๋ก ์๋ฌ๋ฅผ "๋งตํ"ํ๋ค. ์๋ ์์ ์ฝ๋๋ฅผ ๋ค์ฌ๋ค๋ณด์
// ๋คํธ์ํฌ ์์ฒญ์ ๊ฒฐ๊ณผ๋ฅผ ๋ํ๋ด๋ enum
enum NetworkRequestError: Error {
case connectionError
case invalidData
case unknown
}
// ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ค ์๋ฌ
enum UserFriendlyError: Error {
case unableToConnect
case corruptedData
case somethingWentWrong
}
// ๋คํธ์ํฌ ์์ฒญ์ ์๋ฎฌ๋ ์ด์
ํ๋ ํจ์
func performNetworkRequest() -> AnyPublisher<String, NetworkRequestError> {
// ์ค์ ์ ํ๋ฆฌ์ผ์ด์
์์๋ ๋คํธ์ํฌ ์์ฒญ ์ฝ๋๋ฅผ ์ฌ๊ธฐ์ ์์ฑ
Fail(error: NetworkRequestError.connectionError)
.eraseToAnyPublisher()
}
// ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋ฐ ์๋ฌ ๋งคํ
func handleRequest() {
let cancellable = performNetworkRequest()
.map { data in
// ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋ก์ง์ ์ฌ๊ธฐ์ ์ถ๊ฐ
print("Data received: \(data)")
}
.mapError { error -> UserFriendlyError in
// ๋คํธ์ํฌ ์๋ฌ๋ฅผ ์ฌ์ฉ์ ์นํ์ ์ธ ์๋ฌ๋ก ๋งคํ
switch error {
case .connectionError:
return .unableToConnect
case .invalidData:
return .corruptedData
case .unknown:
return .somethingWentWrong
}
}
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Completed successfully.")
case .failure(let error):
print("An error occurred: \(error)")
}
}, receiveValue: { _ in
// ๋ฐ์ดํฐ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌํ ๊ฒฝ์ฐ ์คํ๋๋ ๋ธ๋ก
})
}
์ ์์ ์ฝ๋๋ฅผ ๋ณด๋ฉด tryMap์ ์๋ฌ๋ฅผ throwํ๊ฑฐ๋ ์ถ์ํ๋ ์๋ฌํ์ ์ ๊ตฌ์ฒดํ๋ ์๋ฌํ์ ์ผ๋ก "๋ณํ"์ ํด์ฃผ์๋ค๋ฉด mapError๋ ์๋ฌ ์ผ์ด์ค๋ณ๋ก ๋ ๋ค๋ฅธ ์๋ฌ์ "๋งตํ"์ ํด์ฃผ๊ณ ์์์ ํ์ธํ ์ ์๋ค. ์ด๋ฐ ์๋ฌ ๋งตํ์ ๋ณด๋ฉฐ ๋ ์๊ฐ์ ํด๋ฆฐ์ํคํ ์ณ ๊ธฐ์ค์ผ๋ก ๊ฐ ๋ ์ด์ด๊ฐ ์๋ก ๋ค๋ฅธ ์๋ฌ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ด์ผํ ๋ ์ ์ฉํ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์๋ค.
24.05.15 ์ถ๊ฐ
import Combine
let jsonData = """
{
"name": "John",
"age": 30
}
""".data(using: .utf8)!
let publisher = Just(jsonData)
.tryMap { data -> [String: Any] in
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
throw URLError(.badServerResponse)
}
return json
}
.sink(receiveCompletion: { completion in
if case let .failure(error) = completion {
print("Error occurred: \(error)")
}
}, receiveValue: { json in
print("Received JSON: \(json)")
})
์ปด๋ฐ์ธ์ ์๋ฌ์ฒ๋ฆฌ์์ tryMap ์ฐ์ฐ์๋ฅผ ์๊ฐํ์ง๋ง tryMap์ ๋ฐ์ดํฐ ๋ณํ ์์ ์ ์ค์ ์ ๋๋ฉฐ, ์๋ฌ ์ฒ๋ฆฌ๋ณด๋ค๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉ๋๋ค. ์ด ๊ณผ์ ์์ ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ throw๋ก ์ฒ๋ฆฌํด์ค ์ ์๋ค. ๋ง์ฝ ์ง์ ์ด๋ฏธ ๋ฐ์ํ ์๋ฌ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์คํธ๋ฆผ์ ํ๋ฆ์ ๋ณ๊ฒฝํ๊ฑฐ๋ ๋์ฒด ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ๊ณ ์ ํ ๋์๋ cacth ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์
https://www.kodeco.com/21773708-intermediate-combine/lessons/4
'๐ฆ Flutter' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ํ๋ฌํฐ ํ๋ก์ ํธ ์คํ์ ์์ดํฐ ์ค๊ธฐ๊ธฐ(or ์๋ฎฌ)๊ฐ ๋์๊ฐ์ง ์๋ ๋ฌธ์ (0) | 2024.05.19 |
---|---|
| Combine | 12. Retrying and Catching Errors (0) | 2024.05.15 |
| Combine | 10. Managing Backpressure (0) | 2024.05.14 |
| Combine | 9. Networking with Combine (0) | 2024.05.14 |
| Combine | 8. Sequencing Operators (0) | 2024.05.13 |