[RxSwift 정리 - 2] Cold Observable

팔랑이·2025년 5월 16일

iOS/Swift

목록 보기
71/81

[RxSwift 정리 - 1] Hot Observable에 이어서, Cold Observable
Repositry, Usecase쪽에서 자주 쓰는 Single, Completable을 정리해 보겠당

썸네일용

Cold Observable을 비동기 계층에서 사용하는 이유

이녀석들은 주로 Repository·UseCase 단에서 사용했다. 대체로 네트워크 요청, 디스크 I/O, 인증, 파일 읽기 등 실제 side-effect가 발생하는 작업을 담당한다.

Cold Observable은 “구독할 때마다 새로운 데이터 스트림을 만든다”.

이 특성 때문에 다음이 보장된다.

  • 요청이 필요할 때만 실행됨
  • 재구독 시 동일한 비동기 작업을 다시 수행
  • 부작용을 명확하게 제어할 수 있음
  • UI 계층에서 여러 번 구독해도 데이터 소스 자체는 ViewModel에게만 노출됨

결과적으로 Cold Observable은 “비동기 작업이 실제로 발생해야 하는 계층”에 적합하다고 할 수 있다.


Single

Single은 성공 또는 실패 중 하나의 이벤트만 방출하는 Observable이다.

.success(value) 또는 .failure(error) 방출

네트워크 요청의 결과처럼 “값 하나 반환 후 끝나는 비동기 작업”에 가장 자주 사용한다.

예시

생성

func fetchUser() -> Single<User> {
    return Single.create { observer in
        api.loadUser { result in
            switch result {
            case .success(let user):
                observer(.success(user))
            case .failure(let error):
                observer(.failure(error))
            }
        }
        return Disposables.create()
    }
}

구독

fetchUser()
    .subscribe { event in
        switch event {
        case .success(let user):
            print("user:", user)
        case .failure(let error):
            print("error:", error)
        }
    }
    .disposed(by: disposeBag)

구독할 때마다 실제 API 요청이 실행된다. 이것이 Cold Observable의 대표적인 흐름이다.


Completable

Completable은 값을 내보내지 않고, 성공 또는 실패 여부만 전달하는 Observable이다. 어떤 작업이 끝났는지(완료/에러)만 중요할 때 사용한다.

대표적인 예시는 데이터 저장, 캐시 삭제, 토큰 갱신 등 결과값이 필요 없는 비동기 작업 이다.

예시

생성

func saveUser(_ user: User) -> Completable {
    return Completable.create { observer in
        database.save(user) { error in
            if let error = error {
                observer(.error(error))
            } else {
                observer(.completed)
            }
        }
        return Disposables.create()
    }
}

구독

saveUser(user)
    .subscribe(
        onCompleted: {
            print("saved")
        },
        onError: { error in
            print("failed:", error)
        }
    )
    .disposed(by: disposeBag)

다음 편에는 Relay를 래핑한 Signal, Driver도 살펴볼 예정

profile
정체되지 않는 성장

0개의 댓글