RxSwift 공부하게 된 이유?
RxSwift를 왜 사용할까???
RxSwift를 사용하기 이전에는 비동기적인 task를 처리하기 위해서 escaping closure
를 사용해야했습니다.
이로 인해서 return 타입이 명시되지 않고 escaping closure 내부의 파라미터에 적어줘서 직관적이지 않고, 중첩 클로저가 쌓이는 등 다양한 문제가 생겼습니다.
-> 'return 타입으로 비동기 처리의 결과를 전달할 수 있지 않을까?' 라는 생각을 했습니다.
Rx를 사용하지 않기 위한 노력
항상 어떠한 기술을 사용하기 전에 그 기술이 필요한 이유를 생각하고 정말 필요하다고 생각이 들 때 도입을 하려고 노력을 합니다.
Rx 공부를 하기 전 뷰를 최대한 멍청하게 하고 뷰모델이 뷰를 모르게 하기 위해서 didSet
을 적극적으로 활용해보았습니다.
@propertyWrapper
class ViewModelState<T> {
typealias WillUpdate = (T) -> Void
typealias DidUpdate = (T) -> Void
var willUpdate: WillUpdate?
var didUpdate: DidUpdate?
var wrappedValue: T {
willSet {
willUpdate?(newValue)
}
didSet {
didUpdate?(wrappedValue)
}
}
var projectedValue: ViewModelState<T> {
self
}
init(wrappedValue initialValue: T) {
wrappedValue = initialValue
}
}
propertyWrapper
를 활용하여 ViewModelState라는 클래스를 만들어서 클로저를 저장하고, 값이 바뀔 때마다 클로저를 실행시켜주는 형식입니다.
//선언부(viewmodel)
@ViewModelState var contents = [Product]()
//클로저 설정(viewController)
viewModel.$contents.didUpdate = { [weak self] _ in
...
}
다음과 같이 didUpdate
클로저를 지정해두고 처리를 해주는 형식입니다.
( 클로저 설정을 해주는 부분이 구독을 하고 있는 부분이라고 생각하면 될 것 같습니다.)
RxSwift의 필요성
간단하게 하나의 값을 UI에 바인딩하거나, 트리거로 사용할 때는 위의 코드로 문제가 없었으나 하나의 같은 값을 가지고 여러가지 로직을 처리하고, 두 개 이상의 값을 구독하고 합쳐서 이뤄지는 로직을 수행하거나, 데이터에 변형을 하려고 했을 때 관리가 어려워지는 것을 느꼈고, RxSwift의 stream의 개념의 필요성을 느끼게 되었습니다.
또한 Data layer와 Domain layer에서 completion handler
의 과도한 사용으로 코드에서 인덴트가 점점 들어가게 되었고 가독성이 좋지 않게 되었습니다.
RxSwift의 요소
1.Observables
데이터를 전달할 수 있는 이벤트들을 비동기적으로 생성하는 요소.
전달할 수 있는 3가지:
1.error를 알려줄 때
onError
를 던져준다
2.값을 전달할 때onNext(데이터)
를 활용해서 값을 던져준다.
3.끝났다고 알려줘야 할 때
onCompleted
로 끝났다고 알려준다.
2.Operator
Observable에 어떤 처리를 해주어서 내가 원하는 로직을 만들어야 하는데, 이러한 로직을 만들어줄 수 있는 요소
ex) 아이디와 비밀번호를 입력하고 로그인 버튼을 눌렀을 때 아이디와 비밀번호를 통해 로그인 허용 여부를 확인한다
-> 이러한 로직을 만들 수 있는 것이 Operator
3.Schedulers
함수를 실행하는 스레드를 결정해주는 역할을 하는 요소
Main Thread에서 실행해야 하는 함수(UI관련 등)와 Background에서 실행해야 하는 함수를 구분해서 적용할 수 있다.
최종 정리
Observable
로 전달하고 싶은 값을 만들어 주고,Operator
로 값에 대한 로직을 처리해서Scheduler
를 알맞게 지정해서 사용을 한다.
Observable
Observable은 관찰 가능하도록 껍데기를 입혀서 Observer들이 구독을 할 수 있도록 하는 데이터 타입.
Observable은 구독을 하고 있는 Observer에게 3가지 이벤트를 전달해준다.
1.onError: 에러
2.onNext(): 값 전달
3.onCompleted: 끝났어
Observable의 생명주기
1.Create되고
2.Subscribe되고
3.next로 값 던져주다가
4.complete로 끝난다 / 아니면 error 와서 끝난다
5.Dispose로 없애준다
-주의: create한다고 실행되지 않는다.
그럼 언제 실행될까? → subscribe가 됐을 때
Traits
Observable을 사용해서 모든 것을 처리할 수 있지만, 해당 값의 목적에 알맞은 기능의 타입의 필요성이 생겨서 사용
Single
Foundation
의 Result 타입을 내부적으로 사용해서
성공일 때 -> 값 전달
실패일 때 -> 오류 전달
을 해주어서 직관적으로 처리할 수 있도록 해주는 타입입니다.
API Call처럼 단일 값이 전달되고 성공 실패 유무를 알려주기 좋을 때 자주 사용.
스트림이 계속 이어지지 않고 하나의 값을 전달해줄 때 사용된다
-튜플로 성공 여부와 값을 전달하면 안될까?
(실패, 값) , (성공, 에러) 와 같은 경우에 있을 수 없는 경우의 수임에도 불구하고 전달이 될 수 있고, 이에 대한 분기 처리를 해줘야 하기 때문에 Result 타입과 같이 성공할 때는 값만, 실패할 때는 오류만 전달을 해서 경우의 수를 알맞게 조절한다(Result 타입이 도입된 이유와 동일)
Completable
completed
또는 error
를 전달.
작업이 끝났을 때 데이터를 전달하지 않아도 될 때 자주 사용
Maybe
Single과 Completable의 짬뽕
success
, completed
, error
를 방출할 수 있음.
RxCocoa Traits
Driver
UI의 경우에 error나 completed가 방출된다고 해서 사용 중에 멈추면 안되기 때문에 Driver가 탄생
-error를 방출하지 않는다.
-항상 Main Thread에서 작동을 보장한다.
-drive
함수로 이벤트 처리
Signal
-Driver와 공통점이 많지만 새로운 Observer에게 replay해주지 않는다. (구독할 때 최신 값을 전달하지 않는다)
-emit
함수로 이벤트 처리
Subject
Observable은 데이터가 정해진 스트리밍입니다. create 할 때부터 어떤 데이터를 줘야할 지 정해져 있고, 해당 데이터를 바꿔줄 수는 없습니다.
이에 반해 Subject는 안에 들어가는 데이터를 변경해줄 수 있습니다.
즉, 값이 A에게 들어오면, A를 구독하고 있는 아이들에게 알려주는 역할입니다.
PublishSubject
구독을 하는 시점부터 방출되는 이벤트를 전달해주는 아이입니다.
BehaviorSubject
publishSubject와 달리 구독을 했을 때 가장 최근의 이벤트를 전달해줍니다.
구독을 했을 시점에 어떤 값이 들어와야할 때 사용합니다.
따라서 전달해줄 초기값이 무조건 있어야 하므로 생성할 때 초기값을 지정해줘야합니다.
그 외 subject들
AsyncSubject, ReplaySubject 등 다양한 subject들이 있지만 대부분의 로직에서는 위의 두 subject로 해결이 됩니다...
Relay
subject의 경우 complete나 error가 방출되면 stream이 끊기게 됩니다.
Driver를 사용하는 이유와 동일하게 해당 스트림이 끊기지 않게 유지를 하기 위해서 onNext만 있는 아이가 필요했고, 이러한 아이가 Relay입니다.
PublishRelay, BehaviorRelay 등이 있습니다.
'🍎 iOS > ⚡️ RxSwift' 카테고리의 다른 글
Operators - RxSwift를 편리하게 사용해보자 (0) | 2022.12.03 |
---|