아직은 개린이

[RxSwift] Subject 본문

Swift + iOS/RxSwift

[RxSwift] Subject

jiyeonlab 2021. 10. 11. 22:12
Observables are a fundamental part of RxSwift, but they're essentially read-only. You may only subscribe to them to get notified of new events they produce.
A common need when developing apps is to manually add new values onto an observable during runtime to emit to subscribers. What you want is something that can act as both an observable and as an observer. That something is called a Subject.

 

Raywenderich의 Subjects 파트의 첫 문단이다. 일반적으로 앱 개발을 할 때 observable에 어떤 값을 추가하고 subscriber에게 값을 방출해야하는데, 이 때 observable과 observer 두가지 기능이 모두 필요하고 이 역할을 하는 것이 subject라고 한다. 이게 무슨 뜻일까... 좀 더 알아보자.

Subject = Observable + Observer

 

 

Subject가 next 이벤트를 받아 subscribe에게 전달하고 있는 예제를 살펴보자.

PublishSubject는 신문 출판사처럼 데이터를 받아 구독자들에게 전달하는 역할을 한다. 위 코드에서 2번째 라인까지는 print 되지 않는다. 이유는 subscriber가 없기 때문!

4번째 라인에서 subject를 subscribe하고 next 이벤트가 발생하면 string을 출력하도록 했지만, 역시나 이 때까지 콘솔에 찍히지 않는다. next 이벤트가 발생한 후에 subscribe를 하면 이벤트가 전달되지 않기 때문이다. 따라서 8번째 라인을 추가하면, 그제서야 print문이 수행된다.

Observable과 뭐가 다르지?

흠.. 여기까지 읽은 후의 질문은 Observable이 있는데, 왜 Subject라는 애가 또 있어야하는가이다.......

 

Observable이나 Subject나 모두 subscribe가 가능하다. 하지만, 큰 차이점은 여러 observer를 구독할 수 있느냐 없느냐의 차이이다. 

Observable과 Subject

Observable은 unicast 방식으로 하나의 observer만 구독할 수 있고, Subject는 multicast 방식으로 여러 observer를 구독할 수 있다고 한다. 실질적으로 사용되는 예제를 많이 살펴봐야 이해가 될 것 같다...... 이 글의 내용도 참고해보자... (으려워...)

일단 Subject 종류를 살펴보자

RxSwift에는 PublishSubject, BehaviorSubject, ReplaySubject, AsyncSubject 이렇게 4가지 subject가 있다고한다.

하나씩 살펴보자.

 

  1. PublishSubject
    - 빈 상태로 생성되고, 새로운 element가 subscribe 이후의 event만 subscriber에게 전달된다.
  2. BehaviorSubject
    - PublishSubject와 달리 기본값을 가진 채로 생성되고, 초기값 혹은 최신 element 값을 방출한다.
  3. ReplaySubject
    - 정해진 buffer size를 가진 채로 생성되고, 새로운 구독자에게 방출한다.
  4. AsyncSubject
    - completed 이벤트가 방출될 때만 마지막 next 이벤트를 방출한다. (많이 사용하지 않는 subject라서 raywenderich 강의의 책에서는 다루지 않을 것이라는 내용이 있다)

Publish Subject

PublishSubjects는 가장 기본적인 Subject이다. 구독을 시작하는 순간부터 이벤트를 구독자에게 전달하기 때문에, subscribe 이전에 일어난 이벤트를 몰라도 되는 경우에 사용하기 좋다.

Publish Subject

첫번째 줄은 Publish Subject이고, 두세번째 줄은 subscriber에 해당한다. 위로 가는 점선은 subscribe, 아래로 향하는 점선은 emit을 뜻한다.

 

첫번째 subscribe는 1번 이후에 구독을 시작했기 때문에 1 이벤트는 받지 못하고, 2와 3 이벤트만 받았다. 두번째 subscribe는 2번 이후에 구독을 시작하여 3 이벤트만 받았다고 보면 된다.

 

코드로 살펴보자.

 

  • check 1 부분에서 completed 이벤트를 발생시켜, subject가 더이상 이벤트를 방출하지 못하도록 종료시켰다.
  • 따라서, check 2 의 "5" 이벤트는 방출되지 않을 것이다. check 3에서 dispose까지 했다
  • 그런데 check 4에서 이미 종료된 subject를 subscribe했다. 어떻게 될까?

일단 위 코드의 전체 출력 결과는 아래와 같다.

// 출력 결과
1
2
3
2) 3
2) 4
2) completed
3) completed

"3"의 경우 subscriptionOne과 subscriptionTwo에 의해 2번 출력되는 것을 볼 수 있고, "5"의 경우 subject가 completed 된 후라 출력되지 않는다. 마지막 2라인이 종료된 subject를 subscribe하여 출력된 결과이다. Subject는 종료된 후에도 새로운 subscriber에게 자신이 종료되었음을 알려준다. 

 

위처럼 Publish Subject는 시간의 흐름과 관련된 시스템에 적합하다고 한다. (ex. 실시간 경매시스템)

