[Flutter] BloC & MVVM 구현 - Stream과 yield에 대해 (Feat. Kotlin Android)
Flutter에서 자주 쓰이는 아키텍쳐 패턴으로 BloC 패턴이 있습니다.
Flutter는 상태에 따라 렌더링이 일어나기 때문에 상태 관리가 매우 중요하며, 이 상태 관리를 제어하기 위해 Google 개발자에 의해 BloC 패턴이 만들어졌습니다.
Bloc makes it easy to separate presentation from business logic, making your code fast, easy to test, and reusable.
비즈니스 로직과 UI를 분리하기 위한 수단으로, BloC 패턴을 활용한 MVVM 패턴 구현에 대해 글로 작성해보겠습니다.
Kotlin / Java 안드로이드에 대한 경험이 있으신 분들은 설명에 더 이해가 되실 수 있도록 추가적인 첨언을 붙이겠습니다.
BloC 패턴을 알기 전에, 미리 알아두어야 하는 내용이 있습니다.
Future vs Stream
두 가지 모두 비동기 처리라는 것에 대한 공통점이 있지만, 값이 단일이냐 아니면 연속적이냐의 차이가 있습니다.
Future는 단일 값에 대한 비동기 처리이고, Stream은 연속적인 값에 대한 비동기 처리입니다.
예로 Future는 Android에서 suspend 느낌이라면, Stream은 Flow의 느낌이라고 보시면 되겠습니다.
코드 예시를 보시면 이해가 더 빠르게 되실 겁니다.
Future의 예시 코드
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2)); // Simulate network delay
return "Data fetched from network";
}
Stream의 예시 코드
Stream<int> generateNumbers() async* {
for (int i = 1; i <= 10; i++) {
await Future.delayed(Duration(seconds: 1)); // Simulate delay
yield i;
}
}
Stream은 물이 흐르는 파이프라고 생각하시면 됩니다.
잘 보시면 Future에서는 async가 붙여져 있고, Stream에는 async* 이 붙여져 있는 차이점이 있습니다.
async는 Future 비동기 함수를 만들고 async*는 Stream 비동기 함수를 만듭니다.
또한 Stream에서는 yield라는 개념을 사용하는데요, 값을 리턴하고 끝내는 것이 아니라
연속적으로 값을 배출하기 때문에 yield를 통해 값을 내보내게 됩니다.
Android Kotlin을 예로 들었을 때, suspend 함수 내에서는 return을 통해 값을 내뱉겠지만
flow 에서는 emit을 통해 여러 값을 내뱉는 것과 동일합니다.
suspend fun fetchData(): String {
delay(2000) // Simulate network delay
return "Data fetched from network"
}
fun generateNumbers(): Flow<Int> = flow {
for (i in 1..10) {
delay(1000) // Simulate delay
emit(i)
}
}
yield vs yield*
Stream에서 쓰이는 값 배출 방식으로는 앞서 봤던 yield와 함께 yield* 라는 것도 있습니다.
yield : 직접 값을 리턴합니다. single value를 리턴
yield* : 값을 delegate(위임)한다. 다른 Stream 함수를 호출한다고 보면 된다. multiple value를 리턴
마찬가지로 코드를 예시로 보면 이해가 더 쉽습니다.
Stream<int> generateEvenNumbers() async* {
for (int i = 2; i <= 10; i += 2) {
await Future.delayed(Duration(seconds: 1)); // Simulate delay
yield i;
}
}
Stream<int> generateOddNumbers() async* {
for (int i = 1; i <= 9; i += 2) {
await Future.delayed(Duration(seconds: 1)); // Simulate delay
yield i;
}
}
Stream<int> generateNumbers() async* {
yield* generateOddNumbers();
yield* generateEvenNumbers();
}
Future, Stream, async, async*, yield, yield* 뭐가 이렇게 많고 헷갈리죠??
이해가 쉽게 다시 한 번 정리하자면
Future는 단일 값으로 async와 붙어서 사용하고
Stream은 연속 값으로 async*와 붙어서 사용하며
Stream 내에서는 순간 순간 단일 값을 내뱉는 yield와 연속값을 내뱉는 yields*이 있습니다.
* return과 yield의 차이점은, return은 값을 리턴하고 함수가 종료된다는 특징, yield는 종료되지 않는다는 차이가 있습니다.
다음 글에서는 Flutter의 BloC 구현에 대해 본격적으로 작성해보도록 하겠습니다.