IAuthFacade
추상화는 이전 부분 이후에 적용됩니다. 이 인터페이스를 사용하면 인증이 실제로 어떻게 구현되는지에 대한 단서 없이도 로그인 form 애플리케이션 로직을 작성할 수 있습니다. states와 events를 사용하여 UI가 BLoC와 통신하는 방법을 모델링해 보겠습니다.
다이어그램에 표시된 것처럼 BLoC는 원시(raw) 데이터가 포함된 UI로부터 events를 입력받고 무엇보다도 검증된 데이터가 포함된 sates를 출력합니다. 이제 SignInFormBloc에 대한 클래스를 만들어 보겠습니다.
BLoC 라이브러리에 아직 익숙하지 않다면 먼저 이 튜토리얼을 확인하세요.
파일 생성에 소요되는 시간을 크게 줄이려면 VS Code 또는 IntelliJ용 확장 프로그램을 사용하세요.
flutter_bloc을 종속성으로 추가해 보겠습니다.
dependencies:
...
flutter_bloc: ^3.2.0
application/auth/sign_in_form 경로가 존재하도록 새 디렉터리를 만듭니다. 위에서 언급한 확장 프로그램을 사용하여 "sign_in_form"이라는 이름의 BloC을 만듭니다.
events와 states가 프레젠테이션 계층의 일부라는 점을 기억하는 것이 중요합니다. 따라서 Flutter 위젯에서 진행되는 작업과 밀접하게 결합되어 있으면 괜찮습니다.
로그인 form의 UI에서는 어떤 이벤트가 발생할 수 있을까요? 다른 로그인 버튼을 누르는 것 외에도 DDD를 따르지 않는 한 일반적으로 UI에서 직접 처리되는 두 가지 다른 이벤트(이메일 및 비밀번호 입력 변경)도 있습니다. union으로 표현하면 이벤트는 다음과 같습니다.
part of 'sign_in_form_bloc.dart';
abstract class SignInFormEvent with _$SignInFormEvent {
// Notice that these events take in "raw" unvalidated Strings
const factory SignInFormEvent.emailChanged(String emailStr) = EmailChanged;
const factory SignInFormEvent.passwordChanged(String passwordStr) =
PasswordChanged;
const factory SignInFormEvent.registerWithEmailAndPasswordPressed() =
RegisterWithEmailAndPasswordPressed;
const factory SignInFormEvent.signInWithEmailAndPasswordPressed() =
SignInWithEmailAndPasswordPressed;
const factory SignInFormEvent.signInWithGooglePressed() =
SignInWithGooglePressed;
}
part of
때문에 여기서는 일반적인part '*.freezed.dart'
문을 지정하지 않습니다.
BLoC는 일반적으로 state의 여러 하위 클래스(또는 union cases)를 출력합니다. 이는 단일 상태 클래스만 있는 UI 데이터의 유효성을 검사하는 BLoC와는 다릅니다.
로그인 양식의 UI에 다시 전달하려면 무엇이 필요합니까?
1. Validated values
TextFormFields
에 오류 메시지를 표시할 수 있도록 검증된 EmailAddress
및 Password
값 개체를 다시 전달하고 싶습니다.
2. Auth progress
진행률 인디케이터를 표시하는 것은 당연한 일이므로 bool isSubmitting
도 다시 전달해야 합니다.
3. Success or error backend response
백엔드에서 문제가 발생할 때 에러 Snackbar
를 표시하려면 IAuthFacade
에서 반환된 Either<AuthFailure, Unit>
을 다시 전달해야 합니다. 우리는 이를 authFailureOrSuccess
라고 부르겠습니다. 인증 백엔드 "response"이라고 생각하면 됩니다.
그러나 처음에는 사용자가 버튼을 누를 때까지 응답이 없습니다. 처음에는 authFailureOrSuccess
필드에 null
을 할당할 수 있지만 이것이 좋지 않다는 것을 알고 있습니다.
훨씬 더 나은 옵션은 Option
을 사용하는 것입니다. Either
와 마찬가지로 Some
과 None
이라는 두 값의 union입니다. 이는 null
이 None
union cases로 대체되는 일종의 null을 허용하지 않는 유형입니다. Some
union case만이 Either<AuthFailure, Unit>
이 될 값을 보유합니다.
4. Whether or not to show input error messages
마지막으로 로그인/등록 버튼을 처음 누른 후에만 TextFormFields
아래에 입력 유효성 검사 오류 메시지를 표시하려고 합니다. 이는 bool showErrorMessages
내부의 UI로 다시 전달됩니다.
part of 'sign_in_form_bloc.dart';
abstract class SignInFormState with _$SignInFormState {
const factory SignInFormState({
EmailAddress emailAddress,
Password password,
bool showErrorMessages,
bool isSubmitting,
Option<Either<AuthFailure, Unit>> authFailureOrSuccessOption,
}) = _SignInFormState;
factory SignInFormState.initial() => SignInFormState(
emailAddress: EmailAddress(''),
password: Password(''),
showErrorMessages: false,
isSubmitting: false,
authFailureOrSuccessOption: none(),
);
}
events 및 state 데이터 클래스는 프레젠테이션 계층에 있는 뷰 모델로 볼 수 있지만 SignInFormBloc
클래스 자체는 애플리케이션 로직을 수행합니다. 즉, 다른 계층이 함께 작동하도록 조정합니다.
이곳은 원시 String
이 검증된 ValueObject
로 바뀌고 IAuthFacade
의 메서드가 호출되는 곳입니다. 여기서 수행되는 로직은 수신 이벤트를 상태로 변환하는 데 중점을 둡니다.
다음 글을 위해 로직 작성을 남겨두겠지만, 최소한 파일을 설정해 보겠습니다. 확장 프로그램에 의해 생성된 기본 events 및 states에 대한 변경 사항으로 인해 freezed에 대한 part
문을 추가하고 initialState
를 변경해야 합니다. 또한, IAuthFacade
종속성을 위한 필드를 추가하겠습니다.
part 'sign_in_form_event.dart';
part 'sign_in_form_state.dart';
part 'sign_in_form_bloc.freezed.dart';
class SignInFormBloc extends Bloc<SignInFormEvent, SignInFormState> {
final IAuthFacade _authFacade;
SignInFormBloc(this._authFacade);
SignInFormState get initialState => SignInFormState.initial();
Stream<SignInFormState> mapEventToState(
SignInFormEvent event,
) async* {
// TODO: Implement
}
}
이제 마침내 코드 생성을 시작하기 위해 가장 선호하는 명령을 실행할 시간입니다.
flutter pub run build_runner watch --delete-conflicting-outputs
우리는 UI에서 수행할 수 있는 events와 BLoC가 UI에 값을 다시 전달하는 방법인 state를 모델링하여 이 부분에서 중요한 단계를 수행했습니다. 이 모든 것이 준비되면 바로 애플리케이션 로직 작성으로 넘어갈 수 있습니다.