Wednesday, July 26, 2023

Flutter StreamBuilder 활용법, 초보자도 쉽게 이해하기

1장: Flutter StreamBuilder 기본 알아보기

StreamBuilder는 Flutter에서 데이터 흐름을 관리하는 위젯으로, 비동기 작업 수행에 유용합니다. 이 장에서는 StreamBuilder의 기본적인 개념과 사용 방법에 대해 알아봅니다.

1.1 StreamBuilder의 이해

Flutter의 StreamBuilder는 데이터의 연속적인 흐름을 다루는 스트림을 기반으로 하는 위젯입니다. 스트림은 데이터가 연속적으로 도착할 것을 예상할 때 유용하며, 따라서 비동기 작업에 적합하도록 설계되어 있습니다.

StreamBuilder는 스트림에서 새로운 데이터가 도착할 때마다 해당 데이터를 표시하는 위젯을 업데이트합니다. 이를 통해 실시간으로 데이터 변경을 사용자에게 보여주는 UI를 손쉽게 구현할 수 있습니다.

1.2 StreamBuilder의 핵심 속성

StreamBuilder에서는 주로 두 가지 주요 속성을 사용합니다:

  1. stream: StreamBuilder에 데이터를 제공하는 스트림입니다.
  2. builder: 새로운 데이터가 스트림에 도착할 때마다 호출되는 함수로, 반환하는 위젯으로 화면을 업데이트합니다.

2장: StreamBuilder 구현 예시

이 장에서는 간단한 StreamBuilder를 활용한 예제를 통해 그 동작 방식을 이해해봅시다. 이 예제에서는 주기적으로 새로운 데이터를 받아 표시하는 애플리케이션을 구현해보겠습니다.

2.1 주기적으로 데이터를 생성하는 스트림 생성

먼저, 주기적으로 데이터를 생성하는 스트림을 만들어봅시다. 아래 코드는 매 초마다 새로운 날짜와 시간을 생성하는 스트림입니다:

Stream<DateTime> createDateStream() {
  return Stream.periodic(Duration(seconds: 1), (_) => DateTime.now());
}

2.2 StreamBuilder 활용하기

이제, StreamBuilder 위젯을 활용하여 스트림에서 받은 데이터를 화면에 표시해봅시다. 아래 예제는 StreamBuilder를 사용하여 위에서 생성한 createDateStream()에서 받은 데이터를 표시합니다:

StreamBuilder<DateTime>(
  stream: createDateStream(),
  builder: (BuildContext context, AsyncSnapshot<DateTime> snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return Text('Loading...');
    } else if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    } else {
      return Text('Current time: ${snapshot.data}');
    }
  },
)

위 코드에서는 StreamBuilder의 stream 속성에 createDateStream()를 전달하고, builder 함수에서는 상황에 따라 화면에 다르게 표시하도록 구현하였습니다.

- ConnectionState.waiting: 데이터 수신 전에 '로딩 중...' 표시

- snapshot.hasError: 오류 발생 시 '오류: 오류 내용' 표시

- 그 외의 경우: 현재 시간 표시

3장: StreamBuilder의 다양한 활용 예시

이 장에서는 StreamBuilder를 활용한 다양한 예제를 통해 실제 애플리케이션에서 어떻게 사용할 수 있는지 알아봅시다.

3.1 Firebase Realtime Database와의 연동

Firebase Realtime Database는 실시간 데이터 동기화를 제공하는 클라우드 기반 NoSQL 데이터베이스입니다. StreamBuilder와 함께 사용하면 실시간 데이터 변경을 간단하게 처리할 수 있습니다.

StreamBuilder(
  stream: FirebaseDatabase.instance.reference().child('messages').onValue,
  builder: (context, snapshot){
    if(snapshot.hasData) {
      DataSnapshot dataSnapshot = snapshot.data.snapshot;
      List messages = [];
      dataSnapshot.forEach((m) => messages.add(m.value));
      return ListView.builder(
        itemCount: messages.length,
        itemBuilder: (context, index) {
          return ListTile(title: Text(messages[index]));
        },
      );
    }
    else {
      return CircularProgressIndicator();
    }
  },
)

3.2 Firestore와의 연동

Firestore는 Google의 실시간 동기화 기능을 제공하는 NoSQL 클라우드 데이터베이스입니다. 아래는 StreamBuilder와 Firestore를 연동한 예제입니다.

StreamBuilder<QuerySnapshot>(
  stream: FirebaseFirestore.instance.collection('users').snapshots(),
  builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
    if (snapshot.hasError) {
      return Text('오류: ${snapshot.error}');
    }
    switch (snapshot.connectionState) {
      case ConnectionState.waiting:
        return CircularProgressIndicator();
      default:
        return ListView(
          children: snapshot.data.docs.map((DocumentSnapshot document) {
            return ListTile(
              title: Text(document['name']),
              subtitle: Text(document['email']),
            );
          }).toList(),
        );
    }
  },
)

5장: StreamBuilder의 성능 최적화 방법

마지막 장에서는 StreamBuilder를 사용할 때 성능 및 리소스 사용의 효율성을 향상시키는 몇 가지 팁을 알아보겠습니다.

5.1 데이터 디바운싱 활용하기

스트림의 변경 사항이 너무 자주 발생하면, UI의 업데이트 속도에 영향을 줄 수 있습니다. 이런 경우 데이터 디바운싱을 활용하여 동일한 시간 내에서 여러 차례 일어나는 변경을 하나로 제한할 수 있습니다.

Stream<Data> debouncedDataStream(Stream<Data> inputStream) {
  return inputStream.transform(
    debounce(Duration(milliseconds: 500)),
  );
}

5.2 스트림 데이터 필터링하기

불필요한 상황에서 스트림 데이터를 처리하는 것을 방지하고, 성능을 향상시키기 위해 필터 기능을 사용하여 스트림 데이터를 제한하세요. 아래 예제에서는 where 함수를 사용하여 필터링을 한 예입니다.

Stream<int> filteredStream = originalStream.where((value) => value % 2 == 0);

5.3 StreamBuilder 사용 시 메모리 누수 방지하기

StreamBuilder를 사용할 때 내부에서 사용된 리소스를 정상적으로 해제하지 않으면 메모리 누수가 발생할 수 있습니다. 이를 방지하기 위하여 dispose() 또는 close() 메서드를 통해 StreamController 사용이 끝났을 때 리소스를 해제해주세요.

@override
void dispose() {
  streamController.close();
  super.dispose();
}

0 개의 댓글:

Post a Comment