최근에 회사에서 SDK 데모 앱을 만들면서 겪은 일입니다. 무제한으로 사용하게 둘 수는 없고… 그렇다고 매번 만료일을 하드코딩해서 빌드하기도 번거롭더라구요.
그래서 Firebase Remote Config를 활용해서 꽤 괜찮은 해결책을 만들었습니다. 오늘은 그 과정을 공유해보려고 합니다.
문제 상황
저희가 원했던 건 이런 거였어요:
- 빌드할 때마다 자동으로 3일 사용 기한 설정
- “며칠만 더 써보고 싶다”고 하면 즉시 연장 가능
- 문제 생기면 바로 사용 중지
- 앱 재배포 없이 모든 걸 제어
처음엔 SharedPreferences에 첫 실행 날짜를 저장하는 방법도 생각해봤는데, 앱 삭제하고 재설치하면 리셋되는 문제가 있더라구요. 서버 구축은 오버엔지니어링 같았고…
해결 방법: 빌드 타임스탬프 + Remote Config
결국 이렇게 해결했습니다:
1. 빌드 시점 자동 기록
먼저 build.gradle.kts
에서 빌드할 때마다 타임스탬프를 자동으로 기록하도록 설정했어요.
android {
defaultConfig {
buildConfigField("long", "BUILD_TIMESTAMP", "${System.currentTimeMillis()}L")
}
}
이제 앱에서 BuildConfig.BUILD_TIMESTAMP
로 언제 빌드됐는지 알 수 있습니다.
2. Firebase Remote Config 설정
Firebase Console에서 이런 값들을 설정했습니다:
{
"demo_duration_days": 3,
"demo_enabled": true
}
심플하죠? 기본 3일이고, 필요하면 언제든 바꿀 수 있어요.
3. 만료 체크 로직 구현
핵심 로직은 이렇습니다:
class DemoExpiryManager(private val remoteConfig: FirebaseRemoteConfig) {
fun checkDemoExpiry(callback: (Boolean, String?) -> Unit) {
remoteConfig.fetchAndActivate()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val isExpired = isDemoExpired()
val message = if (isExpired) "데모 기간이 만료되었습니다." else null
callback(isExpired, message)
}
}
}
private fun isDemoExpired(): Boolean {
// Remote Config에서 활성화 여부 먼저 체크
if (!remoteConfig.getBoolean("demo_enabled")) {
return true
}
// 빌드 시간 + 데모 기간으로 만료일 계산
val demoDays = remoteConfig.getLong("demo_duration_days")
val buildTime = BuildConfig.BUILD_TIMESTAMP
val expiryTime = buildTime + (demoDays * 24 * 60 * 60 * 1000L)
return System.currentTimeMillis() > expiryTime
}
}
MainActivity에서는 이렇게 사용해요:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
demoExpiryManager.checkDemoExpiry { isExpired, message ->
if (isExpired) {
// 만료 다이얼로그 표시 후 앱 종료
showExpiryDialog(message)
}
}
}
가장 좋은 점은 앱을 다시 빌드하거나 배포할 필요가 없다는 거예요. Firebase Console에서 숫자 하나만 바꾸면 끝!
꿀팁 몇 가지
실제로 써보면서 알게 된 팁들:
- 빌드 타입별 관리: Remote Config에 JSON으로 빌드 타입 별로 만료일을 따로 관리할 수도 있어요.
- 남은 기간 표시: 앱 하단에 작게 “3일 남음” 같은 걸 표시하면 사용자도 알 수 있어서 좋아요.
- 캐시 주기 설정:
minimumFetchIntervalInSeconds
를 1시간 정도로 설정하면 적당해요.
테스트 코드 작성할 때 주의점
Unit Test 작성할 때 Tasks.forResult()
쓰면 Android Looper 때문에 에러나더라구요. MockK로 Task를 완전히 mocking해야 합니다:
val mockTask = mockk<Task<Boolean>>()
every { mockTask.isSuccessful } returns true
every { mockTask.addOnCompleteListener(any()) } answers {
firstArg<OnCompleteListener<Boolean>>().onComplete(mockTask)
mockTask
}
이거 때문에 한참 헤맸네요 ㅠㅠ
무엇보다 서버 구축 없이 이런 시스템을 만들 수 있다는 게 큰 장점인 것 같아요. 혹시 비슷한 고민 있으신 분들께 도움이 되었으면 좋겠습니다!
질문 있으시면 댓글로 남겨주세요 🙂