원문은 Turn Your App into Revenue: Building Paywalls in Android With Jetpack Compose에서 확인하실 수 있습니다.
인앱 구독은 모바일 애플리케이션에서 사용자에게 반복적인 가치를 제공하여 수익을 창출하는 방식으로 널리 자리 잡았습니다. 특히 구독 모델은 AI 플랫폼, 소셜 네트워크, 생산성 도구 등 다양한 산업 분야에서 널리 채택되고 있으며, 광고 없이 제품을 사용하는 기능과 앱 내에서 제공하는 프리미엄 기능을 원하는 헤비 유저에게 구독은 매력적인 상품이 될 수 밖에 없습니다.
그러나 인앱 구독 기능을 처음부터 바닥에서부터 구축하는 것은 생각보다 많은 비용이 요구됩니다. 안드로이드에서 제공하는 Google Play Billing 라이브러리는 API에 큰 변화가 자주 생기기도하고, 안드로이드 뿐만 아니라, iOS와의 구독 시스템 통합 등을 고려한다면 플랫폼별 복잡성을 관리해야 합니다. 이는 API 마이그레이션, 안전한 서버 인프라, 데이터베이스 및 커스텀 API 유지 관리 비용이 요구됩니다.
이번 포스트에서는 Jetpack Compose와 RevenueCat SDK를 사용하여 Android에서 인앱 구독 및 페이월 기능을 원활하게 구현하는 방법을 알아보고, 오픈소스 프로젝트인 Cat Paywall Compose를 살펴보겠습니다.
RevenueCat은 "Helping developers make more money" (개발자들이 더 많은 수익을 낼 수 있도록 돕기)라는 사명으로 인앱 구독, 결제, 페이월 솔루션을 제공합니다. ChatGPT를 개발한 OpenAI, 전세계 대부분의 IT 기업들이 사용하는 Notion부터 개인앱을 통해 수익을 창출하는 수많은 인디 개발자에 이르기 까지 RevenueCat은 모든 모바일 비즈니스에서 고도의 수익 창출을 돕고 있습니다.
Cat Paywall Compose는 Google의 공식 아키텍처 가이드라인을 준수하여 중소 규모 프로젝트에 적합한 아키텍처 디자인을 제공합니다. 아키텍처는 아래 그림과 같이 UI 레이어와 데이터 레이어, 두 가지 주요 레이어로 구성됩니다
UI 계층은 아키텍처의 최상위 계층이며, 텍스트, 버튼, 메뉴와 같은 시각적 요소를 통해 사용자와 직접 상호 작용합니다. 이러한 요소들이 모여 사용자가 직접적으로 커뮤니케이션하는 화면을 형성합니다. 이 계층에는 UI 상태를 관리하고 노출하는 ViewModel도 포함됩니다. ViewModel은 화면 회전이나 기기 언어 설정 변경과 같은 구성 변경 시 상태를 보존하는 데 도움이 됩니다.
UI 계층은 UI 요소와 ViewModel(UI 상태 홀더)로 구성됩니다. UI 요소는 상태가 아래로, 이벤트가 위로 흐르는 단방향 데이터 흐름(UDF) 패턴에 따라 ViewModel과 상호 작용합니다. 사용자가 이벤트를 트리거하면 UI 요소는 이벤트를 ViewModel로 전송하고, ViewModel은 데이터 계층의 데이터를 UI 상태로 변환하여 실제 데이터 소스로 제공합니다. 이 상태는 UI 요소에 의해 관찰되어 인터페이스에 반영됩니다.
데이터 계층은 UI 계층에서 사용하는 도메인별 데이터를 관리합니다. 여러 repository로 구성되며, 각 repository는 네트워크 서비스나 로컬 데이터베이스와 같은 하나 이상의 데이터 소스와 상호 작용하여 필요한 데이터를 검색합니다. 이 프로젝트에서 데이터 계층은 주로 RevenueCat 백엔드를 포함한 원격 소스와 통신하며, 대부분의 작업은 SDK를 통해 처리되므로 저장소 내에서 직접 통합할 수 있습니다.
이 섹션에서는 Google Play Billing 및 RevenueCat 대시보드를 설정합니다. Google Play Billing을 RevenueCat 대시보드에 연결하면 통합된 사용자 ID, 간소화된 구독 관리, 크로스 플랫폼 분석과 여러 앱 스토어에서 정확한 가격 정보를 얻을 수 있습니다. RevenueCat을 사용하면 좋은 이유는 Google I/O 2023 “Make More Money on Android” round-up에서 더 확인하실 수 있습니다.
이제 단계별로 프로젝트를 구성해 보겠습니다.
먼저 Google Play Console에 로그인하고, 본인의 애플리케이션을 선택한 다음, 아래 이미지에 표시된 대로 사이드바에서 Play로 수익 창출 > 제품으로 이동한 후 구독을 클릭합니다.
"앱에 아직 구독이 없습니다. 새 APK를 업로드하세요."라는 메시지가 표시되면 AndroidManifest.xml
에 다음 권한을 추가하고 Play Console에 APK를 업로드해야 합니다.
<uses-permission android:name="com.android.vending.BILLING" />
필요한 결제 권한이 있는 APK를 업로드하면 새 구독을 생성할 수 있습니다. 아래 이미지와 같이 구독 제품을 생성하시면 됩니다.
자세한 내용은 Google Play 제품 설정 지침에서 학습하실 수 있습니다.
아직 RevenueCat 계정이 없으시다면 RevenueCat Account에 가입하여 제품, 수익 창출, 분석 등을 관리하실 수 있습니다. 이메일 주소 하나만 있으면 가입하실 수 있습니다.
다음으로, Google Play 스토어 서비스 사용자 인증 정보 가이드의 단계에 따라 앱을 RevenueCat 백엔드에 제대로 연결하기 위한 Google Play 스토어 서비스 사용자 인증 정보를 설정해야합니다. 이 사용자 인증 정보는 RevenueCat 서버가 사용자를 대신하여 Google과 안전하게 통신하는 데 필요합니다. 아래와 같이 총 4가지의 단계를 순차적으로 따라하시면 됩니다.
이제 Google Play 애플리케이션이 RevenueCat에 연결되어 Google Play에서 생성한 인앱 상품과 구독을 자동으로 가져올 수 있습니다.
이렇게 하려면 RevenueCat 대시보드의 프로젝트 설정에서 'Products' 탭으로 이동하여 '+ New' 버튼을 클릭하고 'Import Products'를 선택하시면 아래와 같이 RevenueCat에서 가져올 수 있는 스토어 상품 목록이 표시됩니다.
제품을 가져온 후 아래 이미지에 표시된 대로 Google Play에서 추가한 구독 상품들이 RevenueCat 프로젝트에 성공적으로 추가된 것을 확인할 수 있습니다.
Offer라는 것을 한국어로 번역하면 "무언가를 제안하다 혹은 제공하다"라는 의미입니다. 따라서 오퍼링은 유저가 인앱 구독 시스템을 통해서 어떤 상품을 구매하는 경우, 앱 내에서 해당 상품 구매 유저에게 "보상으로 무엇을 제공할지"에 대한 것이라고 생각하시면 됩니다.
이제 대부분의 기본적인 설정이 완료되었습니다. 이제 첫 번째 오퍼링을 생성할 차례입니다. 오퍼링은 페이월에서 사용자에게 표시되는 제품의 정보를 나타냅니다. (가령, 한 달에 2천원으로 유료 구독을 할지, 1년에 1만 5천원으로 유로 구독을 할지) 오퍼링 생성 가이드의 단계에 따라 새 오퍼링을 생성할 수 있습니다. 생성이 완료되면 아래와 같이 결과가 반영됩니다.
RevenueCat 대시보드에서 페이월을 만들어 보겠습니다. RevenueCat 페이월을 사용하면 코드 변경이나 앱 업데이트 없이 전체 페이월 UI를 원격으로 구성할 수 있습니다. 텍스트, 이미지, 아이콘, 스택, 캐러셀, 타임라인 등 다양한 구성 요소를 제공하고, 변수 지원, 현지화 등 다양한 기능을 제공합니다. 자세한 내용은 페이월 만들기 가이드를 참조하세요.
시작하려면 프로젝트 설정에서 'Paywalls' 탭으로 이동하여 '+New' 버튼을 클릭하세요. 아래 이미지와 같은 템플릿 선택 화면이 나타납니다.
RevenueCat은 제품을 빠르게 구성하실 수 있도록 몇 가지 템플릿을 제공합니다. 따라서 처음부터 직접 페이월 화면을 구성하시거나, 제공되는 템플릿 중에서 선택할 수 있습니다. 템플릿을 선택하면 바로 페이월 편집기 화면으로 이동합니다.
Paywalls v2에 도입된 Paywall Editor는 Figma와 유사한 인터페이스를 제공하여 전체 Paywall UI를 디자인하고 맞춤 설정할 수 있습니다. 변경 사항을 적용한 후 "Save Changes"을 클릭하고 "Publish Paywall"를 클릭하면 업데이트된 디자인이 서버 기반 UI (server driven UI) 시스템을 통해 모든 사용자 (Android 및 iOS)가 앱을 플레이 스토어에서 업데이트 하지 않아도 자동으로 업데이트됩니다.
즉, RevenueCat Paywalls의 가장 강력한 기능 중 하나는 새로운 Paywall UI를 테스트 해보기 위해 새 버전의 앱을 스토어에 출시할 필요가 없습니다. PM과 같은 비개발직군들이 다양한 수익 창출 전략을 모색하기 위해 동적으로 Paywall UI를 변경하고 실험을 수행할 수 있다는 장점이 있습니다.
마지막으로, 본인의 프로젝트에서 직접 빌드하고 테스트하는 경우 Android SDK 설치 가이드에 따라 RevenueCat SDK를 Android 앱에 통합하실 수 있습니다.
Cat Paywall Compose 오픈소스 프로젝트를 통해 빌드 테스트를 하시는 경우, 대시보드에서 RevenueCat API 키를 가져와야 합니다. RevenueCat 대시보드의 프로젝트 설정에서 'API Keys' 탭으로 이동하면 아래 화면이 표시됩니다.
'Show key' 버튼을 클릭하여 API 키를 확인하실 수 있습니다. 그리고 프로젝트 루트 디렉터리에 secrets.properties
라는 이름의 새 파일을 만들고 아래 코드를 추가하시면 됩니다.
REVENUECAT_API_KEY=<YOUR_API_KEY>
또한 Google Play Console에 등록된 ID와 일치하도록 application ID를 변경하셔야합니다.
이제 프로젝트를 빌드하고 실행하시면 됩니다. 모든 설정이 올바르게 완료되었다면 세부 화면에서 구독하기 버튼을 눌렀을 때 정상적으로 페이월 화면이 나오게 됩니다.
참고: 에뮬레이터에서 프로젝트를 실행하는 경우 기기가 Google Play에 로그인되어 있는지 확인하세요.
Jetpack Compose를 사용하여 안드로이드 프로젝트에 페이월을 구현하는 방법을 살펴보겠습니다. 이 섹션에서는 CatArticlesApp 클래스에서와 마찬가지로 RevenueCat SDK를 이미 초기화했다고 가정합니다.
먼저, RevenueCat 대시보드에서 사용자에게 제공되는 구매 옵션을 정의하는 상품 정보를 가져와야 합니다. 아래 예시와 같이 Purchases.sharedInstance.awaitOfferings()
메서드를 사용하면 쉽게 이 작업을 수행할 수 있습니다.
internal class DetailsRepositoryImpl @Inject constructor(
@Dispatcher(CatArticlesDispatchers.IO) private val ioDispatcher: CoroutineDispatcher,
) : DetailsRepository {
override fun fetchOffering(): Flow<ApiResponse<Offering>> = flow {
try {
val offerings = Purchases.sharedInstance.awaitOfferings()
offerings.current?.let { currentOffering ->
val response = ApiResponse.of { currentOffering }
emit(response)
}
} catch (e: PurchasesException) {
ApiResponse.exception(e)
}
}.flowOn(ioDispatcher)
}
RevenueCat의 Android SDK는 Kotlin 코루틴을 지원하므로 이미 코루틴 기반 아키텍처를 사용하는 프로젝트에 쉽게 통합할 수 있습니다. 위 예시에서 볼 수 있듯이 RevenueCat 대시보드에 구성된 현재 제공 항목을 검색할 수 있으며, 이를 Flow
로 값을 방출하고 있습니다.
이제 모든 설정이 완료되었습니다. com.revenuecat.purchases:purchases-ui
의존성을 이미 추가했다면 Jetpack Compose에서 페이월 UI를 쉽게 빌드하실 수 있습니다.
RevenueCat의 UI 라이브러리는 페이월 화면이나 다이얼로그를 표시하기 위해 다양한 API를 제공하며, PaywallDialog
와 같이 완전히 사용자 정의가 가능한 다이얼로그 API 등이 있습니다. 다음 코드 예제를 사용하여 페이월 UI와 동작을 쉽게 구현하고 커스텀할 수 있습니다.
val offering by viewModel.offering.collectAsState()
PaywallDialog(
PaywallDialogOptions.Builder()
.setDismissRequest { }
.setOffering(offering)
.setFontProvider(..)
.setCustomPurchaseLogic(..)
.setListener(object : PaywallListener {
override fun onPurchaseStarted(rcPackage: Package) {
super.onPurchaseStarted(rcPackage)
}
override fun onPurchaseCompleted(customerInfo: CustomerInfo, storeTransaction: StoreTransaction) {
super.onPurchaseCompleted(customerInfo, storeTransaction)
customer = customerInfo
}
override fun onPurchaseError(error: PurchasesError) {
super.onPurchaseError(error)
}
override fun onPurchaseCancelled() {
super.onPurchaseCancelled()
}
})
.build()
)
페이월을 표시하는 방법에 대한 자세한 지침은 페이월 표시 가이드를 참조하세요.
프로젝트를 빌드하는 동안 아래와 같은 오류가 발생할 수 있습니다.
Error fetching offerings – PurchasesError(code=ConfigurationError, underlyingErrorMessage=There’s a problem with your configuration. None of the products registered in the RevenueCat dashboard could be fetched from the Play Store)
위의 오류를 마주하신다면 아래 항목을 순차적으로 확인해보시길 바랍니다.
잠재 고객에게 원활한 구매 경험을 제공하기 위해서는 앱 내 구매를 철저히 테스트하는 것이 좋습니다 Android 앱에서 인앱 구독을 효과적으로 테스트하는 방법이 궁금하시다면 앱앱 구독 테스트를 위한 종합 가이드를 참조하실 수 있습니다.
이번 포스트에서는 Google Play 및 RevenueCat 대시보드를 설정하는 방법과 Jetpack Compose를 사용하여 Android에서 페이월 기능을 구현하는 방법을 살펴보았습니다. 특히 Google Play Billing 라이브러리의 잦은 변경과 통합 사용자 ID, 구독 수명 주기, 크로스 플랫폼 분석, 혹은 여러 앱 스토어의 정확한 가격 책정을 관리해야 하는 상황에서 인앱 구독 시스템을 구축하는 것은 생각보다 챌린징 할 수 있습니다. RevenueCat은 OpenAI, Notion과 같은 대형 기업부터 인디 개발자 모두가 앱 개발을 통한 수익 전환에 있어 복잡한 측면을 간소화하여, 궁극적으로 더 많은 수익을 창출할 수 있도록 지원합니다.
즐거운 코딩 되시길 바랍니다!