728x90
Dart의 Stream (비동기 데이터 스트림)
Dart에서 Stream은 비동기 데이터의 연속적인 흐름을 처리하는 기능입니다.
- Future는 단일 비동기 결과를 반환하지만,
- Stream은 여러 개의 비동기 데이터를 순차적으로 전달할 수 있습니다.
Stream이 필요한 경우
- 네트워크 요청을 통해 실시간 데이터 수신 (예: WebSocket)
- 센서 데이터 처리 (예: GPS, 가속도 센서)
- 버튼 클릭 이벤트 감지
- 파일 다운로드 진행 상황 모니터링
- 오디오/비디오 스트리밍 데이터 처리
1. Stream 기본 개념
- 비동기적으로 여러 개의 데이터를 순차적으로 처리
- 이벤트 기반으로 동작하며 데이터가 발생할 때마다 수신
- listen()을 사용하여 구독(subscribe)
- 한 번만 사용할 수도 있고, 여러 구독자(multicast)와 공유 가능
- await for를 사용하여 데이터를 동기 방식으로 처리 가능
2. Stream 생성 및 데이터 수신
2.1. 기본적인 Stream 생성
Stream<int> numberStream() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i; // 1초마다 숫자 전송
}
}
void main() async {
print("스트림 시작...");
await for (int value in numberStream()) {
print("받은 값: $value");
}
print("스트림 종료!");
}
스트림 시작...
(1초 후) 받은 값: 1
(1초 후) 받은 값: 2
(1초 후) 받은 값: 3
(1초 후) 받은 값: 4
(1초 후) 받은 값: 5
스트림 종료!
- yield를 사용하면 데이터를 순차적으로 전송 가능
- await for를 사용하면 Stream을 동기 방식처럼 처리 가능
2.2. listen()을 사용하여 Stream 수신
listen()을 사용하면 비동기적으로 Stream을 수신 가능합니다.
void main() {
Stream<int> stream = Stream.periodic(Duration(seconds: 1), (count) => count).take(5);
stream.listen(
(data) => print("받은 값: $data"),
onDone: () => print("스트림 종료!"),
);
}
(1초 후) 받은 값: 0
(1초 후) 받은 값: 1
(1초 후) 받은 값: 2
(1초 후) 받은 값: 3
(1초 후) 받은 값: 4
스트림 종료!
- listen()을 사용하면 Stream이 비동기적으로 실행되며, onDone을 통해 종료 감지 가능
3. StreamController를 사용한 동적 Stream 생성
StreamController를 사용하면 스트림을 동적으로 생성하고 데이터를 실시간으로 추가할 수 있습니다.
3.1. 기본 StreamController 사용
import 'dart:async';
void main() {
StreamController<String> controller = StreamController();
// 리스너 추가 (데이터 수신)
controller.stream.listen(
(data) => print("받은 데이터: $data"),
onDone: () => print("스트림 종료!"),
);
// 데이터 추가 (스트림에 이벤트 전송)
controller.sink.add("Hello");
controller.sink.add("Dart Stream");
controller.sink.add("Goodbye");
// 스트림 종료
controller.close();
}
받은 데이터: Hello
받은 데이터: Dart Stream
받은 데이터: Goodbye
스트림 종료!
- StreamController를 사용하면 스트림 데이터를 동적으로 추가 가능
- .sink.add()를 사용하여 실시간으로 데이터를 추가할 수 있음
- .close()를 호출하면 스트림이 종료됨
3.2. broadcast 스트림 (여러 구독자 지원)
일반 StreamController는 단일 구독자만 허용하지만, broadcast 스트림을 사용하면 여러 구독자가 동일한 스트림을 사용할 수 있습니다.
import 'dart:async';
void main() {
StreamController<int> controller = StreamController.broadcast();
// 여러 구독자 추가
controller.stream.listen((data) => print("구독자 1: $data"));
controller.stream.listen((data) => print("구독자 2: $data"));
// 데이터 추가
for (int i = 1; i <= 3; i++) {
controller.sink.add(i);
}
controller.close();
}
구독자 1: 1
구독자 2: 1
구독자 1: 2
구독자 2: 2
구독자 1: 3
구독자 2: 3
- broadcast 스트림을 사용하면 여러 개의 listen()을 등록할 수 있음
- 여러 개의 구독자가 동시에 스트림을 수신 가능
4. Stream 변환 및 필터링
4.1. map()을 사용한 데이터 변환
void main() {
Stream<int> numbers = Stream.periodic(Duration(seconds: 1), (x) => x + 1).take(5);
numbers.map((num) => "숫자: $num").listen(print);
}
숫자: 1
숫자: 2
숫자: 3
숫자: 4
숫자: 5
- map()을 사용하여 스트림 데이터를 변환 가능
4.2. where()을 사용한 데이터 필터링
void main() {
Stream<int> numbers = Stream.periodic(Duration(seconds: 1), (x) => x + 1).take(5);
numbers.where((num) => num.isEven).listen(print);
}
2
4
- 짝수만 필터링하여 출력 가능
4.3. take()와 skip()을 사용하여 데이터 제어
void main() {
Stream<int> numbers = Stream.periodic(Duration(seconds: 1), (x) => x + 1);
numbers.take(3).listen((data) => print("처음 3개: $data"));
numbers.skip(3).take(3).listen((data) => print("3개 건너뛰고: $data"));
}
- take(n): 처음 n개의 데이터만 가져옴
- skip(n): 처음 n개의 데이터를 건너뜀
5. Stream과 Future 비교
특징 | Future | Stream |
반환 값 | 한 번만 반환 | 여러 개 반환 가능 |
비동기 작업 | 단일 결과 | 지속적인 데이터 흐름 |
사용 방식 | .then() 또는 await | .listen() 또는 await for |
예제 | API 응답 | WebSocket, 이벤트 리스너 |
- Future는 한 번만 실행되는 비동기 작업에 적합
- Stream은 연속적인 데이터 처리에 적합
Stream 요약
기능 | 사용법 |
스트림 생성 | Stream.periodic(), Stream.fromIterable(), StreamController() |
데이터 수신 | listen(), await for |
데이터 변환 | map(), where(), take(), skip() |
여러 구독 지원 | StreamController.broadcast() |
스트림 수동 종료 | controller.close() |
728x90
'Flutter for Beginners' 카테고리의 다른 글
Dart 3.0의 레코드 (Records) (0) | 2025.03.04 |
---|---|
Dart Completer (커스텀 비동기 처리) (0) | 2025.03.04 |
Dart 비동기 프로그래밍 (0) | 2025.02.28 |
Dart 개발자 가이드 (코딩 스타일 & 협업 가이드) (0) | 2025.02.25 |
Dart: Generics, Static, Cascade Operator (0) | 2025.02.25 |