💡 Intro
레벨4 프로젝트를 진행하고 있는데 출시했던 앱에서 로그인 기능 문제가 생겼다. 앱을 사용하는 사용자의 입장에서 생각해보면 내가 사용하던 앱이 비정상종료가 계속된다? 그러면 나는 그 앱을 더 사용하지 않을 것 같다. 슬프게도 실사용자가 있는 앱은 아니지만.. 굉장히 불편한 요소 중 하나이며 무조건 고쳐야 하는 버그라고 생각한다.
고쳤다는 가정을 해도 한 가지 문제가 더 남는다. 만약 사용자가 고치기 이전 버전을 사용하고 있다면? 사용자가 플레이스토어에 들어가 직접 앱을 업데이트 하지 않는 이상 발생했던 문제는 사라지지 않는다. 이를 해결하기 위해 많은 앱에서 업데이트를 강제하는 방법을 사용하는 것 같다.
공식문서를 보면 API레벨 21 이상의 기기에서는 플레이스토어 인앱 업데이트를 지원한다. 하지만 실제로 사용해봤을 때 그렇게 맘에들지는 않았다. 실제로 앱을 사용할 때에도 인앱 업데이트를 한 적은 없는 것 같고..
그래서 더 좋은 방법이 있나 찾아보았고, Firebase Remote Config를 활용하는 방법이 있었다.
아래나오는 내용은 프로젝트에 Firebase를 연결했다는 가정하에 진행하도록 하겠다.
Firebase Remote Config
1️⃣ Firebase Console
Firebase console에 등록한 프로젝트로 들어가 Remote Config로 들어가면 위와 같은 페이지가 나온다.
매개변수 추가를 클릭하면 위와 같은 페이지가 나온다.
매개변수 이름과 설명, 기본 값을 설정할 수 있다. 매개변수의 이름과 기본 값은 필수지만 설명은 선택사항이다. Remote Config에서 해당 변수가 어떤 역할을 하는지 조금 더 쉽게 보기 위해 설명을 적을 수도 있다.
데이터의 유형은 문자열, 숫자, 불린, JSON
이 가능하다. (데이터 유형을 알맞게 잘 사용하면 좀 더 다양하게 활용할 수 있을 것 같다.)
그냥 변수 하나를 추가해준다고 생각하면 쉬운데, 어디서나 사용가능한 전역 변수인 것이다. Firebase Remote Config에 등록해두면 앱을 실행하는 각 휴대폰에서 이 값을 참조해서 현재 버전보다 참조한 버전이 높다면 강제 업데이트를 요청할 수 있다.
2️⃣ Code
private fun updateCheck() {
val remoteConfig = Firebase.remoteConfig
val configSettings = remoteConfigSettings {
minimumFetchIntervalInSeconds = 3600
}
remoteConfig.setConfigSettingsAsync(configSettings)
remoteConfig.setDefaultsAsync(mapOf(MIN_VERSION to DEFAULT_VERSION))
val curVersion = packageManager.getPackageInfoCompat(packageName).versionName
remoteConfig.fetchAndActivate().addOnCompleteListener {
if (it.isSuccessful) {
val minVersion = remoteConfig.getString(MIN_VERSION)
if (minVersion > curVersion) {
showUpdateDialog()
} else {
...
}
}
}
}
...
companion object {
const val MIN_VERSION = "version"
const val DEFAULT_VERSION = "0.0.0"
}
업데이트를 체크하는 로직은 위와 같이 단순하다. 코드를 하나하나씩 뜯어보자.
val remoteConfig = Firebase.remoteConfig
val configSettings = remoteConfigSettings {
minimumFetchIntervalInSeconds = 3600
}
remoteConfig.setConfigSettingsAsync(configSettings)
remoteConfig.setDefaultsAsync(mapOf(MIN_VERSION to DEFAULT_VERSION))
val curVersion = packageManager.getPackageInfoCompat(packageName).versionName
firebase 인스턴스를 가져온 뒤 최신 데이터를 가져오는 주기를 1시간으로 설정한다. 만약 설정한 주기보다 일찍 값을 가져오려고 한다면 이전에 가져왔던 값을 반환한다.
플레이스토어의 업데이트를 우리가 컨트롤 할 수 없기 때문에 주기가 짧을수록 좋겠지만, 주기를 0초로 설정한다고 해서 정말 0초로 갱신되지는 않는다. 또, 만약 자주 호출하게 된다면 Remote Config 사용량이 제한된다..
version
이라는 변수에 대한 값을 가져올 때 만약 등록되어 있는 데이터가 없다면 기본 값을 가져오도록 세팅해준다.
현재 앱을 실행하는 버전이름을 가져온다.
remoteConfig.fetchAndActivate().addOnCompleteListener {
if (it.isSuccessful) {
val minVersion = remoteConfig.getString(MIN_VERSION)
if (minVersion > curVersion) {
showUpdateDialog()
} else {
...
}
}
}
// 참고
@NonNull
public Task<Boolean> fetchAndActivate() {
return fetch().onSuccessTask(executor, (unusedVoid) -> activate());
}
Firebase Remote Config에 등록한 값을 가져오려면 fetch()
메서드를 호출해야 한다. fetch()
메서드를 사용하면 설정한 모든 값을 가져와 원격 구성 객체에 저장한다.
가져온 매개변수 값을 앱에 적용하려면 activate()
메서드를 호출해야 한다. 호출 한 번으로 값을 가져오고 활성화하려는 경우에는 fetchAndActivate()
메서드를 사용할 수 있다.
가져온 버전과 현재 앱을 실행하는 버전을 비교하여 실행하는 앱의 버전이 가져온 버전보다 낮다면 업데이트를 요청하는 다이얼로그를 추가한다.
이런 방식을 사용하면 앱을 업데이트 할 때마다가 아니라 내가 원하는 시점에만 업데이트 혹은 다른 요청을 할 수 있다. 그럼 사용자가 사용하는 면으로 봤을 때도 더 부드러운 앱이 되지 않을까? (횟수 제한을 넘어서면 어떻게 해야할지는 아직 잘 모르겠다…)
마무리하며
앱을 사용하는 사용자의 입장에서 생각해보면 필요한 경우에 사용하기 위해 들어간 앱에서 업데이트를 요청한다..? 그것또한 불편하다. 업데이트 하는동안 앱을 사용하지 못하고 기다려야 하니까. 그래서 최대한 앱을 출시하기 전에 꼼꼼하게 QA를 진행하여 강제 업데이트를 요청하는 경우가 없어야하겠다고 생각했다.
from 공식문서..ㅋㅋ
irebase Remote Config를 사용해보면서 느낀점은 일단 사용하기가 너무 쉽다. Firebase는 진짜 짱짱맨인 느낌? 없는게 없다. 그래서 정말 좋았는데 한 가지 단점이 있었다.
앱의 업데이트 심사 요청까지는 우리가 제어할 수 있는 영역이지만 앱이 업데이트 되는 것은 우리가 제어할 수 있는 부분이 아니다. 버전이 새로 나오는 것과 Firebase Remote Config에서 관리하는 값을 변경하는 것을 동시에 진행할 수 있지 않다는 것이다. 업데이트가 필요한 경우는 팀마다 어떻게 정의하는가에 따라 다르겠지만, 안드로이드 공식문서에서는 다음과 같이 경우를 나눠놨다.
- 일부 UI 개선: 낮은 우선순위 업데이트. 유연한 업데이트도 즉시 업데이트도 요청하지 않습니다.
- 성능 개선: 중간 우선순위 업데이트. 유연한 업데이트를 요청합니다.
- 중요 보안 업데이트: 높은 우선순위 업데이트. 즉시 업데이트해야 합니다.
중요 보안 업데이트 이외에도 요구사항이 변경되어 이전에 사용하던 API가 구버전과 호환되지 않는 경우의 업데이트라면? 앱이 업데이트 되는 시기와 Firebase Remote Config에서 관리하는 값을 업데이트 하는 시기 사이에 앱을 사용하는 사용자는 아마 영문도 모르고 앱이 비정상 종료할 가능성이 있다고 생각한다.
그래서 이 부분도 트레이드 오프의 영역일까? 하는 생각이 든다.
추가적으로 Firebase Remote Config 공식문서를 쭉 읽어보며 느낀점은 이것에 너무 의존하지 않는 것이 좋겠다는 것이다. 일단 많은 요청이 들어온경우 아예 사용하지 못하는 것은 말도 안된다. 이게 가장 큰 것 같다. 그리고 그 ‘많다’라는 말이 너무 추상적인 지표이기 때문에 어느정도부터를 많은 것으로 보는지 가늠할 수가 없다.
참고
https://medium.com/harrythegreat/android-remote-config-잘-활용하기-f8b04ef2645a
https://firebase.google.com/docs/remote-config/get-started?hl=ko&platform=android