흐름:
1. my 컴포넌트 마운트, 서비스에 있는 Subject 변수에 값을 저장 →
2. my컴포넌트에서 routerLink='./farms' 이동 →
3. farms컴포넌트 init →
4. share에 있는 Subject를 구독해 값을 받아오려 했으나 구독자체를 실행하지 않는 상황.
여기서 Subject는 'asObservable()'로 모든 컴포넌트가 같은 값을 받아볼 수 있도록 했다.
// 서비스
@Injectable({
providedIn: 'root'
})
export class UserFarmService {
_userFarms = new Subject();
get userFarms$(): Observable<any> {
return this._userFarms.asObservable();
}
constructor(
private _apiService: ApiService
){}
setUserFarms(usrIdx){
this._apiService.req('farm/list', { usrIdx }).subscribe(result => {
this._userFarms.next(result);
})
}
}
// 컴포넌트
@Component({
selector: 'farms',
templateUrl: './farms.component.html',
styleUrls: ['./farms.component.scss']
})
export class FarmsComponent implements OnInit {
constructor(
private _userFarmService: UserFarmService
){}
ngOnInit(){
this._userFarmService.userFarms$.subscribe(result => {
console.log('RESULT: '. result);
})
}
}
그런데 왜 'userFarms$'를 구독 실행하지 않지??
흠... Subject로 실험을 해보았다
// case #1
const { Subject, BehaviorSubject } = rxjs
const subject = new Subject()
subject.next(2)
subject.subscribe(x => console.log('바로구독: ' + x))
setTimeout(_ => {
subject.subscribe(x => console.log('3초 후 구독: ' + x))
}, 3000)
위와 같이 Subject를 변수에 할달하고 바로 2라는 값을 발행했을때 그 다음줄에 있는 subscsribe가 실행되지 않는다. setTimeout에 있는 subscribe 또한 실행되지 않는다.
하지만 발행(next)과 구독(subscribe)의 순서를 바꾸면
// case #2
const { Subject, BehaviorSubject } = rxjs
const subject = new Subject()
subject.subscribe(x => console.log('바로구독: ' + x))
subject.next(2)
setTimeout(_ => {
subject.subscribe(x => console.log('3초 후 구독: ' + x))
}, 3000)
첫 번째 콘솔로그에서 Subject를 구독해 값이 읽혔다. 하지만 setTimeout에 있는 구독은 실행되지 않았다.
하지만 setTimeout 뒤에서 값을 발행시켜도 setTimeout 안에 있는 구독은 실행되지 않았다.
여기서 알 수 있는 점은,
1. Subject인 변수의 값을 읽기 위해서는 Subject에 값을 발행하기전에 구독을 미리 하고 있어야 한다. 즉, 구독자가 있는 상태에서 값을 발행해야 한다는 말이다.
그렇다면 setTimeout 뒤에서 값을 발행한다면 어떨까? 해보았는데 역시나 setTimeout 안에서는 구독이 실행되지 않는다.
// case #3
const { Subject, BehaviorSubject } = rxjs
const subject = new Subject()
setTimeout(_ => {
subject.next(1)
}, 4000)
subject.subscribe(x => console.log('바로구독: ' + x))
setTimeout(_ => {
subject.subscribe(x => console.log('3초 후 구독: ' + x))
}, 3000)
※ case#1을 이해하기 위해서는 브라우저의 이벤트 루프와 stack / queue의 이해도가 필요하다. 처리순서를 나열해보자면...
1. const subject = new Subject(), 두 개의 setTimeout 초기화
2. 첫 번째 subscribe 가 즉시 구독
3. 첫 번째 setTimeout이 task queue에 추가
4. 이벤트 루프에 의해 callback stack에 첫 번째 setTimeout에 쌓이고, 스코프 안에 있는 두 번째 subscribe가 추가되면서 구독 시작
5. 4000ms, setTimeout이 마지막으로 task queue를 거쳐 callback stack에 쌓이고 subject.next(1)가 호출된 뒤 구독자들에게 값을 전달한다.
다시말해 구독자가 있는 상태에서 발행물을 발행해야 구독자가 발행물을 구독할 수 있다!
그렇다면 위 상황을 다시 한번 정리해 보면...
1. my 컴포넌트 마운트, 서비스에 있는 Subject에 값을 저장 →
발행완료 (구독자: 0)
2. my컴포넌트에서 routerLink='./farms' 이동 →
발행물 있음
3. farms컴포넌트 init →
발행물 있음 (구독자 한 명이 생겼다.)
4. share에 있는 Subject를 구독해
발행물 있음 (구독자: 1)
이제 보니 구독자가 없는 상태에서 발행물을 발행했기 때문이었던 것!
이 상황은 단순히 BehaviorSubject로 바꿔주면 된다.
'RxJS(Reactive X)' 카테고리의 다른 글
[스트림 결합] 실전 사용 모음 (0) | 2024.03.13 |
---|---|
(기록용) 결합(combine/join)과 관련된 샘플 코드/상황 설명 (0) | 2023.12.08 |
servie.ts 에서 모든 구독자에게 동일한 값을 발행할때 (0) | 2023.07.27 |
[RxJS] 실습 - 스마트한 키워드 검색창 만들기 (0) | 2023.01.16 |
[Chapter#2] RxJS 연산자들 -ing (0) | 2023.01.13 |
댓글