TIL

Flutter의 Stream에 대해서..

kimw746 2025. 1. 15. 16:50

Flutter에서 데이터가 비동기적으로 처리되는 경우가 많습니다. 이러한 비동기 데이터 흐름을 관리하기 위해 Stream이라는 도구를 사용합니다. 이번 글에서는 Stream이 무엇인지, 왜 사용하는지, 그리고 실생활에서 어떻게 활용되는지를 저 같은 초보자도 이해할 수 있도록 쉽게 설명하겠습니다.

 

Stream이란?

Stream은 Dart에서 비동기 데이터 이벤트의 연속적인 흐름을 처리하기 위한 클래스입니다.

쉽게 말하면, Stream은 "데이터의 강"이라고 생각하면 됩니다. 이 강에서는 데이터가 하나씩(혹은 여러 개씩) 흘러오고, 이를 필요할 때마다 받아서 처리할 수 있습니다.


Stream의 작동 방식

Stream은 두 가지 주요 구성 요소로 이루어져 있습니다:

  1. Stream: 데이터를 제공하는 쪽입니다. "데이터를 흘려보내는 강"이라고 생각할 수 있습니다.
  2. Listener(구독자): 데이터를 소비하는 쪽입니다. "강에서 물을 퍼올리는 사람"에 해당합니다.

Stream을 사용하려면 먼저 데이터를 구독(subscribe)해야 합니다. 구독한 후에는 Stream에서 데이터를 받을 수 있습니다.

 


Stream의 종류

Stream에는 두 가지 주요 종류가 있습니다:

  1. Single-Subscription Stream
    • 한 번에 하나의 리스너만 데이터를 구독할 수 있습니다.
    • 주로 네트워크 요청, 파일 읽기 등 단일 이벤트 스트림에 사용됩니다.
    Stream<int> numberStream() async* {
      for (int i = 1; i <= 5; i++) {
        await Future.delayed(Duration(seconds: 1));
        yield i;
      }
    }
    
    void main() {
      numberStream().listen((number) {
        print('Received number: $number');
      });
    }
  2. Broadcast Stream
    • 여러 리스너가 동시에 데이터를 구독할 수 있습니다.
    • 주로 이벤트 버스나 실시간 데이터 스트림에 사용됩니다.
    StreamController<String> controller = StreamController.broadcast();
    
    void main() {
      controller.stream.listen((data) {
        print('Listener 1 received: $data');
      });
    
      controller.stream.listen((data) {
        print('Listener 2 received: $data');
      });
    
      controller.add('Hello, Stream!');
    }

Stream을 사용하는 이유

  1. 비동기 데이터 처리
    • 네트워크 요청, 파일 읽기, 사용자 입력 등 비동기적으로 발생하는 데이터를 효율적으로 처리할 수 있습니다.
  2. 실시간 데이터 스트림 관리
    • 채팅 앱, 주식 가격 업데이트, 실시간 알림과 같은 앱에서 매우 유용합니다.
  3. 코드의 가독성 향상
    • 데이터를 순차적으로 처리하거나 이벤트 기반 코드를 작성할 때 Stream을 사용하면 코드가 더 깔끔해집니다.

Stream을 활용한 예제

1. 네트워크 요청 처리

Stream을 사용하면 비동기 데이터 요청과 응답을 관리할 수 있습니다.

Stream<String> fetchServerData() async* {
  await Future.delayed(Duration(seconds: 2));
  yield 'Data from server';
}

void main() {
  fetchServerData().listen((data) {
    print(data);
  });
}

2. 실시간 타이머

Stream을 활용해 타이머를 구현할 수도 있습니다.

Stream<int> timerStream(int seconds) async* {
  for (int i = 1; i <= seconds; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

void main() {
  timerStream(5).listen((tick) {
    print('Tick: $tick');
  });
}

3. 사용자 입력 처리

사용자가 입력하는 데이터를 실시간으로 처리하는 예제입니다.

import 'dart:async';

void main() {
  final controller = StreamController<String>();

  controller.stream.listen((input) {
    print('You entered: $input');
  });

  controller.add('Hello, World!');
  controller.add('Stream is awesome!');
}

Stream 사용 시 주의할 점

  1. Stream 구독 해제
    • 구독한 Stream은 사용 후 반드시 해제해야 메모리 누수를 방지할 수 있습니다.
    • StreamSubscription.cancel()을 사용하세요.
    StreamSubscription<int> subscription;
    
    void main() {
      subscription = timerStream(5).listen((tick) {
        print('Tick: $tick');
      });
    
      Future.delayed(Duration(seconds: 3), () {
        subscription.cancel();
        print('Stream cancelled');
      });
    }
  2. 에러 처리
    • Stream에서 에러가 발생할 수 있으니 반드시 처리해 주세요.
    void main() {
      timerStream(5).listen(
        (tick) {
          print('Tick: $tick');
        },
        onError: (error) {
          print('Error: $error');
        },
        onDone: () {
          print('Stream completed');
        },
      );
    }

끝으로..

Stream은 Flutter에서 비동기 데이터와 실시간 이벤트를 효율적으로 처리하기 위한 도구입니다. 이번 글에서 소개한 기본 개념과 예제를 잘 활용하면, 앞으로 만드는 앱을 더 잘 만들 수 있을 것 같습니다!!