이번 포스팅은 Reactive Programming이 적용된 RxDart와 그걸 Flutter에 적용해보도록 하겠다.
Reactive Programming이란, 비동기 데이터 스트림을 이용한 프로그래밍하는 것을 말한다. 혹시 자세한 것을 알고 싶다면 포스팅해놓은 것을 참고하기 바란다.
버전 정보
Flutter 2.10.5 • channel stable • https://github.com/flutter/flutter.git
Tools • Dart 2.16.2 • DevTools 2.9.2
rxdart
실시간으로 데이터가 변하는 rx, dart에서 rx의 Subject를 3가지를 제공하고 있다.
- PublishSubject
- BehaviorSubject
- ReplaySubject
이렇게 3가지가 있다. 이 3가지 방법으로 데이터를 실시간으로 변하는 것을 관찰(Observe)한다. 각각의 기능에 대해서 설명하겠다. 아래의 글에 참고를 많이 하였고, 내가 부족한 부분을 추가해서 설명하도록 하겠다.
https://www.woolha.com/tutorials/rxdart-using-subject-publish-behavior-replay
PublishSubject
PublishSubject는 구독하는 시점으로부터 그 이후에 나타나는 이벤트에만 반응을 한다.
위와 같은 그림처럼 작동하는데, 이 전에 있던 데이터는 관찰을 할 수 없다. 사람이 Youtube에 채널을 구독을 했다고 한다면, 그 시점 이후에 나오는 동영상만 볼 수 있다는 뜻이다. 아래의 코드를 보면서 조금 더 이해해보자.
import 'package:rxdart/rxdart.dart';
void main() {
PublishSubject publishSubject = PublishSubject<int>();
publishSubject.stream.listen((value) {
print('PublishSubject Observer 1: $value');
});
publishSubject.add(1);
publishSubject.add(2);
publishSubject.stream.listen((value) {
print('PublishSubject Observer 2: $value');
});
publishSubject.add(3);
publishSubject.add(4);
publishSubject.close();
}
//결과 :
// PublishSubject Observer 1: 1
// PublishSubject Observer 2: 3
// PublishSubject Observer 1: 2
// PublishSubject Observer 2: 4
// PublishSubject Observer 1: 3
// PublishSubject Observer 1: 4
결과를 보다 보면 분명 이상한 점이 있을 것이다. 위에서 설명했던걸 생각하면, 결괏값은
import 'package:rxdart/rxdart.dart';
void main() {
PublishSubject publishSubject = PublishSubject<int>();
// Observer1 will receive all data and done events
publishSubject.stream.listen((value) {
print('PublishSubject Observer 1: $value');
});
publishSubject.add(1);
publishSubject.add(2);
// Observer2 will receive all data and done events
publishSubject.stream.listen((value) {
print('PublishSubject Observer 2: $value');
});
publishSubject.add(3);
publishSubject.stream.listen((value) {
print('PublishSubject Observer 3: $value');
});
publishSubject.add(4);
publishSubject.close();
}
// PublishSubject Observer 1: 1
// PublishSubject Observer 2: 3
// PublishSubject Observer 3: 4
// PublishSubject Observer 1: 2
// PublishSubject Observer 2: 4
// PublishSubject Observer 1: 3
// PublishSubject Observer 1: 4
BehaviorSubject
BehaviorSubject는 구독하는 시점 이 전의 값을 가져와서 시작한다.
BehaviorSubject는 직전에 있던 데이터부터 가져와서 관찰한다. 위와 같은 예시를 들자면, Youtube에 채널을 구독을 했다고 한다면, 그 직전에 나온 동영상부터 볼 수 있다는 뜻이다. 아래의 코드를 보면서 조금 더 이해해보자.
import 'package:rxdart/rxdart.dart';
void main() {
BehaviorSubject behaviorSubject = BehaviorSubject<int>();
behaviorSubject.stream.listen((value) {
print('BehaviorSubject Observer 1: $value');
});
behaviorSubject.add(1);
behaviorSubject.add(2);
behaviorSubject.stream.listen((value) {
print('BehaviorSubject Observer 2: $value');
});
behaviorSubject.add(3);
behaviorSubject.add(4);
behaviorSubject.close();
}
// BehaviorSubject Observer 1: 1
// BehaviorSubject Observer 2: 2
// BehaviorSubject Observer 2: 3
// BehaviorSubject Observer 1: 2
// BehaviorSubject Observer 2: 4
// BehaviorSubject Observer 1: 3
// BehaviorSubject Observer 1: 4
아까 위에서 봤던 것처럼 우선 구독을 다 하고 그 이후로 한 칸씩 이동한다고 생각하면 되는데, 다만 구독하는 시점 바로 뒷부분도 출력이 되는 것을 알 수 있다. 그래서 BehaviorSubject OBserver 1 : 1이 출력된 후, BehaviorSubject OBserver 1 : 2 rk dkslfk BehaviorSubject OBserver 2 : 2가 먼저 출력되는 것을 볼 수 있다.
import 'package:rxdart/rxdart.dart';
void main() {
BehaviorSubject behaviorSubject = BehaviorSubject<int>.seeded(100);
behaviorSubject.stream.listen((value) {
print('BehaviorSubject Observer 1: $value');
});
behaviorSubject.add(1);
behaviorSubject.add(2);
behaviorSubject.stream.listen((value) {
print('BehaviorSubject Observer 2: $value');
});
behaviorSubject.add(3);
behaviorSubject.stream.listen((value) {
print('BehaviorSubject Observer 3: $value');
});
behaviorSubject.add(4);
behaviorSubject.add(5);
behaviorSubject.close();
}
// BehaviorSubject Observer 1: 100
// BehaviorSubject Observer 1: 1
// BehaviorSubject Observer 2: 2
// BehaviorSubject Observer 2: 3
// BehaviorSubject Observer 3: 3
// BehaviorSubject Observer 3: 4
// BehaviorSubject Observer 1: 2
// BehaviorSubject Observer 2: 4
// BehaviorSubject Observer 3: 5
// BehaviorSubject Observer 1: 3
// BehaviorSubject Observer 2: 5
// BehaviorSubject Observer 1: 4
// BehaviorSubject Observer 1: 5
BehaviorSubject는 seeded라는 것으로 데이터 초기값을 설정해줄 수 있다. 그래서 내가 개인적으로 Flutter에서 Rx를 구현할 때 많이 쓴다.
ReplaySubject
ReplaySubject는 말 그대로 구독하는 순간 데이터를 모두 보게 된다.
코드 순서는 다를 수 있지만 모든 시점을 살필 수 있어서 사실 listen 하는 위치에 크게 영향받지 않는다고 생각하면 된다. 우리가 아는 Youtube의 구독처럼 구독하면 모든 동영상을 볼 수 있다고 생각하면 된다.
import 'package:rxdart/rxdart.dart';
void main() {
ReplaySubject replaySubject = ReplaySubject<int>();
replaySubject.stream.listen((value) {
print('ReplaySubject Observer 1: $value');
});
replaySubject.add(1);
replaySubject.add(2);
replaySubject.stream.listen((value) {
print('ReplaySubject Observer 2: $value');
});
replaySubject.add(3);
replaySubject.close();
}
// ReplaySubject Observer 1: 1
// ReplaySubject Observer 2: 1
// ReplaySubject Observer 2: 2
// ReplaySubject Observer 2: 3
// ReplaySubject Observer 1: 2
// ReplaySubject Observer 1: 3
Replay는 replaysubject.stream.listen 하는 순간 모든 add 되는 데이터를 보게 된다. 다만 모두 볼 수 없는 상황도 존재하는데 그건 최대 데이터를 저장하는 걸 제한할 때 발생한다.
import 'package:rxdart/rxdart.dart';
void main() {
ReplaySubject replaySubject = ReplaySubject<int>(maxSize: 1);
replaySubject.stream.listen((value) {
print('ReplaySubject Observer 1: $value');
});
replaySubject.add(1);
replaySubject.add(2);
replaySubject.add(3);
replaySubject.stream.listen((value) {
print('ReplaySubject Observer 2: $value');
});
replaySubject.close();
}
// ReplaySubject Observer 1: 1
// ReplaySubject Observer 2: 3
// ReplaySubject Observer 1: 2
// ReplaySubject Observer 1: 3
다음 포스팅은 이 rxdart를 가지고 stream으로 연결해서 위젯을 직접 커스텀해보도록 하겠다.
오류, 지적사항 그리고 궁금한 것은 댓글 부탁드립니다.