ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RxSwift - Subject란? (PublishSubject, Behavior Subject, Replay Subject, Variable)
    Ray Wenderlich/RxSwift 2018. 2. 14. 14:06


    이번 포스트는 코드로 시작해보자 (바로 이해할 필요는 없음)

    PublishSubject는 데이터를 전달받아 subscriber들에게 뿌려주는 역할을 한다. 마치 신문사(newspaper publisher)에서 하는 Publish같은 느낌임

    이 코드는 수행해도 print는 되지 않는다. 이유는 subscriber가 없기 때문에!


    그럼 subject를 subscribe하는 코드를 코드를 넣어보자.

    subject에서 next이벤트가 발생하면 string을 출력해주고 있다.

    이 코드를 넣어도 마찬가지로 print문은 수행되지 않는다.

    왜냐하면 PublishSubject는 현재 나를 subscribe하는 subscriber들에게만 이벤트를 emit하기 때문이다.

    즉, next이벤트가 발생한 뒤에 subscribe한 subscriber에게는 이벤트가 전달되지 않는다.


    그럼 아래 코드를 추가해보자.


    이번엔 print문이 수행될 것이다.


    on(.next(_))대신 onNext(_)로 축약하여 사용할 수도 있다.

    코드로 살짝 맛을 봤으니 Subject에 대해서 알아보자!!!



    Subject란??

    Subject는 Observable과 Observer역할을 동시에 수행한다. 

    위 예제에서 이벤트를 발생하고 subject를 subscribe하는 과정을 살펴보았다.  -> subject객체가 next이벤트를 전달받아 subscriber에게 전달하고 있는 과정


    Subject는 총 4가지가 존재한다.

    - PublishSubject: 위에서 봤던 subject. Element없이 빈 상태로 생성되고, subscriber는 subscribe한 시점 이후에 발생되는 이벤트만 전달받는다.

    - BehaviorSubject: PublishSubject와 유사하다. 차이점은 반드시 초기 값을 가지고 생성된다. 

                                   subscribe가 발생하면, 발생한 시점 이전에 발생한 이벤트 중 가장 최신의 이벤트를 전달받는다.

    - ReplaySubject: BufferSize와 함께 생성된다. BehaviorSubject와 유사하지만, BufferSize만큼의 최신 이벤트를 전달받는다.

    - Variable: BehaviorSubject의 Wrapper라고 보면 된다. BehaviorSubject처럼 작동하며, 더 쉽게 사용하기 위해 만들어졌다.


    위 4가지의 Subject에 대해서 차례대로 알아보자.



    Working with publish subjects

    PublishSubject는 Subscriber가 새로운 이벤트만 전달받으면 되는 경우에 사용하기 좋다. 즉, Subscribe 이전에 발생했던 이벤트는 전혀 몰라도 되는 경우.

    아래 그림을 보면서 PublishSubject를 더 쉽게 이해해보자.

    - 1번 시퀀스에서 (1)이벤트를 발생한다. 하지만 subscriber가 없으므로 이벤트는 emit되지 않는다.

    - 2번 시퀀스가 1번 시퀀스를 subscribe한다. 그리고 (2)이벤트가 발생하고 2번 시퀀스는 이벤트를 전달 받는다.

    - 3번 시퀀스가 1번 시퀀스를 subscribe한다. 그리고 (3)이벤트가 발생하고 2번 시퀀스와 3번 시퀀스는 이벤트를 전달 받는다.


    위 그림과 관련하여 코드를 보자. (포스트 상단에 있는 코드와 이어지는 코드임)

    1. complete이벤트를 발생시킨다. 더 이상 subject는 이벤트를 발생시키지 않을 것이다.

    2. 당연히 "5" 이벤트는 emit되지 않는다.

    3. subscription이 끝나면 dispose하는 건 필수!!

    4. complete되어 끝난 시퀀스를  subscribe했다? 어떤 일이 일어날까?


    이미 종료된 시퀀스를 subscribe를 했음에도 불구하고 complete이벤트가 emit되었다.


    모든 Subject는 complete또는 error이벤트로 인해 종료된 이후 발생한 subscribe에 해당 event를 다시 한번 emit한다.

    위에선 complete로 종료된 시퀀스이기 때문에, 새로운 subscriber에게 complete 이벤트를 전달해 주었다. (주석 //4 부분)

    만약 error로 종료된 시퀀스였다면 error이벤트가 전달 되었을 것이다.


    PublishSubject는 시간의 흐름과 관련된 모델에 적합하다.

    경매사이트를 예를 들어보자.

    10시에 경매가 종료되기 때문에, 9시 59분에 '1분 뒤 경매가 종료됩니다.'라는 알림을 준다고 할 때,

    10시 1분에 접속한 사용자는 이 정보를 알 필요가 없다.


    경매사이트와 다른 모델에서는 이미 지나간 알림이 필요할 경우도 있겠다.

    그 때, 사용하는 Subject가 BehaviorSubject다.



    Working with behavior subjects

    Behavior Subject와 Publish Subject의 차이점은 딱!! 하나다.

    PublishSubject를 subscribe하면 subscibe이전에 발생했던 이벤트는 전달받을 수 없다.  (complete, error이벤트는 emit하는걸 위에서 확인했습니다.)

    하지만 BehaviorSubject를 subscribe하면 가장 최신 next이벤트를 하나 전달받을 수 있다. (끝이에요! 간단하죠?)


    그림을 통해 이해해보자.

    위에서 본 Publish Subject와 상당히 유사하지만, Subscriber가 발생하는 즉시, 가장 최근에 발생한 이벤트 하나를 emit하고 있다.

    - 2번이 1번을 subscribe하는 즉시, (1)이벤트가 전달된다. Publish Subject였다면 (1)이벤트는 전달되지 않았을 것이다.

    - 3번이 2번을 subscribe하는 즉시, (2)이벤트가 전달된다.

    - (3)이벤트가 발생하는 시점에는 2번과 3번 모두 이미 subscribe하고 있는 상태이기 때문에, 잘 전달된다. 


    코드를 통해 이해해보자


    BehaviorSubject는 subscription이 생성됨과 동시에 반드시 최신 이벤트를 emit해야 한다. 그래서 이벤트가 없는 Subject를 생성할 수 없다. 반드시 초기 값을 설정해야만 한다!



    아래 코드를 추가해보자

    subject를 생성하고 어떤 이벤트도 추가하지 않았다. 그 상태로 subscribe가 발생한 상황이다.

    이 코드의 결과는 아래와 같다. 초기값을 그대로 emit한다.


    그렇다면 subscirbe가 발생하기 전에 아래 코드를 넣어주면 어떻게 될까?


    subject에 ("Initial Value") - ("X") 2개의 이벤트가 추가된 상태로 subscription이 발생했기 때문에, 결과는 아래와 같다.


    예제코드 마지막에 아래 코드를 추가해보겠다.

    subject가 error이벤트를 emit했고, 그 상태에서 새로운 subscription이 발생했다.

    결과를 한 번 예상해보자.


    (전체 코드는 아래와 같습니다. 코드를 추가했다 추가했다 이래서 알아보기 힘듬)


    결과는 아래와 같다.

    Behavior Subject는 새로운 Subscriber가 최신 이벤트를 알아야 하는 경우에 사용하면 좋다. (너무 당연한 소리?ㅎㅎ)


    예를 들어, 사용자 프로필 화면에 들어오면 가장 최근에 수정된 프로필을 보여주어야 한다.

    프로필 화면에 진입하면 가장 최근에 수정된 프로필 하나면 충분하다.


    다음은, 검색화면을 예로 들어보자 내가 최근에 검색한 단어 5개를 알고 싶다면??

    Behavior Subject를 사용하면 1개밖에 알 수 없기 때문에 사용하지 않는것이 좋다.

    이 때 적합한 Subject가 Replay Subject다.


    Working with replay subjects

    Replay Subject는 최신 이벤트를 여러개 캐싱하고 있다가, 새로운 Subscriber에게 한 번에 전달한다. 몇 개의 이벤트를 캐싱할 것인지는 직접 설정할 수 있다.


    아래 그림을 보자.

    그림을 보면 2번은 이미 1번을 Subscribe하고 있는 상태고, 중간에 3번이 1번을 Subscribe하고 있다.

    1번은 버퍼 사이즈가 2인 Replay Subject이고, (1), (2)에 해당하는 이벤트가 있다. Subscriber인 2에게는 이벤트가 발생하는 즉시 전달되고 있다.

    3번은 (1), (2) 이벤트 발생이 끝난 뒤에 Subscribe를 시작한다. Subscribe를 함과 동시에 (1), (2) 이벤트를 모두 전달받았다.


    캐싱을 사용할 때는, 메모리 이슈를 빼놓을 수 없다.

    메모리 이슈를 고려하여 적절한 버퍼 사이즈를 정해야 하며, 이미지 같은 대용량 데이터를 캐싱할 땐, 더욱 더 조심해야 한다.

    Element타입이 배열이라면, 마찬가지로 주의해야할 것이다.


    그럼 코드를 살펴보자.

    1. 버퍼의 크기가 2인 ReplaySubject를 생성했다. String이벤트를 취급하는 Subject로 선언되었다.

    2. ReplaySubject에 바로 이벤트 3개가 추가되었다.

    3. 2개의 Subscription을 생성했다.


    이 코드의 결과는 어떻게 될까?

    Subscriber에게는 최신의 이벤트 2개가 전달되므로 아래와 같을 것이다.


    아래 코드를 추가해보자.

    Subject에 이벤트 "4"가 추가되었고, 3번째 Subscription이 생성되었다. 

    위에서 생성한 2개의 Subscription은 당연히 "4"이벤트를 전달 받을 것이고, 새로 생성한 Subscriber는 최근 2개의 이벤트 "3", "4"를 전달받을 것으로 예상된다.


    결과는 아래와 같다.


    이제 좀 특별한 상황을 만들어보자.

    "4" 이벤트를 생성한 코드 (subject.onNext("4")) 바로 아래에 이 코드를 넣어보자.


    결과는 어떻게 될까? 조금 놀라울 수 있다.


    아니! subject가 error이벤트로 종료되었음에도 불구하고 여전히 "3", "4"이벤트가 전달되고 있다. 

    Subject가 종료되었을지언정, 버퍼에는 여전히 데이터가 남아있기 때문에, Subscriber에게 전달해줄 수 있는 것이다.


    Publish, Behavior, Replay. 총 3개의 Subject를 알아보았다.

    이 3개면 대부분의 모델을 구현할 수 있을 것이다.



    Working with variables

    Variable은 BehaviorSubject를 랩핑하는 클래스이며, value라는 프로퍼티를 갖는다.

    value프로퍼티를 통해서 Subject의 현재 값을 얻을 수 있고 value프로퍼티를 통해 새로운 값을 추가할 수 있다. 즉, onNext(_:)메소드를 사용하지 않는다.


    BehaviorSubject를 랩핑하는 만큼, Variable을 생성할 때는 항상 초기 값이 있어야 한다.  Variable에 asObservable()을 사용하면 Subject에 접근할 수 있다.


    또 Variable이 Subject와 다른점이 있다.

    Variable은 error를 emit하지 않는다. 따라서, Variable을 subscribe할 때는, error이벤트를 핸들링할 필요가 없다.

    또한 Variable이 메모리에서 해제될 때, 자동으로 complete이벤트를 발생시키기 때문에 별도로 complete이벤트를 추가할 필요도 없다.

    (실제로 error, complete이벤트를 발생시키는 것도 불가능함)


    Variable을 코드를 통해 살펴보자.

    1. Variable을 하나 만들었다. Variable<String>("Initial value")라고 작성한 것과 같다. 타입이 추론되기 때문에 <String>은 생략가능한 것이다.

    2. variable에 새 Element를 추가해준다.

    3. variable을 subscribe한다. variable자체는 subject가 아니기 때문에, asObservable()을 사용하여 subject에 접근한 뒤 subscribe하고 있다.


    결과는 아래와 같다. subscribe와 동시에 최신 element를 얻을 수 있다.


    아래 코드를 추가해보자.

    1. variable에 새로운 Element를 추가해준다.

    2. variable에 새로운 subscription이 생성되었다.

    3. variable에 Element를 하나 더 추가해준다.


    결과는 아래와 같다.


    위에서 설명했지만, variable에는 error나 complete이벤트를 추가할 수 없다.

    추가하기 위해 아래 코드와 같은 시도를 할 수 있겠지만, 모두 컴파일 에러를 발생시킨다.

Designed by Tistory.