말랑한 하루

[Flutter] 애플리케이션 강제 업데이트 본문

개발/Flutter

[Flutter] 애플리케이션 강제 업데이트

지수는말랑이 2024. 3. 5. 00:12
반응형

사용자들에게 비공개 테스트를 진행하면서, 새로운 버전을 업데이트하여 제공해야 하는 경우가 발생합니다. 하지만 사용자들은 앱을 설치하고 나면, 애플리케이션 페이지로 다시 접속할 일이 없죠. 리뷰를 작성하지 않는다면요!

 

특정 앱 버전에서 문제가 발생하는 경우, 또는 앱의 버그를 고치는 경우가 새로운 버전이 나오는 시기이기 때문에 사용자들에게 앱이 업데이트가 되었음을 명시적으로 알리고, 업데이트를 진행할 수 있도록 안내해야 합니다.

 

그래서 이번 포스트는 강제 업데이트를 적용시키기 위해 어떤 과정을 거쳐야 하는지, 필요한 것은 무엇인지 알아보겠습니다.

 

🥕 구현

강제 업데이트를 구현해 내기 위한 최적의 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을 사용해야합니다.

 

반응형
Comments