iOS 개발자에서 Flutter 개발자로 전직해보자 1탄
Installation for Flutter Project
1. Flutter SDK 설치
2. Xcode(혹은 Android) 설치하기
3. VSCode 설치
Flutter에 대해서는 네드팍 선생님의 Dart언어 및 Flutter강의를 듣게 되었다.
Swift와 SwiftUI로 개발을 하다보니 Flutter의 UI 구성 방식이 너무 익숙하지 않고, State와 ObservableObject와 같은 기능을 자체적으로 제공하기 보다 트리 구조로 데이터를 넘겨주면서 UI간의 데이터 이동 시 효율성에 대한 고민이 되었다.
왜 Bloc 라이브러리인가?
아래 무료강의를 통해서 Bloc이 어떤 것인지에 대해서 간단하게 알아봤다. Flutter의 UI 연결 방식을 보았다면 뷰 사이의 데이터 이동에 대해서 효율적인 방식을 찾고싶을 것이다. Bloc 패턴이 이 부분을 많이 해소해 줄 수 있을 것 같다고 느꼈다.(아직 해보지는 않았지만)
또한 TDD!!! 어떤 개발을 하더라도 구현 이후에는 TestCode의 중요성에 대해서 점점 더 느끼게 되는만큼 TDD의 용이성과 이를 지원하기 위한 BlocTest 기능을 지원한다는 것이 일단 듣기에는 매력적으로 다가오는 것 같다.
[지금 무료][플러터 상태관리] Bloc 마스터 코스 기초부터 응용까지! 강의 | 개발하는남자 - 인프런
개발하는남자 | , 플러터 bloc은 실무에 어떻게 쓰냐고요?이 강의에서 확인해보세요! 🤗 플러터 bloc의기본부터 응용까지 📌 플러터에는 상태 관리 라이브러리가 다양하게 존재합니다. bloc, getx, p
www.inflearn.com
아래 튜토리얼을 통해서 플러터와 상태 관리를 하는 라이브러리에 대해서 공부를 해보고자 한다.
https://bloclibrary.dev/ko/tutorials/flutter-counter/
Flutter Counter
An in-depth guide on how to build a Flutter counter app with bloc.
bloclibrary.dev
Create Project
프로젝트를 생성할 폴더 위치에서 아래 명령어를 실행한다.
flutter create projectName
pubspec.yaml
- pubspec.yaml은 프로젝트의 설정 및 의존성(dependencies)을 관리하는 파일로 Swift 프로젝트의 Package.swift(Swift Package Manager)나 Podfile(CocoaPods)과 비슷한 역할을 한다고 보면 된다.
- pubspec.yaml을 변경한 이후에는 flutter pub get을 실행해야 한다. flutter pub get을 실행하면 pubspec.yaml에 추가한 패키지들을 다운로드하고, pubspec.lock 파일을 업데이트하기 때문에 이 과정이 없으면 새로 추가한 패키지를 프로젝트에서 사용할 수 없다.
name: flutter_counter
description: A new Flutter project.
version: 1.0.0+1
publish_to: none
environment:
sdk: ">=3.6.0 <4.0.0"
dependencies:
bloc: ^9.0.0
flutter:
sdk: flutter
flutter_bloc: ^9.1.0
dev_dependencies:
bloc_test: ^10.0.0
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
mocktail: ^1.0.0
flutter:
uses-material-design: true
위 코드의 경우 아래 나와있는 것처럼 Bloc 패턴을 사용하기 위한 패키지를 구성한 코드이며, 테스트 환경 구축을 위한 코드도 포함되어 있다는 것을 알 수 있다.
BlocObserver
class CounterObserver extends BlocObserver {
const CounterObserver();
@override void onChange(BlocBase<dynamic> bloc, Change<dynamic> change) { //<dynamic> 어떤 타입도 받을 수 있는 타입
super.onChange(bloc, change);
print('${bloc.runtimeType} $change');
}
}
BlocObserver는 BLoC 패턴에서 모든 BLoC의 상태 변화를 감지할 수 있는 추상 클래스이다. flutter_bloc 패키지를 통해서 제공하며, Bloc이나 Cubit의 상태 변화를 로깅하거나 디버깅할 때 사용한다.
BlocObserver를 상속받으면, 앱의 모든 BLoC에서 발생하는 이벤트와 상태 변화를 감지할 수 있으며 onChange, onTransition, onError 등의 메서드를 오버라이드해서 원하는 로직을 추가할 수 있다.
BlocObserver는 앱 전체에서 하나만 존재해야 하는 전역 객체로 const 생성자를 사용하면 동일한 인스턴스를 여러 번 재사용할 수 있게 되어 효율적이다. 앱을 실행하는 동안 CounterObserver()가 단 하나의 객체로 계속 사용되면 메모리 사용량이 줄어들고, 성능 향상에 도움이 된다.
-> 나는 이 부분이 가장 이해가 가지 않았다. 싱글톤 패턴도 명확하게 아니면서 전역 객체로 하나만 생성되게 한다는 것이 아직도 이해가 되지 않지만 우선 앱 전체에서 하나만 존재해야 한다고 하니... 그렇다고 하자...
그렇다면 위 코드로 CounterObserver() 객체는 생성되더라도 메모리 상으로 하나의 객체로 사용하게 되는거니까 아래 코드에서는 CounterObserver() 객체를 생성할 때 const를 붙이지 않아도 되는 것이 아닌가? 하는 의문이 들었다.
void main() {
Bloc.observer = const CounterObserver();
runApp(const MyApp());
}
하지만 이 코드는 싱글톤이다. 여기서 const CounterObserver()는 CounterObserver 클래스의 객체를 상수로 생성하는데, const 생성자를 사용하면 앱 내에서 이 객체는 하나만 존재하게 되고 CounterObserver의 객체가 한 번만 생성되고 재사용되는 구조가 된다.
MaterialApp
MaterialApp은 Flutter에서 앱의 기본적인 구조와 테마를 설정하는 중요한 위젯으로 Flutter 앱을 구성하는 여러 요소를 관리하는 최상위 위젯이다. MaterialApp을 사용하면, Flutter 앱의 일관된 UI 디자인을 구현할 수 있게 도와준다.
일반적으로는 Widget을 사용하지만, 이번 튜토리얼에는 Widget이 필요하지 않아서 간단하게 MaterialApp으로 사용하는 듯 하다.
StatelessWidget
class CounterPage extends StatelessWidget {
const CounterPage();
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CounterCubit(),
child: const CounterView(),
);
}
}
Build 메서드는 StatelessWidget의 메서드로 위젯을 화면에 어떻게 그릴지 결정하는 메서드이다.
BuildContext context는 위젯의 위치와 상태에 대한 정보를 담고 있는 객체로 위젯 트리에서 현재 위젯이 어디에 위치하는지, 상위 위젯의 데이터 등을 알 수 있게 해준다.
우리는 BlocProbider 형식으로 Widget을 반환하게 된다. BlocProvider는 Bloc 상태 관리를 위한 위젯으로, CounterCubit과 같은 Cubit이나 Bloc객체를 자식 위젯에게 제공하는 역할을 한다.(아직 CounterCubit 선언 및 생성하지 않았음)
BlocProvider
create 속성은 Bloc 또는 Cubit을 생성하는 함수이다.
(_)는 파라미터 이름을 사용하지 않겠다는 의미로 create의 파라미터로 BuildContext context가 있지만, 이 값은 코드에서 사용하지 않기 때문에 _로 처리하여 사용하지 않겠다고 선언한다.
CounterCubit은 상태를 관리하는 로직을 처리하는 클래스로 여기서는 CounterCubit()이 생성되어 BlocProvider를 통해 하위 위젯들로 전달됩니다.
child는 BlocProvider가 제공하는 상태 관리를 CounterView 위젯에 전달하고 CounterView는 상태를 기반으로 UI를 그리는 위젯으로 CounterCubit을 통해 상태 관리된 데이터를 사용하여 UI를 업데이트하게 된다.(아직 CounterView 선언 및 생성하지 않았음)
Cubit
class CounterCubit extends Cubit<int> {
// 초기 상태는 0으로 정의
CounterCubit() : super(0);
/// Add 1 to the current state.
void increment() => emit(state + 1);
/// Subtract 1 from the current state.
void decrement() => emit(state - 1);
}
Cubit은 Bloc 패턴의 일종으로 상태를 간단하게 관리하는 데 사용된다. Cubit은 Stream을 사용하여 상태의 변화를 방출하고 구독한 위젯들이 이 상태 변화를 받아 처리할 수 있도록 한다.
Cubit은 상태 변경을 관리하는 객체로 이 객체가 상태 값을 가지고 있으며 이 상태 값을 업데이트하거나 변경할 수 있는 메서드를 제공한다.
Cubit<int>
Cubit<int>는 Cubit 클래스의 제네릭 타입이다. Cubit은 상태의 타입을 제네릭으로 받는데 튜토리얼에서는 int 타입을 사용하여 이 Cubit이 관리할 상태가 정수형이라는 것을 나타내고 있다.
Emit
Cubit을 사용하면 상태가 변경될 때마다 그 상태를 방출(emit) 할 수 있다.
-> 뭔가 Combine같다는 느낌이 든다!
이렇게 여차저차 공부하면서 만든 내 첫 플루터 앱이 완성되었다!!