Flutter/Dart - Stream 사용 방법

Dart에서 Stream을 사용하기 위해 필요한 기본적인 내용에 대해서 알아보겠습니다.

1. Stream

Stream은 비동기적인 이벤트의 흐름이며, 0개 또는 다수의 이벤트가 전달합니다.

Stream에 Listener를 등록하여 데이터를 수신할 수 있으며, Listener는 0개 또는 1개 이상이 될 수 있습니다.

Future와 차이점

Future도 비동기 프로그래밍에 사용되며, Stream과 동일하게 미래에 생성되는 이벤트를 얻을 때 사용됩니다.

하지만 Future와 Stream의 차이점이 있습니다.

Future의 경우 1개의 데이터 값만 가질 수 있으며, Stream 처럼 다수의 데이터를 갖고 있을 수 없습니다. 그리고 Stream의 경우 다수의 Listener가 있을 수 있지만 Future는 한곳에서만 읽을 수 있습니다.

2. Stream 이벤트 생성 및 수신

Iterator의 데이터로 Stream 생성

Stream<Type>.fromIterable()은 Type 데이터를 갖고 있는 Iterator의 요소들을 순차적으로 Stream 이벤트를 생성합니다. 그리고 Stream.listen(func)으로 Stream으로 전달되는 데이터를 func 함수에서 처리하도록 만들 수 있습니다.

void main() {
  // Iterator의 요소들을 Stream 이벤트로 생성
  final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);

  // 이벤트 수신
  stream.listen((data) {
    print('data: $data');
  });
}

Output:

data: 1
data: 2
data: 3
data: 4
data: 5

yield로 Stream 생성

yield 키워드를 이용하여 Stream을 생성할 수 있습니다.

yield는 return 키워드와 비슷한데, 함수를 종료하진 않고 Stream으로 데이터를 전달합니다.

아래 예제와 같이 Stream을 만들면, 1초 간격으로 Integer 값이 Stream으로 전달됩니다. Stream에서 yield로 이벤트를 발생시킬 때 listen()의 함수가 호출되어 작업이 수행됩니다.

Stream<int> makeStream() async* {
  yield 1;
  await Future.delayed(Duration(seconds:1));
  yield 2;
  await Future.delayed(Duration(seconds:1));
  yield 3;
  await Future.delayed(Duration(seconds:1));
  yield 4;
  await Future.delayed(Duration(seconds:1));
  yield 5;
  await Future.delayed(Duration(seconds:1));
}

void main() {
  // Stream 생성
  final stream = makeStream();

  // 이벤트 수신
  stream.listen((data) {
    print('data: $data');
  });
}

Output:

data: 1
data: 2
data: 3
data: 4
data: 5

3. where() : 이벤트 필터링

Stream.where(func)는 func 함수의 리턴 값이 true인 이벤트만 읽습니다.

Stream.where(func).listen()처럼 사용하면, 조건에 맞는 데이터만 listen()으로 전달됩니다.

아래 예제는 where를 사용하여 Stream에서 짝수만 읽는 예제입니다.

void main() {
  final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5, 6]);
  stream.where((data) => data % 2 == 0).listen((data) {
    print('짝수: $data');
  });
}

Output:

짝수: 2
짝수: 4
짝수: 6

firstWhere()

firstWhere()where()와 동일한데, 차이점은 조건에 맞는 데이터를 1개만 읽습니다.

Stream.firstWhere(func).then()처럼 사용할 수 있으며, 가장 먼저 조건에 일치하는 데이터가 then()으로 전달됩니다. 아래와 같이 Stream의 첫번째 짝수를 읽을 수 있습니다.

void main() {
  final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5, 6]);
  stream.firstWhere((data) => data % 2 == 0).then((data) {
    print('첫번째 짝수: $data');
  });
}

Output:

첫번째 짝수: 2

4. map() : 이벤트 변환

Stream.map()은 Stream의 데이터를 map() 함수의 리턴 값으로 변환합니다.

아래 예제는 Stream의 데이터에 2를 곱하여 출력하는 예제입니다. map을 통과하면서 변환된 데이터는 listen()으로 전달됩니다.

void main() {
  final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);
  stream.map((data) => data * 2).listen((data) {
    print('data: $data');
  });
}

Output:

data: 2
data: 4
data: 6
data: 8
data: 10

asyncMap()

asyncMap()은 비동기적으로 데이터를 변환합니다. map()에 async가 추가되었다고 보면 됩니다.

아래와 같이 Stream 이벤트를 asyncMap()에서 읽을 때 비동기적인 방식으로 처리하여 listen()에 전달할 수 있습니다.

void main() {
  final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);
  stream.asyncMap((data) => Future.delayed(Duration(seconds: 1), () => data * 2)).listen((data) {
    print('data: $data');
  });
}

Output:

data: 2
data: 4
data: 6
data: 8
data: 10

5. take() : 제한된 수만큼 이벤트 읽기

Stream.take(count)는 Stream에서 count 개수만큼만 데이터를 읽습니다.

아래 예제는 Stream에서 순차적으로 3개의 이벤트만 읽습니다.

void main() {
  final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);
  stream.take(3).listen((data) {
    print('data: $data');
  });
}

Output:

data: 1
data: 2
data: 3

6. reduce() : 이벤트 누적

reduce()는 Stream의 데이터를 순차적으로 처리하여 결과를 누적할 때 사용합니다.

Stream.reduce((acc, data) => acc + data)처럼 사용할 수 있으며, acc + data의 결과는 다음 이벤트의 acc로 전달됩니다.

아래 예제에서 처음 acc의 초기 값은 0이며, 아래와 같은 순서로 계산되면서 15가 리턴됩니다.

  • (0, 1) => 0 + 1
  • (1, 2) => 1 + 2
  • (3, 3) => 3 + 3
  • (6, 4) => 6 + 4
  • (10, 5) => 10 + 5
  • 15 리턴
void main() {
  final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);
  stream.reduce((acc, data) => acc + data).then((result) {
    print('Result: $result');
  });
}

Output:

Result: 15

7. 에러 처리

Stream에서 에러가 발생할 수 있으며, 아래 예제처럼 Stream.handleError()으로 처리할 수 있습니다.

void main() {
  final stream = Stream<int>.error(Exception('에러 발생'));
  stream.handleError((error) {
    print('Error: $error');
  }).listen((data) {
    // 처음부터 에러가 발생하여 이 부분은 실행되지 않음
  });
}

Output:

Error: Exception: 에러 발생
Loading script...

Related Posts

codechachaCopyright ©2019 codechacha