iOS 인앱 결제 및 페이월 개요
0:02:00RevenueCat의 iOS SDK 코드랩에 오신 것을 환영합니다!
이 코드랩에서 다음 내용을 학습합니다:
RevenueCat SDK를 Xcode 프로젝트에 연동합니다. SwiftUI 애플리케이션에서 인앱 결제를 구현합니다. 유료 사용자와 무료 사용자를 구분하는 방법을 배웁니다. RevenueCat의 서버 사이드 구성을 활용하여 SwiftUI로 페이월 화면을 구축합니다.
이 코드랩을 완료하면 RevenueCat의 iOS SDK와 SwiftUI를 사용하여 iOS 앱에서 인앱 결제를 성공적으로 구현하고 동적 페이월을 표시할 수 있습니다.
RevenueCat SDK 가져오기
0:05:00먼저, 인앱 결제를 구현하기 전에 RevenueCat SDK를 프로젝트에 가져와야 합니다. 가장 쉬운 방법은 Swift Package Manager를 사용하는 것입니다.
Xcode에서 Podfile에 다음을 추가하세요:
pod 'RevenueCat'
pod 'RevenueCatUI'Xcode가 패키지를 가져옵니다. RevenueCat을 앱의 메인 타겟에 추가했는지 확인하세요.
사전 구축된 Paywall을 구현할 계획이라면 RevenueCatUI 라이브러리도 동시에 추가할 수 있습니다.
Getting Your API Key
Before you can initialize the SDK, you'll need your RevenueCat API key. Here's where to find it:
- Go to the RevenueCat dashboard
- Navigate to your project
- Click on API keys in the left sidebar
- Copy your Apple App Store API key (it should start with
appl_)
Initialize the SDK
다음으로, 아래 코드를 사용하여 앱의 init() 메서드 또는 AppDelegate에서 Purchases SDK를 초기화합니다:
import SwiftUI
import RevenueCat
@main
struct MyApp: App {
init() {
// SDK의 모든 메시지 로그 출력
Purchases.logLevel = .debug
// API 키로 SDK 구성
Purchases.configure(withAPIKey: <public_apple_api_key>, appUserID: <app_user_id>)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
} 사용자 식별: 특정 appUserID를 제공하지 않았으므로 RevenueCat이 자동으로 익명 ID를 생성하고 관리합니다. 복원 기능을 제대로 사용하려면 백엔드에서 제공하는 고유 식별자를 입력하세요. 트랜잭션 완료: 기본적으로 SDK는 App Store와의 트랜잭션 완료를 자동으로 처리하므로 직접 처리할 필요가 없습니다.
이제 구현의 50%를 완료했습니다!
Entitlement 검증하기
0:03:00이제 사용자 Entitlement 검증으로 넘어가겠습니다.
Entitlement는 사용자가 구매 후 잠금 해제하는 접근 수준이나 기능을 나타냅니다. 이는 광고 배너를 표시할지 또는 프리미엄 접근을 부여할지 결정하는 데 유용합니다.
Swift의 최신 async/await 문법을 사용하여 사용자가 활성 Entitlement를 가지고 있는지 쉽게 확인할 수 있습니다:
import SwiftUI
import RevenueCat
struct ContentView: View {
@State private var isSubscribed: Bool = false
let ENTITLEMENT_ID = "premium" // RevenueCat 대시보드의 Entitlement 식별자
var body: some View {
VStack {
if isSubscribed {
PremiumContent()
} else {
Text("무료 콘텐츠입니다.")
// 여기에 배너나 페이월을 표시할 수 있습니다
}
}
.task {
// 뷰가 나타날 때 Entitlement 확인
await checkEntitlement()
}
}
private func checkEntitlement() async {
do {
let customerInfo = try await Purchases.shared.customerInfo()
// Entitlement가 활성 상태인지 확인
if customerInfo.entitlements[ENTITLEMENT_ID]?.isActive == true {
self.isSubscribed = true
} else {
self.isSubscribed = false
}
} catch {
print("고객 정보 가져오기 오류: \(error)")
}
}
}사용자가 특정 Entitlement를 가지고 있는지 확인한 후 어떻게 진행할지 결정할 수 있습니다. 이 SwiftUI 예제에서는 @State 변수(isSubscribed)를 업데이트하여 뷰가 자동으로 다시 렌더링되고 프리미엄 콘텐츠 또는 무료 버전을 표시합니다.
인앱 결제 구현하기
0:04:00이제 프리미엄 경험을 제공하기 위해 인앱 결제를 구현해 보겠습니다. 시작하려면 먼저 RevenueCat에서 구성한 상품을 가져와야 합니다. 이 데이터는 사용자에게 구매 옵션을 표시하는 데 사용됩니다.
Purchases.shared.products(productIdentifiers:)를 호출하여 사용 가능한 상품을 가져올 수 있습니다:
import RevenueCat
class PurchaseViewModel: ObservableObject {
func purchase(product: StoreProduct) async {
do {
let result = try await Purchases.shared.purchase(product: product)
// 사용자가 이제 프리미엄 권한이 있는지 확인
if result.customerInfo.entitlements["premium"]?.isActive == true {
// 프리미엄 콘텐츠 잠금 해제
print("구매 성공!")
}
} catch {
print("구매 오류: \(error)")
}
}
func fetchProducts() async -> [StoreProduct] {
do {
// 식별자를 사용하여 상품 가져오기
let products = try await Purchases.shared.products(["premium_monthly", "premium_yearly"])
return products
} catch {
print("상품 가져오기 오류: \(error)")
return []
}
}
}StoreProduct 객체를 가져온 후 Purchases.shared.purchase(product:)를 호출하여 인앱 결제 플로우를 시작할 수 있습니다. 이렇게 하면 네이티브 App Store 구매 시트가 자동으로 트리거되어 사용자가 앱 내에서 안전하게 트랜잭션을 완료할 수 있습니다.
이처럼 몇 줄의 코드만으로 완전히 기능하는 인앱 결제 플로우를 연동했습니다. 영수증 처리, StoreKit API 또는 구매 검증을 수동으로 처리하는 복잡함이 필요 없습니다.
페이월(Paywall) 구현하기
0:07:00이제 SwiftUI를 사용하여 iOS 프로젝트에서 Paywall을 구현할 차례입니다.
비즈니스 로직
시작하려면 RevenueCat 대시보드에서 현재 Offering을 가져와야 합니다. Offering은 페이월에서 사용자에게 표시되는 사용 가능한 패키지(월간, 연간 등)를 정의합니다. Purchases.shared.offerings()를 사용하여 쉽게 수행할 수 있습니다:
import Foundation
import RevenueCat
import SwiftUI
@MainActor // @Published 속성 변경이 메인 스레드에서 발생하도록 보장
class PaywallViewModel: ObservableObject {
// 페이월에 표시될 현재 Offering을 보유합니다.
@Published var offering: Offering?
// UI에 인디케이터를 표시하기 위한 로딩 상태를 추적합니다.
@Published var isLoading: Bool = false
init() {
// ViewModel이 생성되자마자 Offering을 가져올 수 있습니다.
Task {
await fetchOffering()
}
}
/**
* RevenueCat에서 현재 Offering을 가져와 @Published 속성을 업데이트합니다.
*/
func fetchOffering() async {
self.isLoading = true
do {
let offerings = try await Purchases.shared.offerings()
// 대시보드에서 이 배치에 표시되도록 구성한
// 'current' Offering을 가져옵니다.
self.offering = offerings.current
} catch {
print("Offering 가져오기 오류: \(error.localizedDescription)")
// UI에서도 오류 상태를 처리할 수 있습니다
}
self.isLoading = false
}
}RevenueCat iOS SDK는 Swift의 async/await를 네이티브로 지원하여 최신 SwiftUI 애플리케이션에 완벽하게 맞습니다. @Published로 offering을 게시하면 SwiftUI 뷰가 변경 사항을 반응적으로 관찰할 수 있습니다.
SwiftUI로 Paywall UI 구현하기
이 시점에서 모든 준비가 완료되었습니다. RevenueCatUI 패키지를 추가했다면 SwiftUI를 사용하여 쉽게 페이월 UI를 구축할 수 있습니다.
RevenueCat의 UI 라이브러리는 내장 SwiftUI 뷰인 PaywallView를 제공하여 페이월 화면을 빠르게 표시할 수 있습니다. 이 컴포넌트는 표준 SwiftUI 수정자를 사용하여 완전히 커스터마이징할 수 있습니다.
다음은 SwiftUI의 sheet 수정자를 사용하여 페이월을 구현하고 표시하는 것이 얼마나 간단한지 보여주는 예제입니다:
import SwiftUI
import RevenueCat
import RevenueCatUI
struct ContentView: View {
@State private var showPaywall = false
var body: some View {
Button("페이월 표시") {
showPaywall = true
}
.sheet(isPresented: $showPaywall) {
// 시트에 페이월 표시
PaywallView()
}
}
}PaywallView는 자동으로 현재 Offering을 가져와 RevenueCat 대시보드에서 구성한 템플릿을 사용하여 표시합니다. 이미 가져온 경우 Offering 객체를 직접 전달할 수도 있습니다:
import SwiftUI
import RevenueCat
import RevenueCatUI
struct ContentView: View {
// 페이월 시트 표시를 트리거합니다.
@State private var showPaywall = false
// 페이월용 ViewModel 인스턴스를 생성합니다.
// @StateObject는 뷰와 동일한 생명주기를 보장합니다.
@StateObject private var paywallViewModel = PaywallViewModel()
var body: some View {
VStack(spacing: 20) {
Text("앱에 오신 것을 환영합니다!")
Button("프리미엄으로 업그레이드") {
// 버튼을 탭하면 시트를 표시하도록 플래그를 설정합니다.
showPaywall = true
}
}
.sheet(isPresented: $showPaywall) {
// 시트의 콘텐츠입니다.
// PaywallView를 표시하고 ViewModel에서 Offering을 전달합니다.
// Offering을 가져오는 동안 로딩 인디케이터를 표시할 수 있습니다.
if paywallViewModel.isLoading {
ProgressView()
} else if let offering = paywallViewModel.offering {
// Offering을 사용할 수 있으면 PaywallView에 전달합니다.
PaywallView(offering: offering)
} else {
// 선택 사항: Offering을 로드할 수 없는 경우 오류 또는 메시지를 표시합니다.
Text("죄송합니다. 현재 구독 옵션을 로드할 수 없습니다.")
}
}
}
}루트 뷰와 같은 앱의 최상위 뷰에 일부 수정자를 첨부할 수 있습니다. 표시 로직을 자동으로 처리해 줍니다.
iPhone에서 RevenueCat Paywall은 시트 또는 전체 화면으로 표시할 수 있으며, SwiftUI 또는 UIKit에서 연동하기 위한 여러 옵션이 있습니다:
- presentPaywallIfNeeded를 사용한 Entitlement 기반 자동 표시
presentPaywallIfNeeded를 사용한 커스텀 표시 로직- PaywallView 또는
PaywallViewController를 사용한 수동 연동
예를 들어:
import SwiftUI
import RevenueCat
import RevenueCatUI
@main
struct MyApp: App {
// ... (init 코드) ...
var body: some Scene {
WindowGroup {
ContentView()
// 이 수정자는 "premium" Entitlement를 확인합니다.
// 사용자에게 없으면 페이월 시트가 표시됩니다.
.presentPaywallIfNeeded(
requiredEntitlementIdentifier: "premium",
purchaseCompleted: { customerInfo in
// 구매를 축하하기 좋은 곳입니다!
print("구매 완료: \(customerInfo.entitlements)")
},
restoreCompleted: { customerInfo in
// Entitlement가 활성화되면 페이월이 자동으로 닫힙니다.
print("복원 완료: \(customerInfo.entitlements)")
}
)
}
}
}구성 완료! 이제 사용자가 필요한 Entitlement를 가지고 있지 않을 때마다 Paywall Editor에서 구성한 것과 동일한 디자인으로 페이월을 표시할 수 있습니다.
이미 보셨듯이 페이월 시스템은 서버 기반 UI로 구축되어 있습니다. 즉, 앱 업데이트를 푸시하거나 App Store 심사를 거치지 않고도 대시보드에서 직접 페이월의 콘텐츠와 디자인을 동적으로 업데이트할 수 있습니다.
마무리
이 코드랩에서 RevenueCat의 iOS SDK를 연동하고, 인앱 결제를 구현하고, SwiftUI에서 페이월을 구축하는 방법을 배웠습니다. 이제 앱을 출시하고 더 많은 수익을 창출할 시간입니다! 💰
아래 리소스를 통해 SwiftUI를 사용한 iOS 프로젝트에서 RevenueCat SDK 사용에 대해 더 자세히 알아볼 수 있습니다: