플러터(Flutter)는 효율적이고 확장 가능한 상태 관리를 위해 다양한 패턴을 제공합니다. 그 중에서 BLoC(Block + Logic) 패턴은 많은 개발자들에게 인기 있는 패턴 중 하나입니다. BLoC 패턴은 비즈니스 로직을 별도의 클래스에 분리하여 코드의 가독성을 높이고, 상태 변화를 쉽게 관리할 수 있도록 도와줍니다.
BLoC 패턴의 기본 개념
BLoC 패턴은 크게 세 가지 요소로 구성됩니다.
-
이벤트(Event): 앱의 상태 변화를 일으키는 여러가지 이벤트입니다. 사용자의 액션, 네트워크 호출, 타이머 등 다양한 이벤트가 해당됩니다.
-
상태(State): 앱의 현재 상태를 나타냅니다. 예를 들어, 사용자 인증 상태, 네트워크 상태, 데이터 로딩 상태 등 다양한 상태를 나타낼 수 있습니다.
-
비즈니스 로직(Business Logic): 이벤트와 상태 사이의 변환 로직을 담당합니다. 이벤트를 받아서 해당 이벤트에 따라 상태를 변화시키는 역할을 합니다.
BLoC 패턴의 구현 방법
BLoC 패턴을 구현하기 위해서는 몇 가지 클래스를 정의해야 합니다.
-
BLoC 클래스: BLoC 클래스는 비즈니스 로직을 구현하는 클래스입니다. 주로
Stream
을 사용하여 이벤트를 받고,Sink
를 통해 상태를 내보냅니다. BLoC 클래스는Stream
과Sink
를 노출하고, 필요한 상태 변화 로직을 구현합니다. -
이벤트 클래스: 이벤트 클래스는 사용자 액션 등의 이벤트를 정의하는 클래스입니다. 주로
enum
형태로 이벤트 종류를 정의하고, 필요한 추가 데이터를 포함할 수도 있습니다. -
상태 클래스: 상태 클래스는 앱의 상태를 정의하는 클래스입니다. 주로
enum
형태로 상태 종류를 정의하고, 필요한 추가 데이터를 포함할 수도 있습니다.
구현 예시를 살펴보겠습니다.
// 이벤트 클래스 정의
enum CounterEvent {
increment,
decrement,
}
// 상태 클래스 정의
class CounterState {
final int count;
CounterState(this.count);
}
// BLoC 클래스 정의
class CounterBloc {
int _count = 0;
final _stateStreamController = StreamController<CounterState>();
Stream<CounterState> get stateStream => _stateStreamController.stream;
final _eventSinkController = StreamController<CounterEvent>();
Sink<CounterEvent> get eventSink => _eventSinkController.sink;
CounterBloc() {
_eventSinkController.stream.listen(_mapEventToState);
}
void _mapEventToState(CounterEvent event) {
if (event == CounterEvent.increment) {
_count++;
} else if (event == CounterEvent.decrement) {
_count--;
}
_stateStreamController.add(CounterState(_count));
}
void dispose() {
_stateStreamController.close();
_eventSinkController.close();
}
}
위의 코드는 간단한 카운터 앱을 구현한 BLoC 클래스입니다. 이벤트에 따라 카운트 값을 증가하거나 감소시키고, 상태 스트림에 변화된 상태를 전달합니다.
이제 위에서 정의한 BLoC 클래스를 사용하여 앱에서 상태 변화를 관리할 수 있습니다. 예를 들어, 플러터의 StreamBuilder
위젯과 함께 사용하여 상태 변화를 화면에 반영할 수 있습니다.
class CounterApp extends StatelessWidget {
final CounterBloc _counterBloc = CounterBloc();
@override
Widget build(BuildContext context) {
return StreamBuilder<CounterState>(
stream: _counterBloc.stateStream,
initialData: CounterState(0),
builder: (context, snapshot) {
return Scaffold(
appBar: AppBar(
title: Text('BLoC Counter'),
),
body: Center(
child: Text('Count: ${snapshot.data.count}'),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () => _counterBloc.eventSink.add(CounterEvent.increment),
child: Icon(Icons.add),
),
SizedBox(height: 16),
FloatingActionButton(
onPressed: () => _counterBloc.eventSink.add(CounterEvent.decrement),
child: Icon(Icons.remove),
),
],
),
);
},
);
}
@override
void dispose() {
_counterBloc.dispose();
super.dispose();
}
}
위의 코드에서는 StreamBuilder
를 사용하여 상태 변화를 감지하고, 변경된 상태를 화면에 반영하고 있습니다. 또한, FloatingActionButton
위젯과 함께 이벤트를 발생시켜 상태 변화를 처리하고 있습니다.
이제 BLoC 패턴을 사용하여 효율적이고 확장 가능한 상태 관리를 할 수 있게 되었습니다.
정리
플러터에서 BLoC 패턴을 사용하면 앱의 상태 변화를 효과적으로 관리할 수 있습니다. BLoC 패턴은 이벤트, 상태, 비즈니스 로직을 분리하여 코드의 가독성을 향상시키고, 상태 변화를 쉽게 관리할 수 있습니다. 위의 예제 코드를 참고하여 자신의 앱에 BLoC 패턴을 적용해보세요.