-
RxSwift - Subject와 RelayProgramming/RxSwift 2022. 3. 25. 16:48
안녕하세요 BeePeach입니다.
오늘 공부해볼 내용은 Subject와 Relay입니다.
RxSwift의 기본개념에서 알아본 것처럼 Observable은 단방향입니다.
이벤트를 방출할 수 있지만 받지는 못합니다.
반대로 Observer는 이벤트를 받을 수는 있지만 방출할 수는 없습니다.
앱을 개발할때 Observable이 값을 방출하는 중에 Observer에게 이전에 정의해놓지 않은 새로운 값을 방출해야 하는 경우가 매우 많습니다.
그런데 Observable은 앞에서 말했듯이 read only이기 때문에 새로운 값을 방출하도록 할 수 없습니다.
create, just, of 등 오퍼레이터를 사용할때 미리 정의해놓은 값만 방출합니다.
이럴 때 Observable과 Observer의 특성을 모두 가지는 Subject와 Relay를 이용합니다.
Subject
Subject 중에서 가장 기본적은 PublishSubject를 살펴보면 SubjectType이면서 ObserverType인 것을 확인할 수 있습니다.
그리고 SubjectType은 ObservableType을 채용하고 있습니다.
즉, Subject는 Observable 이면서 Observer 입니다.
그래서 Observable에게 이벤트를 받아서 Observer에게 전달할 수 있습니다.
혹은 자기 자신이 Observer에게 이벤트를 전달할 수도 있죠.
Subject에는 4가지 종류가 있습니다.
- Publish
- Behavior
- Replay
- Async
그럼 하나하나 살펴보도록 하겠습니다.
Publish subject
가장 기본적인 형태의 Subject입니다.
Subject이므로 이벤트를 받고 방출할 수 있습니다.
Publish subject의 특징은 구독 이전에 방출한 이벤트는 Observer에게 전달되지 않습니다.
구독을 시작하고 이후에 전달되는 것만 전달이 됩니다.
그럼 생성해보도록 하겠습니다.
Publish Subject 인스턴스를 생성할 때는 init()을 사용합니다.
그리고 코드를 보면 onNext를 이용해서 10을 전달했지만 이후에 구독을 시작했기 때문에 아무런 이벤트로 전달받지 못한 것을 확인할 수 있습니다.
Subject에 한번 complected가 전달됐다면 이후에 구독자를 추가해도 Next 이벤트를 전달하지 않습니다.
바로 Completed가 전달됩니다,
마찬가지로 Error를 방출했다면 이후 Observer에게는 Error를 전달합니다.
다시 말해서 stop 이벤트가 전달됐다면 이후 구독자에게는 stop이벤트를 바로 전달합니다,
그래서 publish subject는 시간에 민감한 데이터를 처리할 때 주로 사용합니다.
시간이 지나면 이후로는 stop 이벤트가 전달되므로 이에 대한 처리를 해주면 됩니다.
주로 사용하는 예시로는 유저가 무엇인가를 탭 하거나, 알림이 도착했다는 이벤트 등을 처리할 때입니다.
Publish subject로 이제 원하는 새로운 데이터를 방출하도록 만들었습니다.
그런데 구독을 늦게 하게 된다면 이전 Next 이벤트를 받을 수 없었습니다.
만약 구독을 늦게 해도 최근 이벤트를 받고 싶다면 어떻게 할까요??
Behavior subject
이럴 때 사용하는 subject가 바로 Behavior Subject입니다.
Publish subject와 모든 기능이 똑같지만 단 하나,
Next 이벤트 방출 이후에 구독을 시작해도 이전에 지나간 이벤트를 받고 싶을 때 사용합니다.
이 의미는 Behavior subject는 무엇인가 항상 값을 가지고 있다는 의미입니다.
그러니까 Observer가 이후에 추가되어도 이전에 전달된 이벤트를 전달할 수 있겠죠?
그래서 객체를 생성할 때 기본값을 지정해주어야 합니다.
그럼 한 번 생성해보도록 하겠습니다.
BehaviorSubject 인스턴스를 생성할 때는 init(value:)를 사용해서 기본값을 지정해주어야 합니다.
그리고 결과를 보면 Publish subject 때와는 다른 것을 볼 수 있습니다.
구독 이후에 이벤트를 방출하지 않았는데 이벤트가 전달됐습니다.
Observer가 추가된 이후에 새로운 Next 이벤트를 전달하니까 기존 Observer에 이벤트가 전달되는 것을 볼 수 있습니다.
그리고 새로운 Observer를 추가시켰더니 초기값으로 설정한 "RxSwift"가 아닌 가장 최근에 방출한 "RxCocoa"가 전달됐습니다.
Publish subject와 마찬가지로 stop이벤트가 발생했다면 이후 Observer에게 stop이벤트를 바로 전달합니다.
여기서 중요한 부분은 이때 추가된 Observer에게는 저장된 next 이벤트가 전달되지 않습니다.
BehaviorSubject3 >>> 를 보면 새롭게 추가된 Observer이지만 "RxCocoa"는 전달되지 않고 바로 Completed가 전달되었습니다.
Behavior subject의 사용 예시로는
프로필을 Behavior subject로 만들면 새로운 데이터를 가져오기 전에 일단 먼저 저장된 데이터를 먼저 보여주게 됩니다.
이렇게 Behavior subject는 가장 최근 1개의 next이벤트만 저장하고 있다가 전달합니다.
그럼 만약 1개가 아니라 여러 개의 next이벤트를 저장하고 있다가 전달하고 싶다면 어떻게 할까요??
Replay Subject
더 많은 Next 이벤트를 저장하고 있다가 전달하고 싶으면 Replay Subject를 이용합니다.
그럼 이런 생각을 할 수도 있습니다.
'Behavior를 사용하지 않고 그냥 replay로 다 사용하면 되는 거 아닌가?'라는 생각을 할 수 있습니다.
하지만 둘에는 약간의 차이가 존재합니다.
생성할 때 buffersize를 지정해줍니다.
다다익선이지! 하고 buffersize를 불필요하게 많이 잡으면 메모리 낭비가 될 수 있으므로 적당한 양만큼을 정하는 게 좋습니다.
앞에서 살펴본 subject와 다르게 init이 아닌 create 타입 메서드를 사용해서 인스턴스를 생성합니다.
주로 사용하는 메서드는 create(bufferSize:)입니다.
여기서 파라미터로 이벤트를 저장할 size를 결정합니다.
Observer가 추가된 이후에 이벤트를 방출하면 잘 전달되는 것을 확인할 수 있습니다.
그리고 이후에 구독을 추가한다면 우리가 정한 size 개수만큼의 이벤트가 이후에 추가한 Observer에 전달되는것을 확인할 수 있습니다.
이후에 다른 이벤트를 전달해보았습니다.
지금은 bufferSize가 3으로 했기 때문에 가장 최근 이벤트 3개만 buffer에 저장합니다.
그래서 이후에 새롭게 추가된 Observer에게 1, 2, 3이 아니라 3, 4, 5가 전달되는 것을 확인할 수 있습니다.
이제 completed를 전달해보도록 하겠습니다.
Behavior와 다른 부분은 여기서 나타납니다.
Completd나 Error를 전달한 뒤에 Observer를 추가하면 buffer에 저장된 이벤트도 같이 전달된 후에 stop 하게 됩니다.
이 차이를 기억해야 합니다.
그런데 만약 dispose() 오퍼레이터를 호출해서 메모리에서 해제시킨다면
이미 dispose 됐다는 Error 이벤트가 전달되고 buffer에 저장된 데이터도 전달되지 않습니다.
왜 그럴까요??
그 이유는 dispose가 됐다는 의미는 관련된 데이터가 메모리에서 해제됐다는 의미이고 그럼 buffer도 함께 사라지기 때문입니다.
만약 dispose가 아닌 disposed(by:)를 사용하면 bag을 소유하고 있는 VC나 VM이 메모리에서 해제될 때 메모리가 정리되므로 위와 같은 일이 발생하지 않습니다.
Async Subject
completed가 전달된다면 그때 가장 마지막에 방출했던 이벤트를 방출합니다.
그전까지는 아무것도 방출하지 않습니다.
인스턴스 생성은 init()으로 합니다.
그런데 생성을 하고 Observer를 추가한 뒤에 이벤트를 방출시켰는데 이벤트가 전달되지 않았습니다.
이렇게 Completed 이벤트가 전달되고 나서야 가장 최근에 방출됐던 이벤트 1개를 방출하고 Completed 이벤트를 방출합니다.
만약 Completed대신 Error가 전달되면 Next 이벤트를 방출하지 않고 바로 Error를 방출하고 종료됩니다.
Async subject는 잘 사용하지 않으니 이런 게 있구나 하고 넘어가시면 됩니다.
Relay
Relay도 subject와 같이 Observable이면서 Observer입니다.
Relay 코드를 보면 내부적으로 Subject를 가지고 있습니다.
Subject와 Relay의 가장 큰 차이점은 바로 Relay는 Next 이벤트만 전달한다는 점입니다.
Compelted, Error 이벤트는 전달하지 않습니다.
이렇게 코드를 살펴보면 subject를 프로퍼티로 가지고 있는 것을 확인할 수 있습니다.
그리고 subject에서는 on(_:)을 이용해서 이벤트를 전달했지만 Relay에서는 accept(_:)로 전달합니다.
accept(_:)는 내부적으로 onNext를 호출해서 Next이벤트만 전달하는 것을 확인할 수 있습니다.
Relay는 RxSwift에서 제공하는 게 아니라 RxRelay에서 제공해주는 객체입니다. RxRelay는 RxCocoa에 속해 있기 때문에 RxRelay를 사용하기 위해서는 RxRelay 또는 RxCocoa를 import 해주어야 합니다.
RxRelay에서는 3가지 Relay를 제공합니다.
- Publish Relay
- Behavior Relay
- Replay Relay
Async Relay는 존재하지 않습니다.
Next 이벤트만 받고 나머지 이벤트는 받지 않는다고 했습니다.
그래서 종료가 없으니 주로 UI와 관련된 데이터를 처리할 때 Relay를 사용합니다.
그 이유는 만약에 Completed나 Error로 seqence가 종료되면 더 이상 UI 업데이트는 이루어지지 않겠죠?
이러한 상황은 앱 사용자 입장에서 그냥 앱이 작동하지 않는 것으로밖에 보이지 않습니다.
그렇기 때문에 UI와 관련이 되어있다면 dispose 되지 않는 이상 종료되지 않는 Relay를 주로 사용합니다.
Publish Relay
세부사항들은 Publish Subject와 똑같습니다.
차이점은 이벤트를 전달할 때 on(_:)이 아니라 accept(_:)를 사용합니다.
accept(_:)는 내부적으로 onNext()를 호출하므로 Completed나 Error 이벤트를 전달하고 싶어도 전달하지 못합니다.
Behavior Relay
Behavior Relay도 Behavior Subject와 다를 게 없습니다.
대신 Relay이므로 accept로 이벤트를 전달하고 Error, Completed 이벤트는 전달하지 않습니다.
Replay Relay
Replay Relay도 다를게 없습니다.
참고 자료
https://www.raywenderlich.com/books/rxswift-reactive-programming-with-swift
728x90'Programming > RxSwift' 카테고리의 다른 글
RxSwift - Trait (0) 2022.03.24 RxSwift - Observable factory (0) 2022.03.21 RxSwift - Disposable (0) 2022.03.17 RxSwift - Subscribe (0) 2022.03.17 RxSwift - Observable (0) 2022.03.05