본문 바로가기
프로그래밍/Flutter

[flutter 상태관리] 10. 플러터 Stream 과 Sink

by 신나요 2022. 6. 14.

스트림을 활용해서 연속되는 데이터를 처리하는 방법에 대해 알아볼텐데요. 상태 관리 패턴인 BLoC를 다루기에 앞서 Stream과 Sink의 개념을 알아보겠습니다.


Stream이란?

Stream은 연속 데이터, 이벤트를 처리해주는 클래스 입니다. 스트림 자체는 연속적인 값을 가지며 sink를 사용해 메시지(값)를 추가해 줄 수 있습니다. 추가된 메시지는 listen 메소드를 사용해 추가된 타이밍에 값을 사용하여 처리를 하는 리스너를 설정할 수 있습니다. 따라서 사용자의 액션에 따른 처리나, webAPI의 비동기 처리를 할 때 Stream을 사용하게 됩니다. Rxjs와 같은 ReactiveX를 사용해본 분이라면 거부감 없이 받아들일 수 있고 rx라이브러리 호환되는 부분도 있습니다.


Provider와 Stream 컨트롤러 비교

스트림과 싱크에 대해 알아보기에 앞서 지난 포스트에서 다뤘던 Provider를 잠깐 살펴보겠습니다.

여기 ItemProvider라는 프로바이더는 items라는 상태를 제공해주고 있습니다. Provider가 작동하는 방식을 살펴보면 items 이라는 상태에 대한 업데이트가 있을 때 상태를 업데이트해주는 역할이 있어야 합니다. 그 역할을 setItem과 같은 메소드를 통해서 하게 됩니다. 상태가 바뀌었다면 상태를 참고해서 화면을 구성하고 있는 위젯들에게 업데이트 사실을 알려야 합니다. 상태 업데이트 알리고 싶을 때 notifyListeners메서드를 호출하고 위젯들이 갱신되게 됩니다.

위 그림처럼 정보의 흐름으로 생각해 볼 수 있는데요. setItem으로 정보가 유입되고 notifyListeners를 통해 정보가 전달 됩니다. 이제 Provider를 StreamController로 바꿔보겠습니다.

StreamController의 sink와 Stream ?

Provider와 동일한 패턴을 갖게 되지만 유입 데이터와 유출 데이터 사이에 훨씬 더 긴밀한 관계를 갖되는데요. 우선 들어오는 값과 나가는 값을 관리하기 위해 StreamController를 사용하고 있습니다. StreamController에는 sink와 stream이라는 두 가지 측면이 있습니다. sink는 데이터가 컨트롤러로 들어오는 곳이며, stream은 컨트롤러에서 데이터가 외부로 흘러가는 곳입니다. sink를 통해 데이터를 비동기적으로 가져온 후 원하는 데이터 형식으로 적절한 처리를 하게 됩니다. 데이터 처리 후 해당 컨트롤러가 상태의 업데이트를 외부로 보낼 것입니다. Provider를 사용해서 상태 공유를 해결한 것과 마찬가지로 매우 유사한 패턴입니다. 하지만 스트림을 사용하면 비동기적으로 해결할 수 있고 특히 유사한 처리가 반복해서 발생할 때 훨씬 더 깔끔한 방법을 제공해 줍니다.

이제 플러터 코드에서 stream을 어떻게 사용할 수 있는지 알아보겠습니다.


stream 실습

새로운 플러터 프로젝트를 만들어 주었고 아래와 같이 기본 코드를 작성하였습니다.

MyApp 위젯을 스테이트풀 위젯으로 만들어 주었고 main메소드의 runApp에서 MyApp 객체를 넣어주고 있습니다. _MyAppState 클래스는 다음과 같습니다.

_MyAppState 클래스는 MyApp의 스테이트 클래스입니다. 화면은 counter의 내용을 Text위젯으로 출력하고 있고 그 아래에는 ElevatedButton이 있습니다. 에뮬레이터로 화면을 보면 아래와 같은 레이아웃이 됩니다.

counter가 0이므로 0이 화면에 출력되고 있고 버튼을 클릭했을 때 콜백 함수를 설정하지 않았기 때문에 아직 아무런 일도 일어나지 않습니다.

앞으로 해야 할 일은 stream을 사용해서 counter를 관리하고 위젯을 관리하는 것입니다.

 

스트림 컨트롤러 만들기

플러터에서 로컬로 스트림을 작업을 할때는 StreamController 클래스를 사용하면 됩니다. 일반적으로 프로덕션 앱에서는 파일이나 데이터베이스 혹은 데이터를 읽는 WebSocket을 가지게 될 것입니다. 즉 데이터가 스트리밍 되고 해당 스트림이 제공되지만 데모에서는 존재하지 않기 때문에 자체 스트림을 생성할 수 있는 객체를 만들 것이고 그때 StreamController 클래스를 이용하게 됩니다.

StreamController는 counter를 컨트롤해줘야 하고 정수 유형의 메시지를 처리해야 하므로 int 타입으로 만들어 주고 있습니다. 기본적으로 StreamController 와 스트림은 하나의 리스너에게만 메시지를 보냅니다. 만약 여러 리스너에게 브로드캐스트 해야 할 경우에는 broadcast생성자 함수를 사용해야 합니다. 위에서 언급했듯이 StreamController에는 sink와 stream 두 개의 측면이 존재합니다. 싱크를 사용하여 데이터를 추가하고 스트림을 사용해서 컨트롤러에서 데이터를 가져오도록 수정하겠습니다.

1. sink 처리입니다. 이제 버튼을 클릭했을 때 onPressed 핸들러에서 상태 값을 업데이트해줘야 합니다. counterController를 이용해서 add를 호출하고 있습니다. add는 sink에 다른 메시지를 추가해줍니다. 이로서 클릭을 하게 되면 컨트롤러는 처리해야 할 다른 메시지를 갖게 됩니다. 현재 counter 값에서 1을 증가하고 하고 add에 설정해주고 있습니다. 하지만 싱크에 다른 메시지를 추가하더라도 자동으로 메시지를 읽어오지는 않기 때문에 이것만으론 애플리케이션 상태를 업데이트하지 않습니다. 따라서 stream 처리를 해줘야 합니다.

2. stream 처리를 하고 있습니다. 생성자 안에서 리스너를 추가해주고 있는데요. 싱크는 메시지를 추가하는 곳이고 스트림은 메시지를 다시 내보내는 곳입니다. counterController.stream.listen 으로 리스너를 통해 메시지를 건네받게 됩니다. listen메서드는 들어오는 메시지를 처리하는 함수를 설정해 줄 수 있습니다. 전달된 값을 counter에 할당하는 함수를 setState에 설정해 주면 위젯이 업데이트됩니다.

 

화면을 보겠습니다.

클릭을 할 때 이제 숫자가 바뀌게 되는데요. 숫자가 바뀌는 과정을 다시 살펴보면 0인 상태에서 클릭을 하면 add로 0+1의 값이 메시지로 전달됩니다. 이제 생성자에서 추가해준 리스너에서 설정한 함수가 호출되면서 1의 값이 건네 지게 되고 state를 1로 바뀐 후 setState를 통해 위젯이 업데이트가 이뤄집니다.


여기까지 수고하셨습니다!

댓글