반응형
Notice
Recent Posts
Recent Comments
Link
Today
Total
04-16 09:06
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
관리 메뉴

iOS 개발 기록 블로그

REST API를 RxSwift+Moya 환경과 접목하여 알아보자 본문

Back-end

REST API를 RxSwift+Moya 환경과 접목하여 알아보자

crazydeer 2024. 11. 19. 15:57
반응형

RxSwift와 Moya를 사용하여 REST API를 처리하고 있다면,

REST API에 대한 이해를 iOS 개발 관점에서 더 심화하는 것이 좋습니다.

이를 위해 아래의 주제들을 알아두면 더욱 효과적으로 개발할 수 있습니다.

 

 

1. Moya와 REST API의 연계

Moya는 네트워크 계층을 관리하는 데 강력한 라이브러리로, REST API를 RxSwift와 결합하여 비동기적인 방식으로 사용할 수 있습니다. 다음은 알아두면 좋은 Moya 활용 관련 지식입니다:

Moya의 기본 구조 이해

  • TargetType을 정의하여 API의 엔드포인트를 명확히 관리:
enum MyAPI {
    case fetchUsers
    case createUser(name: String, email: String)
}

extension MyAPI: TargetType {
    var baseURL: URL { URL(string: "<https://api.example.com>")! }
    var path: String {
        switch self {
        case .fetchUsers: return "/users"
        case .createUser: return "/users"
        }
    }
    var method: Moya.Method {
        switch self {
        case .fetchUsers: return .get
        case .createUser: return .post
        }
    }
    var task: Task {
        switch self {
        case .fetchUsers: return .requestPlain
        case .createUser(let name, let email):
            return .requestParameters(
                parameters: ["name": name, "email": email],
                encoding: JSONEncoding.default
            )
        }
    }
    var headers: [String: String]? {
        return ["Content-Type": "application/json"]
    }
}

RxSwift와 Moya의 결합

  • Moya에서 RxSwift를 통해 비동기 요청 처리:
let provider = MoyaProvider<MyAPI>()
provider.rx.request(.fetchUsers)
    .map([User].self)  // User는 Codable을 준수
    .subscribe(onSuccess: { users in
        print("Fetched users: \\(users)")
    }, onError: { error in
        print("Error: \\(error)")
    })
    .disposed(by: disposeBag)

에러 처리

  • HTTP 상태 코드 및 서버 응답을 통해 에러를 분류하고 처리:
provider.rx.request(.fetchUsers)
    .filterSuccessfulStatusCodes()  // 200-299 상태 코드만 허용
    .subscribe(onSuccess: { response in
        print("Success: \\(response)")
    }, onError: { error in
        if let moyaError = error as? MoyaError {
            switch moyaError {
            case .statusCode(let response):
                print("Status Code Error: \\(response.statusCode)")
            default:
                print("Other Error: \\(moyaError.localizedDescription)")
            }
        }
    })
    .disposed(by: disposeBag)

 

2. REST API 특화 지식

RxSwift와 Moya를 잘 활용하려면 REST API의 특성을 더 깊이 이해하는 것이 중요합니다.

API Rate Limiting

  • 일부 API는 요청 빈도를 제한합니다. 이를 처리하기 위해:
    • 서버에서 429 Too Many Requests 상태 코드를 반환하는지 확인.
    • 클라이언트에서 요청을 제한하려면 RxSwift의 throttle 연산자를 활용:
Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .throttle(.seconds(1), scheduler: MainScheduler.instance)
    .subscribe(onNext: { _ in
        provider.rx.request(.fetchUsers).subscribe().disposed(by: disposeBag)
    })
    .disposed(by: disposeBag)

Pagination 처리

  • REST API에서 대량의 데이터를 효율적으로 가져오기 위해 페이징 처리가 필요합니다.
  • 보통 쿼리 파라미터로 페이지 정보를 전달:
enum MyAPI {
    case fetchUsers(page: Int)
}

extension MyAPI: TargetType {
    var task: Task {
        switch self {
        case .fetchUsers(let page):
            return .requestParameters(
                parameters: ["page": page],
                encoding: URLEncoding.default
            )
        }
    }
}
  • RxSwift를 사용한 페이징 처리 로직:
var currentPage = 1
func fetchNextPage() {
    provider.rx.request(.fetchUsers(page: currentPage))
        .map([User].self)
        .subscribe(onSuccess: { users in
            self.currentPage += 1
            self.users.append(contentsOf: users)
        })
        .disposed(by: disposeBag)
}

 

Retry 및 백오프 전략

  • 네트워크 요청 실패 시 재시도를 구현:
provider.rx.request(.fetchUsers)
    .retry(3)  // 3회 재시도
    .subscribe(onSuccess: { response in
        print("Success after retries")
    }, onError: { error in
        print("Request failed after retries")
    })
    .disposed(by: disposeBag)

 

  • 지수 백오프(Exponential Backoff) 적용:
provider.rx.request(.fetchUsers)
    .retryWhen { errors in
        errors.enumerated().flatMap { attempt, error -> Observable<Int> in
            let delay = pow(2.0, Double(attempt))
            return Observable<Int>.timer(.seconds(Int(delay)), scheduler: MainScheduler.instance)
        }
    }
    .subscribe()
    .disposed(by: disposeBag)

 

 

3. iOS에서 REST API 활용을 위한 최적화

Codable 활용

  • API 응답을 Codable로 매핑하여 간결한 모델 관리:
struct User: Codable {
    let id: Int
    let name: String
    let email: String
}

provider.rx.request(.fetchUsers)
    .map([User].self)
    .subscribe(onSuccess: { users in
        print("Fetched users: \\(users)")
    })
    .disposed(by: disposeBag)

 

네트워크 모니터링

  • 네트워크 연결 상태를 체크하여 적절한 사용자 피드백 제공:
    • Reachability 또는 Network.framework를 사용.

Response Caching

  • 데이터 요청을 최소화하기 위해 캐싱 사용:
    • URLSession의 URLCache를 활용하거나 Moya의 커스텀 플러그인으로 캐싱 구현.

로컬 데이터와 동기화

  • RxSwift와 CoreData 또는 Realm을 결합해 API 응답 데이터를 로컬 저장소와 동기화.

 

4. 비동기 작업과 UI의 연계

메인 스레드 처리

  • REST API 호출 후 UI 업데이트는 메인 스레드에서 수행:
provider.rx.request(.fetchUsers)
    .observe(on: MainScheduler.instance)
    .subscribe(onSuccess: { users in
        self.tableView.reloadData()
    })
    .disposed(by: disposeBag)

 

데이터 바인딩

  • RxCocoa를 사용해 REST API 데이터를 UI와 바인딩:
provider.rx.request(.fetchUsers)
    .map([User].self)
    .bind(to: tableView.rx.items) { tableView, index, user in
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: IndexPath(row: index, section: 0))
        cell.textLabel?.text = user.name
        return cell
    }
    .disposed(by: disposeBag)

 

5. 실시간 기능

RxSwift를 통해 REST API와 WebSocket 또는 SSE(Server-Sent Events) 같은 실시간 데이터를 조합하는 방식도 알아두면 유용합니다.

REST API 응답의 상태를 Rx 관찰 가능한 형태로 가공해서 활용해보면 좋습니다.

 

이 실시간 기능도 나중에 자세히 더 공부해보려고 합니다. 처음 보는 것들이라 ㅎㅎ

반응형