이번에는 Dart 프로그래밍 중 Stream에 대해 정리합니다. Stream을 이용해 비동기 처리를 할 수 있는 방법을 이해하고, 이를 실제 프로젝트에 적용할 수 있도록 설명합니다.
Dart Stream 사용법
Stream은 데이터의 연속적인 흐름을 나타냅니다. 마치 강물이 끊임없이 흐르는 것처럼, Stream은 시간에 따라 여러 개의 데이터를 순차적으로 전달합니다. 이는 파일 읽기, 네트워크 요청, 센서 데이터 수집 등 지속적으로 데이터를 받아야 하는 상황에서 유용합니다.
Stream 기본 사용법
1) Stream 생성하기
Stream을 생성하는 가장 간단한 방법은 Stream.fromIterable() 메서드를 사용하는 것입니다.
void main() {
// 리스트로부터 Stream 생성
Stream<int> numberStream = Stream.fromIterable([1, 2, 3, 4, 5]);
// Stream 사용하기
numberStream.listen((number) {
print('받은 숫자: $number');
});
}
이 코드를 실행하면 다음과 같은 결과가 출력됩니다:
받은 숫자: 1
받은 숫자: 2
받은 숫자: 3
받은 숫자: 4
받은 숫자: 5
listen 메서드는 Stream에서 데이터가 발생할 때마다 지정된 함수(여기서는 print 함수)를 실행합니다.
2) 비동기 생성자를 이용한 Stream 생성
async* 키워드와 yield 키워드를 사용하여 Stream을 생성할 수 있습니다.
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++){
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() {
countStream(5).listen((number){
print('카운트: $number');
});
}
이 코드는 1부터 5까지 1초 간격으로 숫자를 생성하는 Stream을 만듭니다. 실행 결과는 다음과 같습니다.
카운트: 1
(1초 후)
카운트: 2
(1초 후)
카운트: 3
(1초 후)
카운트: 4
(1초 후)
카운트: 5
async* 는 이 함수가 비동기적으로 실행되며 Stream을 생성한다는 것을 나타냅니다. yield는 Stream에 값을 추가합니다.
Stream 변환과 필터링
Stream의 데이터를 변환하거나 필터링하는 방법을 알아봅시다.
1) map() 메서드 사용하기
map() 메서드를 사용하면 Stream의 각 데이터를 변환할 수 있습니다.
void main() {
Stream<int> numberStream = Stream.fromIterable([1, 2, 3, 4, 5]);
numberStream
.map((number) => number * 2)
.listen((doubledNumber) {
print('2배로 만든 숫자: $doubledNumber');
});
}
실행 결과:
2배로 만든 숫자: 2
2배로 만든 숫자: 4
2배로 만든 숫자: 6
2배로 만든 숫자: 8
2배로 만든 숫자: 10
2) where() 메서드로 필터링하기
where() 메서드를 사용하면 Stream의 데이터를 필터링할 수 있습니다.
void main() {
Stream<int>> numberStream = Stream.fromIterable([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
numberStream
.where((number) => number % 2 == 0)
.listen((evenNumber) {
print('짝수: $evenNumber');
});
}
실행 결과:
짝수: 2
짝수: 4
짝수: 6
짝수: 8
짝수: 10
Stream 에러 처리
Stream 사용 시 발생할 수 있는 에러를 처리하는 방법을 알아봅시다.
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
if (i == 4) {
throw Exception('4는 좋지 않은 숫자입니다!');
}
yield i;
}
}
void main() {
countStream(5).listen(
(number) {
print('받은 숫자: $number');
},
onError: (error) {
print('에러 발생: $error');
},
onDone: () {
print('Stream 완료');
}
);
}
실행 결과:
받은 숫자: 1
받은 숫자: 2
받은 숫자: 3
에러 발생: Exception: 4는 좋지 않은 숫자입니다!
listen 메서드의 onError 파라미터를 사용하여 에러를 처리할 수 있습니다. onDone 파라미터는 Stream이 모든 데이터를 전송했을 때 실행됩니다.
실제 응용 예제 - 파일 읽기
Stream을 사용하여 대용량 파일을 효율적으로 읽는 예제를 살펴보겠습니다.
import 'dart:io';
void main() {
final file = File('example.txt');
file.openRead()
.transform(utf8.decoder)
.transform(LineSplitter())
.listen(
(line) {
print('읽은 줄: $line');
},
onError: () {
print('파일 읽기 에러: $error');
},
onDone: () {
print('파일 읽기 완료');
}
);
}
이 예제는 'example.txt' 파일을 한 줄씩 읽어 출력합니다. openRead() 메서드는 파일의 내용을 Stream으로 반환합니다.
utf8.decoder 와 LineSplitter() 는 바이트 스트림을 문자열로, 그리고 개별 줄로 변환합니다.
이렇게 Stream을 사용하면 대용량 파일도 메모리를 효율적으로 사용하면서 처리할 수 있습니다.
Stream은 비동기 프로그래밍에서 매우 강력한 도구이며, 실시간 데이터 처리, 파일 입출력, 네트워크 통신 등 다양한 상황에서 유용하게 사용될 수 있습니다.
'프로그래밍 언어 > Dart' 카테고리의 다른 글
Dart 입문자를 위한 라이브러리 활용법. 프로그래밍과 파일 입출력 예시 코드 - Dart 기초 #20 (0) | 2024.06.26 |
---|---|
Dart 예외 처리 try-catch 구문. 사용자 정의 예외와 finally 블록 사용하기 - Dart 기초 #19 (0) | 2024.06.25 |
Dart 비동기 프로그래밍. Future와 async/await 사용법 및 예제 코드 - Dart 기초 #17 (0) | 2024.06.21 |
Dart 제네릭 사용법. 타입 안전성과 코드 재사용성을 높이는 방법 - Dart 기초 #16 (0) | 2024.06.20 |
Dart Set 자료구조 이해하기. 셋 만들기와 사용법 - Dart 기초 #15 (0) | 2024.06.19 |