말랑한 하루
[Flutter] (Project) MapleApp: 32. 비공개 테스트-1 본문
비공개 테스트는 약 20명의 테스터를 선발하여 2주 동안 진행하는 과정이다. 신입 개발자 계정은 바로 프로모션으로 출시할 수 없고 이 과정을 반드시 거쳐야 한다는 흠이 있다.
2023년 12월 이후 개정된 사안으로, 애플리케이션 출시 전 앱 자체의 성능과 개선 사항을 해결하면서 사용자에게 맞는 앱으로 탄생할 수 있게 하자는 취지이다.
이런 비공개 테스트를 약 2주 동안 진행하면서, 내가 고쳐왔던 그리고 고치고 싶었던 내용과 보안 이슈 등에 관하여 서술할 예정이고, 추후 고도화를 위한 작업을 진행하면서 새로운 버전의 앱을 출시하고, 사용자들로 하여금 새로운 버전을 다운 받을 수 있게 하는 행위까지 경험해보려 한다.
🐇 dio request exception
CRUD를 진행하면서 한 요청에 하나의 쿼리를 진행하는 과정엔 AsyncValue.guard를 사용하여 각 행동에 대해 value/error/loading을 지정할 수 있다.
하지만 내 프로젝트 같은 경우 Read request 밖에 없기 때문에, 한 함수에 모든 get 요청이 들어가 있다. 이 경우, 각 request에 대해 try/catch를 삽입하며 잘못된 경우 에러를 반환하고자 한다.
try/catch를 적용하기 전 코드는 이렇다. 다양한 구간이 포함되어 있지만, 간략하게 보여드리기 위함이다.
//AsyncNotifier<MainData>
Future<MainData> _fetch() async {
Response response = await dio.get(dotenv.get('PATH'));
final data = Data.fromJson(response.data);
return MainData(data: data);
}
// outpage
final asyncMainData = ref.watch(asyncCharacterProvider);
return switch (asyncMainData) {
AsyncData(:final value) => DataInfo(info: value),
AsyncError(:final error) => MainErrorPage(message: error),
_ => const LoadingSpinner(),
};
함수의 특성 상 MainData 객체를 반환해야 하고, ref.watch로 Provider를 감시하는 경우 MainData가 반환 되기 전 까지 switch 구문의 _ ⇒ 상태에 해당하며, MainData가 반환되면 AsyncData를 표출한다.
그러나 이 구간에서는 AsyncError 개체를 반환하는 구간이 없어, AsyncNotifier에서 request 요청에 실패하는 경우 앱 구동이 멈추게 된다. 이 경우를 try/catch를 적용하여 error가 생기는 경우 AsyncError에 맞는 데이터로 반환하여 MainErrorPage를 표출하려 한다.
AsyncError가 수신하기 위한 Future _fetch 함수의 반환 방법은 다음을 사용하면 됨을 찾았다.
🍒 Future.error('message')
그러나 우리는 dio 객체에서 Response를 가로채 모든 오류에 대해 DioException을 반환하도록 하였고, Future 객체를 반환하는 Notifier를 AsyncValue를 활용하여 관리하고 있기 때문에 Future.error에 어떤 객체를 실어 보내도 AsyncError로 넘겨줄 수 없었다.
그 이유는 해당 값을 바라보는 NotifierProvider가 fetch 함수에서 에러가 발생할 때, 우리는 Future에 특정 개체를 담아서 보내기 때문이다. 그래서 AsyncValue는 개체에 대한 정보가 없는 상태이므로, AsyncError에서 Exception: variable is not found 오류를 표출하게 된다.
그래서 AsyncError의 error 객체를 사용하지 않고, 최대한 각 요청 페이지를 분리하여 메세지를 넘겨주는 방식으로 처리했다.
🐇 TextField 입력 제한
Play Console에 appbundle을 등록하면, 사전 출시 보고서를 받을 수 있다. 안정성/실적/접근성/스크린샷/보안 및 신뢰에 관하여 오류/경고/사소한 문제로 나누어 알려준다. 그 중, TextField에 이모지가 입력되면서, 이모지에 대해 콘텐츠 라벨이 지정되지 않는 문제에 대해 경고했다.
이모지는 446개가 넘고, TextField에 입력해야 할 정보는 영대소문자, 숫자, 한글이 끝이기 때문에 이외 문자는 입력하지 못하게 막아야했다. 그래서 찾아낸 속성이 inputFormatter이다.
※ reference : https://api.flutter.dev/flutter/services/TextInputFormatter-class.html
inputFormatter는 TextInputFormatter class를 List로 받는 개체이다. 지정한 TextInputFormatter에 따라 달라지는데, TextInputFormatter에서 제공되는 withFunction method를 사용하면 TextFiled에 입력되는 oldValue/newValue를 비교하고 사용자의 요구 조건에 따라 처리할 수 있다.
※ reference : https://api.flutter.dev/flutter/services/FilteringTextInputFormatter-class.html
비슷한 행위를 진행하는 class의 예제를 보면 withFunction method의 활용을 볼 수 있다. 내가 지정한 패턴에 맞춰 newValue를 판단하고, old/new Value를 반환하도록 설정할 수 있다. 내가 사용한 코드는 다음과 같다.
TextInputFormatter.withFunction(
(oldValue, newValue) {
return newValue.text.contains(
RegExp(r'[^0-9a-zA-Zㄱ-ㅎ가-힣]'))
? oldValue
: newValue;
})
🐇 콘텐츠 라벨 지정
아이콘/이미지에는 스크린 리더가 읽을 수 있는 라벨이 있어야 합니다. 대표적인 뒤로 가기 버튼인 경우 해당됩니다. 애플리케이션 내 아이콘/이미지를 사용하고 있다면 모든 Widget에 관해 진행하는 것을 추천합니다.
※ reference : https://api.flutter.dev/flutter/widgets/Image/semanticLabel.html
콘텐츠 라벨 지정은 Icon/Image Widget의 semanticLabel 속성을 활용하여 다음과 같이 할 수 있습니다.
Icon(
Icons.navigate_before_rounded,
color: Colors.white,
semanticLabel: '뒤로 가기 버튼',
),
또한 콘텐츠 라벨은 Semantic Widget을 활용하여 설정할 수 있다.
※ reference : https://api.flutter.dev/flutter/widgets/Semantics-class.html
Image나 Icon과 같은 Widget은 내부 semanticLabel을 활용하여 지정할 수 있지만, 대게 많은 요소는 존재하지 않고 설명할 수 있는 방법도 많지 않다.
그래서 Flutter 에서는 Semantic Widget을 제공하는 것이다. 내부 label 속성을 활용하여 요소에 대해 설명하고, 해당 요소가 button인지 readOnly인지 등의 내용을 assitive technologies에게 알려줄 수 있다.
🐇 GestureDetector 활용하기
애플리케이션을 사용하다 보면, NavigationBar는 접근성이 뛰어나지만 상단 SelectTabBar는 높은 위치에 존재하여 접근성에 매우 불리하다. 사용자로 하여금 ViewArea에 존재해야 하기 때문이다. 그래서 사용자 제스쳐를 통해 간단하게 SelectTabBar를 조정할 수 있도록 만드려고 한다.
GestureDetector(
onPanUpdate: (details) {
if (details.delta.dx < -3) {
ref.read(skillSelectTabProvider.notifier).update((state) => 'v');
}
},
)
onPanUpdate 속성은 사용자가 포인터를 변경 시킬 때마다 호출된다. 원하는 임계값에 도달했을 때, SelectTabBar의 속성 값을 변경하여 주면 된다.
'개발 > Flutter' 카테고리의 다른 글
[Flutter] Firebase Remote Config (0) | 2024.03.04 |
---|---|
[Flutter] (Project) MapleApp: 33. 비공개 테스트-2 (0) | 2024.03.02 |
[Flutter] (Project) MapleApp: 31. 심사 통과 (0) | 2024.02.29 |
[Flutter] Infinity Carousel (무한 캐러셀) (0) | 2024.02.23 |
[Flutter] Infinity Scroll (무한 스크롤) (0) | 2024.02.20 |