Behavior Subject

Behavior Subject는 Publish Subject와 유사한데, 새로운 구독자에게 가장 최근의 .next 이벤트도 방출한다는 차이점이 있다.

Behavior Subject

첫번째 줄은 subject이고, 두번째 줄은 첫번째 subscriber, 세번째 줄은 두번째 subscriber이다.

첫번째 subscriber는 1 element가 방출된 이후에 구독을 시작했지만 가장 최근에 방출된 element인 1부터 받는다. 마찬가지로 두번째 subscribers는 2 element가 방출된 이후에 구독을 시작했지만 2 element부터 받기 시작한다.

 

Behavior Subjects는 subscribe가 시작됨과 동시에 최근의 element를 방출하기 때문에 subject가 생성되는 당시에 무조건 초기값을 설정해야한다는 특징이 있다.

 

코드로 살펴보자.

 

  • 만약 12번째 라인이 없었다면, "1 : Initial value" 가 출력되었을 것이다.
  • 하지만 13번째 라인에서 구독을 하기 전에 12번째 라인에서 이벤트를 추가했기 때문에 "1: X"가 출력된다.
  • 또한, 19번째 라인에서 subject에 error 이벤트를 추가하면, error는 두 개의 subscriber에게 모두 출력된다.

즉. 위 코드의 출력은 아래와 같을 것이다.

// 출력결과
1) X
1) anError
2) anError

 

Behavior Subject는 최신 데이터로 화면을 그리는 경우에 유용하다고 한다. 예를 들어, 유저의 프로필 정보 페이지를 Behavior Subject에 연결하면, 프로필 정보가 바뀔 때마다 새로운 정보를 패치할 수 있는 것이다.

Replay Subject

Replay subject는 생성 시 설정한 크기의 버퍼에 자신이 최근 방출한 element를 저장하고, 버퍼에 있는 element를 구독자들에게 방출한다.

Replay Subject

첫번째 줄은 subject이고 버퍼 사이즈는 2이다. 두번째 줄의 구독자는 이미 subject를 subscribe하고 있어 방출하는 모든 element를 받는다. 세번째 줄의 구독자는 element 2가 방출된 후에 구독을 시작했지만 버퍼에 저장된 1, 2를 모두 받고 있다. 

이러한 버퍼들은 메모리에 저장되어있기 때문에 주의해야한다는 특이점이 있다.

 

  • buffer size를 2로 가지는 subject를 만든 후 1, 2, 3 세 개의 요소를 subject에 추가한다
  • 이 subject에 대해 2개의 구독자를 추가한다.

이 경우, 버퍼 사이즈가 2이기 때문에 최신으로 추가된 2와 3만 출력된다.

// 출력 결과
1) 2
1) 3
2) 2
2) 3

아래 코드를 추가하여, 출력을 확인해보자.

 

  • 처음의 두 구독자의 경우, 이미 구독을 하고 있는 상태라 4 element를 정상적으로 받는다.
  • 새로운 구독자의 경우, 버퍼 사이즈 2만큼의 최신 element를 받을 것이다.
    // 출력 결과
    1) 4
    2) 4
    3) 3
    3) 4​
  • 하지만. 9번째 라인을 추가함으로 인한 출력 결과는 다음과 같다.
    // 출력 결과
    1) 4
    2) 4
    1) anError
    2) anError
    3) 3
    3) 4
    3) anError​
    어떻게 이런 결과가 나왔을까? -> subject가 종료되었지만 버퍼에 3, 4가 있었기 때문에 3번째 subscriber는 3, 4, error를 받을 수 있었던 것이다.

Behavior Subject처럼 최근 값 외에 더 많은 것을 보여주고 싶을 때, 예를 들어 최근 5개의 검색 기록을 보여줘야하는 경우 Replay Subject를 사용하는 것이 적합하다고 한다.

Async Subject

Async subject는 completed 이벤트가 발생하기 전까지는 어떤 구독자에게도 이벤트를 전달하지 않는다. completed 이벤트가 발생하면 마지막으로 발생한 next 이벤트를 전달한다.

Async subject

 


Subject는 2회차 Rx 스터디 주제이다. 실제로 프로젝트 예제를 통해 앞서 정리했던 Observable과 이번에 정리한 Subject를 만들어봐야 더 와닿을 것 같다.. 아직까지는 퍼즐 조각들이 맞춰지지 않은 느낌.. 어렵다 Rx....


참고자료

- https://www.raywenderlich.com/books/rxswift-reactive-programming-with-swift/v4.0/chapters/3-subjects#toc-chapter-007-anchor-001

- http://reactivex.io/documentation/ko/subject.html   

- https://arnoldyoo.tistory.com/18    

- https://sujinnaljin.medium.com/rxswift-subject-99b401e5d2e5

'Swift + iOS > RxSwift' 카테고리의 다른 글

[RxSwift] Observable (2)  (0) 2021.10.11
[RxSwift] Observable (1)  (0) 2021.10.05
Reactive Programming, Rx  (0) 2021.10.03