rxjs-dev.firebaseapp.com/guide/subject
Subject는 특수한 종류의 Observable이자 Observer이다.
뭐가 특별한가? 값을 여러 Observer에게 멀티캐스팅해줄 수 있는 류의 옵저버블이다.
아래는 가장 간단하게 subject를 생성해본 것이다.
const subject$ = new Subject();
const obs = {
next: (x) => console.log(x),
error: () => console.log(error),
complete: () => console.log("done"),
};
const subOne = subject$.subscribe(obs);
subject$.next(1);
const subTwo = subject$.subscribe(obs);
subject$.next(2);
subject$.complete();
// output
1
2 2
done done
위와 같은 단순한 Subject 외에 특별한 류의 Subject가 있습니다.
BehaviorSubject, ReplaySubject, and AsyncSubject.
1) BehaviorSubject
"최종값"을 기억했다가 옵저버에게 전달합니다.
말 장난같지만 최종값이란게 최초값이 될 수도 있습니다. 최초값주고 아무 동작 안하면 최초값이 최종값이 되는거니까요.
In the following example, the BehaviorSubject is initialized with the value 0 which the first Observer receives when it subscribes. The second Observer receives the value 2 even though it subscribed after the value 2 was sent.
이는 두 가지 의미가 있는데
1) 초기값을 설정해서 옵저버에서 초기값을 전달할 수 있습니다.
2) 옵저버를 추가하면, 마지막 값을 기반으로 자동으로 트리거 됩니다.
이게 뭔 말이냐구요? 한 번 코드로 보시죠.
import { BehaviorSubject } from 'rxjs';
const subject = new BehaviorSubject(0); // 0 is the initial value
subject.subscribe({
next: v => console.log(`observerA: ${v}`),
});
console.log('==========1==========');
subject.next(1);
console.log('==========2==========');
subject.next(2);
subject.subscribe({
next: v => console.log(`observerB: ${v}`),
});
console.log('==========3==========');
subject.next(3);
출력 결과는 아래와 같습니다.
observerA: 0
==========1==========
observerA: 1
==========2==========
observerA: 2
observerB: 2
==========3==========
observerA: 3
observerB: 3
즉, observer A는 초기값 0이 설정된 이후에 설정되었음에도 초기값을 가지고 트리거가 되며
observer B는 2라는 값이 주어진 이후에 설정되었음에도 2라는 값을 가지고 트리거가 됩니다.
2) ReplaySubject
subject의 별미같은 녀석입니다. 좀 독특하죠.
옵저버가 구독한 시점과 상관없이, 전체 방출된 값들을 전부 replay합니다.
import { ReplaySubject } from 'rxjs';
const subject = new ReplaySubject();
subject.subscribe({
next: v => console.log(`observerA: ${v}`),
});
subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);
console.log('옵저버 B가 생겼으니 1, 2, 3, 4전부 replay ');
subject.subscribe({
next: v => console.log(`observerB: ${v}`),
});
console.log('여기서부턴 다시 정상적으로');
subject.next(5);
observerA: 1
observerA: 2
observerA: 3
observerA: 4
옵저버 B가 생겼으니 1, 2, 3, 4전부 replay
observerB: 1
observerB: 2
observerB: 3
observerB: 4
여기서부턴 다시 정상적으로
observerA: 5
observerB: 5
그런데 상식적으로, 방출된 값을 무한히 저장할 수도 없을테고, 메모리 문제가 생길 것이라고 예상할 수 있습니다.
이럴 경우에는 애초에 생성할 때 3가지 값만 저장하겠다고 ReplaySubject를 생성할 때 지정하면 됩니다.!
const subject = new ReplaySubject(3); // buffer 3 values for new subscribers
버퍼의 크기 뿐만 아니라, 시간 제한도 둘 수 있습니다. 아래 코드는 100만큼의 버퍼를 한계로 하되, 시간은 500ms로 가져갑니다.
500ms 지난 값은 버리겠다는 말이죠.
const subject = new ReplaySubject(100, 500 /* windowTime */);
3) AsyncSubject
의외로 간단한 subject입니다.
옵저버에게 전달된 마지막 값이 complete되었을 때만 딱 한 번 실행됩니다.
import { AsyncSubject } from 'rxjs';
const subject = new AsyncSubject();
subject.subscribe({
next: v => console.log(`observerA: ${v}`),
});
// observerA 무반응
subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);
subject.subscribe({
next: v => console.log(`observerB: ${v}`),
});
subject.next(5);
console.log('이제 complete할거임. 마지막 값이 5를 가지고 이제 출력 시작함');
subject.complete();
이제 complete할거임. 마지막 값이 5를 가지고 이제 출력 시작함
observerA: 5
observerB: 5