Android 앱 원격 설정과 기능 플래그 완벽 가이드 — Firebase Remote Config, Feature Flag, 단계적 출시와 롤백 전략
Android 앱을 Google Play에 출시하고 운영하다 보면 개발 과정에서는 예상하지 못했던 문제가 발생합니다.
예를 들어 새로운 검색 기능을 추가한 뒤 다음과 같은 상황이 발생했다고 가정하겠습니다.
특정 Android 버전에서 검색 기능이 느려진다.
신규 검색 알고리즘에서 일부 결과가 누락된다.
새롭게 추가한 프리미엄 안내 화면의 오류가 발생한다.
광고 노출 설정을 변경해야 한다.
특정 기능을 일부 사용자에게만 먼저 제공하고 싶다.
서버 장애가 발생했을 때 관련 기능을 일시적으로 비활성화해야 한다.
일반적인 Android 앱에서는 이러한 문제를 해결하려면 코드를 수정하고 새로운 버전을 Google Play에 배포해야 합니다.
하지만 앱 업데이트에는 시간이 필요합니다.
사용자가 새로운 버전을 설치하기까지 기다려야 하며, 이미 설치된 앱을 즉시 변경할 수도 없습니다.
이러한 문제를 줄이기 위해 활용할 수 있는 기능이 Firebase Remote Config와 Feature Flag입니다.
이번 글에서는 Remote Config를 이용해 Android 앱의 동작을 원격으로 제어하고, 기능 플래그를 활용해 안전하게 새로운 기능을 출시하는 방법을 살펴보겠습니다.
개발 환경: Kotlin, XML ViewBinding, MVVM, Coroutine, Firebase Remote Config, Firebase Analytics, Google Play Console. 라이브러리 버전과 Firebase Console 메뉴는 실제 사용 환경에 따라 확인해야 합니다.
1. Firebase Remote Config란?
Firebase Remote Config는 앱의 일부 설정값을 Firebase 서버에서 관리할 수 있도록 하는 서비스입니다.
앱에서 설정값을 가져와 적용하면 Google Play에 새로운 버전을 배포하지 않고도 미리 구현된 기능의 동작을 변경할 수 있습니다.
예를 들어 다음과 같은 설정을 관리할 수 있습니다.
new_search_enabled = true
premium_banner_enabled = false
search_result_limit = 100
maintenance_mode = false여기서 중요한 점은 Remote Config가 새로운 코드를 앱에 내려주는 기능이 아니라는 것입니다.
앱에 이미 구현되어 있는 기능을 설정값에 따라 활성화하거나 비활성화하는 방식입니다.
따라서 앱에 존재하지 않는 기능을 Remote Config만으로 새롭게 추가할 수는 없습니다.
Remote Config와 Feature Flag
2. Feature Flag란?
Feature Flag는 특정 기능의 활성화 여부를 설정값으로 제어하는 방법입니다.
예를 들어 새로운 검색 기능을 개발했다고 가정하겠습니다.
일반적인 구현에서는 다음과 같이 새 기능을 직접 호출할 수 있습니다.
fun searchBirds(keyword: String) {
searchWithNewAlgorithm(keyword)
}하지만 새로운 알고리즘에 문제가 발생하면 앱을 업데이트해야 합니다.
Feature Flag를 사용하면 다음과 같이 구성할 수 있습니다.
fun searchBirds(keyword: String) {
if (featureFlags.isNewSearchEnabled) {
searchWithNewAlgorithm(keyword)
} else {
searchWithLegacyAlgorithm(keyword)
}
}이렇게 하면 새로운 검색 기능에 문제가 발생했을 때 기존 검색 알고리즘으로 전환할 수 있습니다.
단, 기존 검색 알고리즘이 앱에 남아 있어야 합니다.
3. Remote Config와 Feature Flag의 차이
| 구분 | Remote Config | Feature Flag |
|---|---|---|
| 개념 | 원격 설정값 관리 서비스 | 기능 활성화 제어 패턴 |
| 목적 | 설정값 변경 | 기능 출시 및 비활성화 |
| 구현 방식 | Firebase SDK | 조건문 및 기능 분기 |
| 활용 | 광고 설정, UI 문구, 기능 옵션 | 신규 기능 출시, 긴급 차단 |
| 관계 | Feature Flag 구현에 활용 가능 | Remote Config로 관리 가능 |
즉, Firebase Remote Config는 Feature Flag를 구현하는 방법 중 하나입니다.
Firebase Remote Config 설치
4. Firebase 프로젝트 연결
Remote Config를 사용하려면 Firebase 프로젝트에 Android 앱이 연결되어 있어야 합니다.
이미 Firebase Analytics나 Crashlytics를 사용하고 있다면 기존 Firebase 프로젝트를 활용할 수 있습니다.
일반적인 설정 순서는 다음과 같습니다.
Firebase Console에서 프로젝트 선택
Android 앱 등록 확인
google-services.json확인Google Services Gradle 플러그인 적용
Firebase Remote Config SDK 추가
Firebase Console에서 매개변수 생성
5. Gradle 의존성 추가
앱 모듈의 build.gradle.kts에 Firebase Remote Config를 추가합니다.
dependencies {
implementation(
platform(
"com.google.firebase:firebase-bom:<version>"
)
)
implementation(
"com.google.firebase:firebase-config"
)
}<version>은 Firebase 공식 문서에서 확인한 버전으로 교체해야 합니다.
Firebase BoM을 사용하면 Firebase 라이브러리 간 호환되는 버전 조합을 관리하기 쉽습니다.
Remote Config 기본값 설정
6. 기본값이 필요한 이유
Remote Config는 네트워크를 통해 설정값을 가져옵니다.
따라서 다음과 같은 상황을 고려해야 합니다.
인터넷 연결이 없는 경우
Firebase 서버에 연결할 수 없는 경우
설정값을 아직 가져오지 못한 경우
앱을 처음 실행한 경우
가져오기 요청이 실패한 경우
이러한 상황에서도 앱이 정상적으로 동작하려면 기본값이 필요합니다.
특히 중요한 기능은 Remote Config 연결에 실패했다고 앱 전체가 동작하지 않도록 설계해야 합니다.
7. 기본값 XML 작성
res/xml/remote_config_defaults.xml 파일을 작성합니다.
<?xml version="1.0" encoding="utf-8"?>
<defaultsMap>
<entry>
<key>new_search_enabled</key>
<value>false</value>
</entry>
<entry>
<key>premium_banner_enabled</key>
<value>false</value>
</entry>
<entry>
<key>search_result_limit</key>
<value>100</value>
</entry>
<entry>
<key>maintenance_mode</key>
<value>false</value>
</entry>
</defaultsMap>여기서는 새로운 검색 기능의 기본값을 false로 설정했습니다.
새로운 기능에 문제가 있거나 원격 설정을 가져오지 못한 경우 기존 기능을 사용하도록 하기 위한 것입니다.
Remote Config 초기화
8. FirebaseRemoteConfig 생성
Application 클래스에서 Remote Config를 초기화할 수 있습니다.
class BirdApplication : Application() {
override fun onCreate() {
super.onCreate()
val remoteConfig =
FirebaseRemoteConfig.getInstance()
remoteConfig.setDefaultsAsync(
R.xml.remote_config_defaults
)
}
}다만 setDefaultsAsync()는 비동기 작업입니다.
앱에서 기본값 적용이 완료되기 전에 설정을 읽는 상황을 피하려면 초기화 순서를 관리해야 합니다.
규모가 커지면 Remote Config 초기화를 Repository 또는 별도의 관리 클래스로 분리하는 것이 좋습니다.
9. 가져오기 간격 설정
Remote Config는 설정값을 가져올 때 최소 가져오기 간격을 적용합니다.
val settings =
FirebaseRemoteConfigSettings.Builder()
.setMinimumFetchIntervalInSeconds(3600)
.build()
remoteConfig.setConfigSettingsAsync(settings)이 예제는 최소 가져오기 간격을 1시간으로 설정합니다.
실제 운영 환경에서는 설정값의 변경 빈도와 서비스 요구사항을 고려해 간격을 결정해야 합니다.
개발 중에는 더 짧은 간격을 사용할 수 있지만 운영 환경에서 지나치게 빈번한 가져오기는 피하는 것이 좋습니다.
원격 설정값 가져오기
10. fetchAndActivate 사용
Remote Config에서는 fetchAndActivate()를 사용해 설정값을 가져오고 활성화할 수 있습니다.
remoteConfig.fetchAndActivate()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
Timber.d(
"Remote Config fetch completed"
)
} else {
Timber.w(
task.exception,
"Remote Config fetch failed"
)
}
}fetchAndActivate()가 성공했다고 해서 항상 새로운 설정값이 적용된 것은 아닙니다.
기존에 활성화된 값과 동일할 수도 있습니다.
또한 네트워크 상태나 캐시 정책에 따라 즉시 최신 설정을 가져오지 못할 수 있습니다.
11. 설정값 읽기
설정값을 가져온 뒤 다음과 같이 사용할 수 있습니다.
val newSearchEnabled =
remoteConfig.getBoolean(
"new_search_enabled"
)정수값도 읽을 수 있습니다.
val searchLimit =
remoteConfig.getLong(
"search_result_limit"
)문자열은 다음과 같이 읽습니다.
val message =
remoteConfig.getString(
"maintenance_message"
)설정값의 자료형과 실제 사용 목적이 일치하도록 관리하는 것이 중요합니다.
MVVM 구조에 적용하기
12. RemoteConfigRepository 만들기
실제 프로젝트에서는 ViewModel에서 FirebaseRemoteConfig를 직접 호출하기보다 Repository를 통해 접근하는 것이 좋습니다.
class RemoteConfigRepository(
private val remoteConfig: FirebaseRemoteConfig
) {
fun isNewSearchEnabled(): Boolean {
return remoteConfig.getBoolean(
"new_search_enabled"
)
}
fun isPremiumBannerEnabled(): Boolean {
return remoteConfig.getBoolean(
"premium_banner_enabled"
)
}
fun getSearchResultLimit(): Int {
return remoteConfig.getLong(
"search_result_limit"
).toInt()
}
}이렇게 하면 Firebase SDK와 앱의 비즈니스 로직을 분리할 수 있습니다.
또한 테스트 환경에서는 가짜 Repository를 주입해 기능 플래그별 동작을 검증할 수 있습니다.
13. FeatureFlags 인터페이스
기능 플래그를 별도 인터페이스로 분리할 수도 있습니다.
interface FeatureFlags {
val isNewSearchEnabled: Boolean
val isPremiumBannerEnabled: Boolean
val searchResultLimit: Int
}Firebase Remote Config 기반 구현은 다음과 같습니다.
class FirebaseFeatureFlags(
private val remoteConfig: FirebaseRemoteConfig
) : FeatureFlags {
override val isNewSearchEnabled: Boolean
get() = remoteConfig.getBoolean(
"new_search_enabled"
)
override val isPremiumBannerEnabled: Boolean
get() = remoteConfig.getBoolean(
"premium_banner_enabled"
)
override val searchResultLimit: Int
get() = remoteConfig.getLong(
"search_result_limit"
).toInt()
}이 구조에서는 앱의 기능 코드가 Firebase SDK에 직접 의존하지 않습니다.
신규 검색 기능 적용
14. 기존 검색과 신규 검색 분리
조류 도감 앱에 새로운 검색 알고리즘을 추가했다고 가정하겠습니다.
기존 검색은 SQLite의 LIKE를 사용하고, 새로운 검색은 FTS 기반 검색을 사용합니다.
class BirdSearchRepository(
private val featureFlags: FeatureFlags,
private val database: BirdDatabase
) {
suspend fun search(
keyword: String
): List<Bird> {
return withContext(Dispatchers.IO) {
if (featureFlags.isNewSearchEnabled) {
database.searchWithFts(keyword)
} else {
database.searchWithLike(keyword)
}
}
}
}이렇게 하면 Remote Config에서 new_search_enabled 값을 변경해 검색 방식을 전환할 수 있습니다.
단, 두 검색 방식 모두 현재 앱 버전에 포함되어 있어야 합니다.
15. 기능 플래그를 어디에서 확인할까?
기능 플래그는 실제 동작이 결정되는 위치에서 확인하는 것이 좋습니다.
예를 들어 검색 알고리즘 선택은 Repository에서 처리할 수 있습니다.
반면 특정 버튼의 표시 여부는 ViewModel 또는 UI 상태에서 관리할 수 있습니다.
중요한 점은 동일한 플래그를 여러 화면에서 제각각 해석하지 않도록 하는 것입니다.
StateFlow와 Remote Config
16. 설정값을 UI 상태로 관리하기
Remote Config의 설정값을 StateFlow로 관리하면 UI에 반영하기 쉽습니다.
data class RemoteFeatureState(
val newSearchEnabled: Boolean = false,
val premiumBannerEnabled: Boolean = false
)ViewModel에서는 다음과 같이 상태를 관리할 수 있습니다.
class FeatureViewModel(
private val featureFlags: FeatureFlags
) : ViewModel() {
private val _state =
MutableStateFlow(
RemoteFeatureState()
)
val state =
_state.asStateFlow()
fun refreshFeatureState() {
_state.value =
RemoteFeatureState(
newSearchEnabled =
featureFlags.isNewSearchEnabled,
premiumBannerEnabled =
featureFlags.isPremiumBannerEnabled
)
}
}Remote Config의 새로운 값이 활성화된 뒤 refreshFeatureState()를 호출하면 UI 상태를 갱신할 수 있습니다.
Fragment에서 기능 표시 제어
17. 프리미엄 배너 표시 여부
Fragment에서는 ViewModel의 상태를 관찰할 수 있습니다.
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(
Lifecycle.State.STARTED
) {
viewModel.state.collect { state ->
binding.premiumBanner.isVisible =
state.premiumBannerEnabled
}
}
}이렇게 하면 Remote Config의 설정값에 따라 프리미엄 안내 배너를 표시하거나 숨길 수 있습니다.
단, Remote Config 값은 UI 표시를 제어하는 용도로 사용하는 것이 적합합니다.
유료 기능의 구매 권한을 Remote Config만으로 판단해서는 안 됩니다.
원격 설정과 보안
18. Feature Flag는 보안 기능이 아니다
Remote Config에서 다음과 같은 설정을 사용한다고 가정하겠습니다.
premium_feature_enabled = true이 값을 확인하는 것만으로 프리미엄 기능의 접근 권한을 결정하는 것은 안전하지 않습니다.
Remote Config 값은 앱에서 확인할 수 있는 설정 정보이며, 클라이언트 환경은 변조될 수 있습니다.
따라서 다음과 같은 기능은 서버 검증이 필요합니다.
인앱 결제 권한
구독 상태
사용자 계정 권한
유료 API 접근
관리자 기능
Feature Flag는 기능 노출을 제어하는 도구이지, 사용자 인증이나 권한 검증을 대체하는 도구가 아닙니다.
조건부 설정
19. 사용자 그룹별 설정
Firebase Remote Config에서는 조건을 이용해 서로 다른 설정값을 제공할 수 있습니다.
예를 들어 다음과 같은 조건을 사용할 수 있습니다.
앱 버전
플랫폼
국가 또는 지역
사용자 속성
무작위 사용자 비율
지원되는 조건 종류와 평가 방식은 Firebase Console 및 SDK 구성에 따라 확인해야 합니다.
20. 앱 버전별 기능 제어
새로운 검색 기능이 앱 버전 1.5 이상에만 구현되어 있다고 가정하겠습니다.
이 경우 구버전 앱에서 해당 기능을 활성화하면 안 됩니다.
Remote Config 조건을 이용해 지원 버전에서만 기능을 활성화하도록 구성할 수 있습니다.
또한 앱 코드에서도 지원 여부를 확인하는 것이 좋습니다.
val enabled =
featureFlags.isNewSearchEnabled &&
supportsNewSearch여기서 supportsNewSearch는 앱에서 실제로 신규 검색 기능을 지원하는지 판단하는 값입니다.
원격 설정만으로 코드의 호환성을 보장할 수는 없습니다.
단계적 출시
21. 기능을 일부 사용자에게만 제공하기
새로운 기능을 모든 사용자에게 동시에 제공하면 문제가 발생했을 때 영향 범위가 커질 수 있습니다.
따라서 일부 사용자에게 먼저 제공하는 전략을 사용할 수 있습니다.
예를 들어 다음과 같은 순서를 고려할 수 있습니다.
내부 테스트
↓
일부 사용자
↓
사용 범위 확대
↓
전체 사용자이 방식은 기능의 안정성을 확인하면서 점진적으로 출시할 때 유용합니다.
22. 단계적 출시 비율 예제
다음은 설명을 위한 예시입니다.
| 단계 | 신규 기능 대상 |
|---|---|
| 1단계 | 내부 테스트 |
| 2단계 | 5% |
| 3단계 | 20% |
| 4단계 | 50% |
| 5단계 | 100% |
이 비율은 모든 앱에 적용해야 하는 고정 규칙이 아닙니다.
사용자 수가 적은 앱에서는 5% 그룹의 표본이 너무 작아 의미 있는 분석이 어려울 수 있습니다.
따라서 실제 사용자 규모에 맞게 출시 범위를 결정해야 합니다.
Google Play 단계적 출시와의 차이
23. Remote Config와 Play Console 비교
Google Play Console에서도 단계적 출시를 지원합니다.
하지만 Remote Config와는 목적이 다릅니다.
| 구분 | Remote Config | Google Play 단계적 출시 |
|---|---|---|
| 제어 대상 | 앱 내부 설정 및 기능 | 새로운 앱 버전 |
| 앱 업데이트 | 일반적으로 불필요 | 필요 |
| 기존 코드 활성화 | 가능 | 새 버전에 포함 |
| 신규 코드 배포 | 불가능 | 가능 |
| 기능 비활성화 | 플래그 변경 | 기존 버전 코드에 따라 다름 |
| 적용 범위 | 조건에 따른 사용자 그룹 | 출시 대상 사용자 |
두 방식을 함께 사용할 수도 있습니다.
예를 들어 새로운 앱 버전을 일부 사용자에게 먼저 배포하면서, 해당 버전의 신규 기능은 Remote Config로 비활성화해 둘 수 있습니다.
이후 안정성을 확인한 뒤 기능 플래그를 활성화하는 방식입니다.
롤백 전략
24. 기능 롤백이란?
롤백은 문제가 발생한 기능을 이전의 안정적인 상태로 되돌리는 작업입니다.
Remote Config에서는 기능 플래그를 비활성화해 신규 기능의 사용을 중단할 수 있습니다.
예를 들어 다음과 같습니다.
new_search_enabled = true검색 오류가 발생하면 다음과 같이 변경합니다.
new_search_enabled = false앱은 다음 설정 갱신 시점에 변경된 값을 적용할 수 있습니다.
하지만 Remote Config는 모든 기기에 즉시 반영되는 강제 제어 수단이 아닙니다.
네트워크 상태, 가져오기 간격, 앱 실행 상태 등에 따라 반영이 지연될 수 있습니다.
25. 롤백을 고려한 코드 설계
Feature Flag를 사용하는 경우 기존 구현을 일정 기간 유지하는 것이 좋습니다.
if (featureFlags.isNewSearchEnabled) {
searchWithFts(keyword)
} else {
searchWithLike(keyword)
}신규 기능을 출시하자마자 기존 코드를 제거하면 플래그를 비활성화해도 돌아갈 구현이 없습니다.
따라서 충분한 안정성 검증이 끝나기 전까지 기존 구현을 유지하는 전략을 고려해야 합니다.
실시간 Remote Config 업데이트
26. 실시간 업데이트란?
Firebase Remote Config는 지원 SDK에서 실시간 설정 업데이트를 수신하는 기능을 제공합니다.
이를 이용하면 앱이 실행 중일 때 새로운 설정이 게시되었음을 감지할 수 있습니다.
예를 들어 다음과 같이 리스너를 등록할 수 있습니다.
val registration =
remoteConfig.addOnConfigUpdateListener(
object : ConfigUpdateListener {
override fun onUpdate(
configUpdate: ConfigUpdate
) {
remoteConfig.activate()
.addOnCompleteListener {
viewModel.refreshFeatureState()
}
}
override fun onError(
error: FirebaseRemoteConfigException
) {
Timber.w(
error,
"Remote Config update failed"
)
}
}
)이 코드는 설정 변경을 감지한 뒤 활성화하고 UI 상태를 갱신하는 기본 예제입니다.
실제 프로젝트에서는 리스너 등록 위치와 생명주기를 고려해야 합니다.
27. 리스너 해제
더 이상 실시간 업데이트가 필요하지 않다면 리스너를 해제해야 합니다.
registration.remove()Activity나 Fragment에서 등록했다면 해당 컴포넌트의 생명주기에 맞춰 해제해야 합니다.
앱 전체에서 지속적으로 사용할 기능이라면 Application 범위의 관리 클래스를 고려할 수 있습니다.
유지보수 모드
28. 서버 점검 안내
Remote Config를 이용해 서버 점검 안내 화면을 표시할 수 있습니다.
예를 들어 다음과 같은 설정을 사용할 수 있습니다.
maintenance_mode = true
maintenance_message = 현재 서버 점검 중입니다.앱에서는 다음과 같이 확인합니다.
val maintenanceMode =
remoteConfig.getBoolean(
"maintenance_mode"
)유지보수 모드가 활성화되면 서버 기능을 사용하는 화면에 점검 안내를 표시할 수 있습니다.
다만 Remote Config가 서버 장애 상황에서 항상 최신 값을 전달할 수 있는 것은 아닙니다.
따라서 서버 오류 처리와 네트워크 예외 처리는 별도로 구현해야 합니다.
광고 설정 관리
29. 광고 관련 Feature Flag
AdMob을 사용하는 앱에서는 광고 관련 일부 설정을 원격으로 관리할 수 있습니다.
예를 들어 다음과 같습니다.
banner_enabled = true
interstitial_enabled = falseKotlin에서는 다음과 같이 확인할 수 있습니다.
if (featureFlags.isBannerEnabled) {
loadBannerAd()
}단, 광고 설정을 원격으로 변경할 수 있다고 해서 광고 정책을 무시해도 되는 것은 아닙니다.
광고 빈도, 노출 위치, 사용자 동의, 구매한 광고 제거 권한 등을 함께 고려해야 합니다.
30. 광고 제거 구매자 처리
광고 표시 여부는 다음과 같은 조건을 함께 확인해야 합니다.
val shouldShowAds =
featureFlags.isBannerEnabled &&
!premiumRepository.hasAdFreeEntitlement()여기서 광고 제거 구매 권한은 서버 검증 또는 신뢰할 수 있는 권한 관리 구조를 통해 확인해야 합니다.
Remote Config의 광고 활성화 설정이 구매자의 광고 제거 권한보다 우선해서는 안 됩니다.
Remote Config와 A/B 테스트
31. A/B 테스트 활용
Firebase A/B Testing을 이용하면 Remote Config 값을 사용자 그룹별로 다르게 적용하고 결과를 비교할 수 있습니다.
예를 들어 프리미엄 안내 배너의 문구를 테스트할 수 있습니다.
A 그룹:
광고 제거하기B 그룹:
광고 없이 편리하게 이용하세요이후 구매 화면 진입률이나 구매 전환율을 비교할 수 있습니다.
다만 구매 전환율이 높게 나타났다고 해서 무조건 효과가 있다고 판단해서는 안 됩니다.
표본 수와 실험 기간, 통계적 불확실성을 함께 고려해야 합니다.
Analytics와 연동
32. 기능 플래그 적용 상태 기록
새로운 검색 기능을 사용하는 사용자와 기존 검색 기능을 사용하는 사용자의 행동을 비교하고 싶을 수 있습니다.
이 경우 Analytics 이벤트에 사용된 검색 방식을 기록할 수 있습니다.
val params = Bundle().apply {
putString(
"search_engine",
if (featureFlags.isNewSearchEnabled) {
"fts"
} else {
"like"
}
)
}
analytics.logEvent(
"bird_search",
params
)이렇게 하면 검색 방식별 이벤트를 구분할 수 있습니다.
다만 기능 플래그를 사용하는 사용자 그룹이 무작위로 구성되지 않았다면 결과 차이를 기능의 효과로 단정할 수 없습니다.
Remote Config 테스트
33. 기본값 테스트
원격 설정을 가져오지 못하는 상황에서도 앱이 정상적으로 동작하는지 확인해야 합니다.
예를 들어 다음 상황을 테스트합니다.
첫 실행
오프라인 실행
Firebase 연결 실패
설정값 누락
잘못된 설정값
앱 재시작
기능 플래그 변경
기본값이 올바르게 적용되지 않으면 앱 시작 과정에서 문제가 발생할 수 있습니다.
34. FakeFeatureFlags 만들기
FeatureFlags 인터페이스를 사용하면 테스트용 구현을 만들 수 있습니다.
class FakeFeatureFlags(
override val isNewSearchEnabled: Boolean,
override val isPremiumBannerEnabled: Boolean,
override val searchResultLimit: Int = 100
) : FeatureFlags테스트에서는 다음과 같이 사용할 수 있습니다.
val featureFlags =
FakeFeatureFlags(
isNewSearchEnabled = true,
isPremiumBannerEnabled = false
)이렇게 하면 Firebase 서버에 연결하지 않고도 기능 플래그별 동작을 테스트할 수 있습니다.
35. 기능 플래그 테스트 케이스
| 테스트 항목 | 기대 결과 |
|---|---|
| 신규 검색 ON | 신규 검색 알고리즘 사용 |
| 신규 검색 OFF | 기존 검색 알고리즘 사용 |
| 설정 가져오기 실패 | 기본값 또는 기존 활성값 사용 |
| 프리미엄 배너 ON | 배너 표시 |
| 프리미엄 배너 OFF | 배너 숨김 |
| 지원하지 않는 앱 버전 | 신규 기능 사용 방지 |
| 광고 제거 구매자 | 광고 표시하지 않음 |
| 실시간 설정 변경 | 적절한 시점에 UI 갱신 |
Remote Config 운영 시 주의사항
36. 설정값을 너무 많이 만들지 않기
Remote Config는 편리하지만 모든 설정을 원격으로 관리할 필요는 없습니다.
예를 들어 버튼 색상, 글자 크기, 화면 간격 등 모든 UI 속성을 Remote Config로 관리하면 앱의 동작을 예측하기 어려워질 수 있습니다.
원격 관리가 필요한 설정부터 선택하는 것이 좋습니다.
대표적으로 다음과 같은 설정이 적합합니다.
신규 기능 활성화 여부
일부 UI 안내 문구
실험용 설정값
서버 기능 사용 여부
광고 관련 일부 운영 설정
37. 설정값 이름 규칙
설정값 이름은 일관된 규칙으로 관리하는 것이 좋습니다.
예를 들어 다음과 같습니다.
feature_new_search_enabled
feature_premium_banner_enabled
ads_banner_enabled
app_maintenance_mode
search_result_limit설정값 이름만 보고도 용도를 파악할 수 있어야 합니다.
또한 사용하지 않는 플래그는 정리해야 합니다.
오래된 기능 플래그가 계속 남아 있으면 코드 복잡도가 증가할 수 있습니다.
38. Feature Flag의 기술 부채
Feature Flag는 새로운 기능을 안전하게 출시하는 데 도움이 됩니다.
하지만 플래그가 계속 증가하면 다음과 같은 문제가 발생할 수 있습니다.
조건문 증가
테스트 조합 증가
코드 가독성 저하
사용하지 않는 기존 구현 유지
플래그 간 의존성 증가
따라서 기능이 충분히 안정화되면 불필요한 플래그와 기존 코드를 제거하는 것이 좋습니다.
예를 들어 신규 검색 기능이 완전히 안정화되었다면 다음과 같은 분기를 정리할 수 있습니다.
if (featureFlags.isNewSearchEnabled) {
searchWithFts(keyword)
} else {
searchWithLike(keyword)
}이후에는 신규 검색 구현만 유지할 수 있습니다.
단, 기존 구현 제거는 충분한 테스트와 출시 검증 이후에 진행해야 합니다.
개인 개발자 적용 전략
39. 어떤 기능부터 적용할까?
개인 개발자가 운영하는 Android 앱이라면 처음부터 복잡한 Feature Flag 시스템을 구축할 필요는 없습니다.
다음 기능부터 적용하는 것이 좋습니다.
1단계: 신규 기능 활성화 플래그
새로운 검색 기능이나 화면을 켜고 끌 수 있도록 구성합니다.
2단계: 프리미엄 안내 배너
프리미엄 기능 안내 여부를 원격으로 관리합니다.
3단계: 서버 점검 안내
서버 기능이 일시적으로 중단되었을 때 안내 화면을 표시합니다.
4단계: 광고 운영 설정
광고 표시 여부를 제어하되 구매 권한과 광고 정책을 우선 적용합니다.
5단계: 단계적 출시
신규 기능을 일부 사용자에게 먼저 제공합니다.
6단계: Analytics 분석
기능 플래그별 사용자 행동을 비교합니다.
7단계: 불필요한 플래그 정리
안정화된 기능은 코드와 설정을 단순화합니다.
Remote Config 체크리스트
40. 출시 전 점검
Firebase 프로젝트 연결
Remote Config SDK 적용
기본값 XML 작성
설정값 이름 규칙 정의
기본값 적용 완료 확인
fetchAndActivate 구현
가져오기 실패 처리
오프라인 동작 확인
FeatureFlags 인터페이스 구성
Repository와 UI 역할 분리
StateFlow 상태 갱신
Fragment 생명주기 고려
앱 버전별 기능 호환성 확인
단계적 출시 조건 확인
실시간 업데이트 리스너 관리
롤백 경로 확보
기존 기능 유지 여부 확인
구매 권한을 Remote Config로 판단하지 않기
광고 정책 준수
Analytics 이벤트 설계
A/B 테스트 표본 검토
불필요한 플래그 정리 계획 수립
실제 기기에서 테스트
41. 마무리
Firebase Remote Config와 Feature Flag는 Android 앱을 운영할 때 유용한 도구입니다.
특히 앱 업데이트 없이 일부 설정을 변경하거나 새로운 기능을 단계적으로 출시할 수 있다는 장점이 있습니다.
하지만 Remote Config를 사용한다고 모든 문제를 즉시 해결할 수 있는 것은 아닙니다.
기능은 미리 앱에 구현되어 있어야 하며, 설정값 반영에는 지연이 발생할 수 있습니다.
또한 보안이나 구매 권한 검증을 Remote Config에 의존해서는 안 됩니다.
이번 글의 핵심을 정리하면 다음과 같습니다.
Remote Config는 앱의 설정값을 원격으로 관리하는 서비스입니다.
Feature Flag는 기능 활성화 여부를 제어하는 설계 패턴입니다.
앱에 구현되지 않은 기능을 Remote Config만으로 추가할 수는 없습니다.
기본값을 설정해 네트워크 오류 상황에 대비해야 합니다.
MVVM 구조에서는 FeatureFlags 인터페이스로 의존성을 분리할 수 있습니다.
신규 기능은 일부 사용자에게 먼저 제공하는 전략을 고려할 수 있습니다.
문제가 발생했을 때 기존 기능으로 돌아갈 수 있는 경로를 유지해야 합니다.
Remote Config는 즉시 적용이 보장되는 강제 제어 수단이 아닙니다.
구매 권한과 보안 검증은 서버에서 처리해야 합니다.
안정화된 기능의 플래그는 정리해 기술 부채를 줄여야 합니다.
기존 Android 앱에 Remote Config를 처음 적용한다면 신규 검색 기능의 활성화 여부를 제어하는 작은 기능부터 시작하는 것이 좋습니다.
이후 운영 경험이 쌓이면 단계적 출시, 광고 설정, A/B 테스트 등으로 활용 범위를 넓힐 수 있습니다.
안정적인 앱 운영은 문제가 발생하지 않도록 만드는 것뿐 아니라, 문제가 발생했을 때 영향 범위를 줄이고 빠르게 대응할 수 있도록 설계하는 것입니다.
참고 자료
Firebase Remote Config 개요
https://firebase.google.com/docs/remote-configAndroid Remote Config 시작하기
https://firebase.google.com/docs/remote-config/get-startedRemote Config 실시간 업데이트
https://firebase.google.com/docs/remote-config/real-timeRemote Config 매개변수 및 조건
https://firebase.google.com/docs/remote-config/parametersFirebase A/B Testing
https://firebase.google.com/docs/ab-testingFirebase Analytics
https://firebase.google.com/docs/analyticsGoogle Play 단계적 출시
https://support.google.com/googleplay/android-developer/answer/6346149
태그: Android, Kotlin, Firebase Remote Config, Feature Flag, 기능 플래그, Android MVVM, StateFlow, 단계적 출시, 롤백, Firebase Analytics, A/B 테스트, Google Play Console, Android 앱 운영, Firebase, Android 앱 개발