Flutter BLoC 패턴: 정의와 사용법
Flutter에서 상태 관리는 필수적인 요소이며, 다양한 방법이 존재합니다. 그중에서도 BLoC (Business Logic Component) 패턴은 명확한 구조와 유지보수성을 제공하는 강력한 패턴입니다. 이번 글에서는 BLoC 패턴이 무엇인지, 어떻게 동작하는지, 그리고 실습 예제를 통해 사용법을 익혀보겠습니다.
1. BLoC 패턴이란?
BLoC 패턴은 Flutter에서 상태를 관리하는 방법 중 하나로, 비즈니스 로직을 UI와 분리하여 관리합니다. flutter_bloc 패키지를 활용하면 간단하게 상태를 관리할 수 있으며, 이벤트(Event)와 상태(State)의 흐름을 명확하게 정의할 수 있습니다.
BLoC 패턴의 핵심 개념
- Event (이벤트): 사용자 입력이나 특정 동작에 의해 발생하는 트리거 (예: 버튼 클릭, API 요청)
- State (상태): UI가 반응하는 데이터 상태 (예: 로딩 중, 데이터 로드 완료, 에러 발생)
- BLoC (비즈니스 로직 컴포넌트): 이벤트를 받고, 처리한 후 새로운 상태를 방출하는 역할
2. BLoC 구조와 동작 방식
BLoC 패턴은 아래와 같은 흐름을 가집니다:
- UI에서 Event 발생 → BLoC에 전달
- BLoC에서 이벤트를 받고 비즈니스 로직을 처리
- 새로운 State를 방출
- UI가 State를 감지하고 화면을 업데이트
이를 코드로 표현하면 다음과 같습니다:
사용자 입력 → Event 발생 → BLoC에서 Event 처리 → 새로운 State 방출 → UI 업데이트
3. BLoC 패턴 사용 방법 (실습)
1) 패키지 설치
BLoC를 사용하기 위해 flutter_bloc 패키지를 추가해야 합니다.
flutter pub add flutter_bloc
2) Counter BLoC 예제
Flutter에서 가장 간단한 카운터 앱을 BLoC 패턴으로 구현해보겠습니다.
(1) Event 정의
이벤트 클래스는 CounterEvent로 정의합니다.
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
(2) State 정의
상태 클래스는 CounterState로 정의합니다.
class CounterState {
final int count;
CounterState(this.count);
}
(3) BLoC 정의
이제 CounterBloc 클래스를 생성하여 이벤트를 받아서 상태를 변경하는 로직을 구현합니다.
import 'package:flutter_bloc/flutter_bloc.dart';
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0)) {
on<IncrementEvent>((event, emit) {
emit(CounterState(state.count + 1));
});
on<DecrementEvent>((event, emit) {
emit(CounterState(state.count - 1));
});
}
}
(4) UI에서 BLoC 사용
이제 BlocProvider를 사용하여 BLoC를 UI에 연결하고, BlocBuilder로 상태를 감지합니다.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
create: (context) => CounterBloc(),
child: CounterScreen(),
),
);
}
}
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Flutter BLoC Example')),
body: Center(
child: BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text(
'${state.count}',
style: TextStyle(fontSize: 40),
);
},
),
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(IncrementEvent()),
child: Icon(Icons.add),
),
SizedBox(width: 10),
FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(DecrementEvent()),
child: Icon(Icons.remove),
),
],
),
);
}
}
4. BLoC 패턴의 장점과 단점
✅ 장점
✔ 비즈니스 로직과 UI 분리: 유지보수가 쉬워짐
✔ 상태 흐름이 명확: 이벤트와 상태를 구조적으로 관리
✔ 테스트 용이: 단위 테스트 및 UI 테스트가 쉬움
❌ 단점
⚠ 초기 학습 비용: 이벤트와 상태를 정의하는 것이 번거로울 수 있음
⚠ 소규모 프로젝트에는 과한 구조: 간단한 앱에서는 Provider 등의 대안이 더 적합할 수도 있음