말랑한 하루
[Flutter] 애플리케이션 강제 업데이트 본문
사용자들에게 비공개 테스트를 진행하면서, 새로운 버전을 업데이트하여 제공해야 하는 경우가 발생합니다. 하지만 사용자들은 앱을 설치하고 나면, 애플리케이션 페이지로 다시 접속할 일이 없죠. 리뷰를 작성하지 않는다면요!
특정 앱 버전에서 문제가 발생하는 경우, 또는 앱의 버그를 고치는 경우가 새로운 버전이 나오는 시기이기 때문에 사용자들에게 앱이 업데이트가 되었음을 명시적으로 알리고, 업데이트를 진행할 수 있도록 안내해야 합니다.
그래서 이번 포스트는 강제 업데이트를 적용시키기 위해 어떤 과정을 거쳐야 하는지, 필요한 것은 무엇인지 알아보겠습니다.
🥕 구현
강제 업데이트를 구현해 내기 위한 최적의 UI는 애플리케이션 로딩 화면이라고 생각한다.
애플리케이션에서 필요한 정보를 가져오는 등, 내부 비즈니스 로직이 이루어지기 이전 대기 시간이 필요한 경우가 있다. 이런 경우, 로딩 화면을 활용하여 그 시간을 벌어 놀 수 있기 때문이다.
또한, 로딩 화면은 이 애플리케이션이 구동 중임을 명확하게 표기해주므로 UX향상에 도움이 된다고 생각한다.
사용자가 애플리케이션을 활용하기 전에, 버전이 최신인지 확인하고 PlayStore에서 업데이트를 진행할 수 있도록 안내 하는 설계로 로딩 화면을 구현해 가보겠다.
버전에 대한 관리는 Firebase의 Remote Config를 활용하는데, Flutter 프로젝트에 이와 관련된 설치부터 사용법까지는 https://malangdidoo.tistory.com/247를 통해 확인할 수 있다.
기본적으로 로딩 화면은 개발자가 설정한 임계값 시간만큼 대기한 뒤 동작이 이루어질 수 있도록 설계된다.
그 과정에서 사용자는 뒤로 가기를 통해 애플리케이션을 종료할 수 없어야 한다는 것이 특징이기도 하다.
🍒 기본 설계
기본적으로 화면이 구성될 때 RemoteConfig에 대한 값과 현재 버전에 대한 값을 비교하고 다음 페이지로 진행될 지, 새로운 버전을 다운로드 받을 수 있게 Redirect시킬 지 정할 수 있다.
Timer를 활용해 로딩 화면을 보여주는 최소 시간을 정해놓고 위 로직을 수행한다고 생각하면 편하다.
@override
void initState() {
super.initState();
checkApplicationVersion();
}
void checkApplicationVersion() async {
...
Timer(Duration(milliseconds: 1500), () {
if (condition) {
// navigatior redirect
context.go('/');
} else {
// google store redirect
}
});
}
주의할 점은, initState에 async/await 키워드를 사용하면 안되므로, 외부 함수로 구현하여 호출해준다.
🍒 버전 정보 가져오기
먼저 Firebase의 Remote Config에서 최신 버전에 대한 정보를 가져온다.
final remoteConfig = FirebaseRemoteConfig.instance;
remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: Duration(milliseconds: 1500),
minimumFetchInterval: Duration(hours: 24),
));
remoteConfig.setDefaults({
'latest_version': '0.0.0',
});
await remoteConfig.fetchAndActivate();
var remoteLatestVersion = remoteConfig.getString('latest_version');
RemoteConfigSettings Class의 경우 fetchTimeout과 minimumFetchInterval에 대한 값을 필수로 요구한다.
fetchTimeout은 remoteConfig값을 불러오는 대기시간을 의미하고, 이 시간이 넘어가면 setDefaults를 통해 설정한 값을 사용하게 된다.
현재 애플리케이션에서 버전 정보를 가져오는 라이브러리는 다양하다. 그 중, 가장 가시적인 방법으로 정보를 가져오는 yaml을 사용해보려 한다.
※ reference : https://pub.dev/packages/yaml
yaml 패키지는 yaml에서 넘어온 값을 loadYaml() 메소드로 파싱하여 jsonDecode처럼 값을 사용할 수 있다.
var doc = loadYaml("YAML: YAML Ain't Markup Language");
print(doc['YAML']);
그래서 pubspect.yaml의 assets에 pubspec.yaml을 추가해서, rootBundle을 활용해 현재 버전에 대한 정보를 가져오면 된다.
단, 현재 버전에 대한 정보는 0.0.0+0으로 표기되기 때문에 데이터 정제가 필요하다.
이 방법은 플랫폼에 구애 받지 않고 사용할 수 있다.
var originVersion = loadYaml(doc)['version'].toString().split('+')[0];
🍒 뒤로가기 버튼 막기
기존에 제공되었던 WillPopScope는 Android가 발전함에 따라 declared되었고, 현재 PopScope로 남아있다.
※ reference : https://api.flutter.dev/flutter/widgets/PopScope-class.html
canPop 속성을 활용하여 간단하게 막을 수 있으며, onPopInvoked를 통해 뒤로 가기가 호출되었을 때 사용자 지정 함수를 호출시킬 수 있다.
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false,
child: SafeArea(
child: Scaffold(
body: Center(
child: (Image.asset('assets/maplespy_icon.png')),
),
),
),
);
}
🍒 store_redirect
※ reference : https://pub.dev/packages/store_redirect
flutter pub add store_redirect
store_redirect는 Google/Apple의 Store내 App 페이지로 Redirect 기능을 지원하는 Flutter 플러그인입니다.
사용 방법은 간단합니다! redirect 메소드를 활용해 androidAppId에 패키지 이름을 작성해주면 됩니다!
StoreRedirect.redirect(androidAppId: 'com.example.exam');
사용자에게는 앱 업데이트에 대한 안내문을 모달로 제공하고, 사용자의 선택에 따라 앱이 종료되거나 스토어로 이동할 수 있게 버튼을 추가해 주는 것이 향상된 UX를 제공할 것입니다.
하지만 store_redirect의 경우,
storeredirect\\StoreRedirectPlugin.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
와 같은 오류로 인해 다른 방법을 찾아야 했다.
🍒 url_launcher
※ reference : https://pub.dev/packages/url_launcher
flutter pub add url_launcher
사용 방법은 간단합니다!
if (Platform.isAndroid || Platform.isIOS) {
final appId = Platform.isAndroid
? dotenv.get('ANDROID_PACKAGE_NAME')
: dotenv.get('IOS_APP_ID');
final url = Uri.parse(
Platform.isAndroid
? 'market://details?id=${appId}'
: '<https://apps.apple.com/app/id${appId}>',
);
launchUrl(
url,
mode: LaunchMode.externalApplication,
);
}
단, url_launcher 6.2.5 version의 경우, Android compilerVersion 34를 요구하므로, FlutterSDK 3.19.0 version을 사용해야합니다.
'개발 > Flutter' 카테고리의 다른 글
[Flutter] 기기 데이터 캐싱 (0) | 2024.03.13 |
---|---|
[Flutter] Firebase Cloud Firestore (0) | 2024.03.12 |
[Flutter] Firebase Remote Config (0) | 2024.03.04 |
[Flutter] (Project) MapleApp: 33. 비공개 테스트-2 (0) | 2024.03.02 |
[Flutter] (Project) MapleApp: 32. 비공개 테스트-1 (0) | 2024.02.29 |