무엇을 만드나요
이 코드랩에서는 노코드 원격 빌더인 RevenueCat Paywalls V2로 페이월을 디자인하고,
RevenueCatUI로 SwiftUI 앱에 네이티브로 표시합니다. 페이월을 코드에 하드코딩하지 않습니다.
대시보드에서 만들고 게시하므로, 나중에 앱 업데이트 없이 여러분이나 마케팅 팀이 직접 페이월을 바꿀 수 있습니다.
페이월은 RevenueCat의 State of Subscription Apps 2026 데이터를 근거로 디자인합니다. 이 리포트는 실제 페이월이 어떻게 만들어지는지 분석했으며, 핵심은 다음과 같습니다.
- 페이월에 플랜 두 개를 둡니다. 앱의 41~60%가 쓰는 가장 흔한 구성입니다.
- 연간 옵션에 절약 배지를 붙여 플랜 하나를 강조합니다. 페이월의 74.5%가 가격을 강조합니다.
- 무료 체험 안내(페이월의 54%)와 "Cancel anytime" 문구로 안심을 줍니다.
- "Continue" CTA를 씁니다. 가장 흔한 콜투액션이며, 카운트다운 타이머나 진행 바는 넣지 않습니다. 상위 페이월에서는 거의 보이지 않습니다.
pro entitlement가 있는 RevenueCat 프로젝트가 필요합니다.
처음이신가요? iOS 코드랩이나
SDK 설정 가이드부터 시작하세요.
Paywalls V2란
Paywalls V2는 노코드로 원격 구성이 가능한 완전 네이티브 페이월 빌더입니다. RevenueCat 대시보드에서 시각적으로 디자인하면 SDK가 이를 네이티브로 렌더링합니다(iOS는 SwiftUI, Android는 Jetpack Compose). 원격 방식이라 앱 업데이트를 배포하지 않고도 페이월 전체를 바꿀 수 있습니다.
왜 중요할까요.
- 배포 없이 반복 개선합니다. 문구, 레이아웃, 가격 강조, 이미지를 대시보드에서 수정합니다.
- 네이티브입니다. 웹뷰가 아니라 네이티브 컴포넌트로 렌더링되고, 라이트/다크 모드에 맞춰 적응합니다.
- 실험으로 이어집니다. 페이월이 offering에 연결되므로 A/B 테스트를 할 수 있습니다(10단계).
purchases-ios 5.16.0 이상이 필요하며, 다른 플랫폼도 최신 SDK 버전이 필요합니다. 이전 빌더는
이제 레거시 페이월이라고 부르며 템플릿 변수 이름이 다릅니다.
Offering과 Package 설정하기
페이월은 offering에 담긴 package를 렌더링하므로, 이것부터 먼저 구성합니다(RevenueCat 대시보드의 프로젝트 아래에서).
- 스토어 상품(월간과 연간 구독)이 존재하고 RevenueCat으로 가져왔는지 확인합니다.
- 두 상품을
proentitlement에 연결합니다. - 기본 offering에 package 두 개, 즉
$rc_monthly와$rc_annualpackage를 추가하고, 각각 맞는 상품을 감쌉니다.
offering과 package가 처음이신가요? 상품과 가격 가져오기를 참고하세요.
에디터에서 페이월 디자인하기
대시보드에서 Paywalls로 이동한 뒤 Create paywall을 클릭합니다. 템플릿에서 시작하고(권장), 에디터의 페이월 속성("Select an Offering for your Paywall")에서 offering에 연결합니다. 이제 데이터 기반 레이아웃을 만듭니다.
- Package 컴포넌트 두 개: 하나는 월간 package, 하나는 연간 package용입니다. 두 플랜 페이월이 가장 흔한 구성입니다.
- 연간 플랜을 강조하고 할인 변수로 절약 배지를 붙입니다.
pro로 열리는 기능을 담은 Feature list와 짧은 헤드라인을 넣습니다.- 무료 체험 안내와 "Cancel anytime" 문구를 넣습니다.
- Purchase button(CTA)의 라벨을 "Continue"로 설정합니다.
템플릿 변수(중괄호 두 개)를 쓰면 가격과 혜택이 항상 스토어에서 옵니다.
{{ product.price }} -> "$9.99" (the package price)
{{ product.price_per_period }} -> "$59.99/year" (price with the billing period)
{{ product.relative_discount }} -> "37%" (annual savings vs the priciest plan)
{{ product.offer_price }} -> "free" (the intro/trial price, when there is one)
만족스러우면 Publish를 클릭합니다. 앱 업데이트 없이 곧바로 적용됩니다.
RevenueCatUI 설치하기
페이월은 RevenueCatUI 라이브러리로 렌더링됩니다. Xcode에서 Swift 패키지
https://github.com/RevenueCat/purchases-ios-spm.git을 추가하고
RevenueCat과 RevenueCatUI 제품을 둘 다 선택하세요.
import SwiftUI
import RevenueCat // the core SDK
import RevenueCatUI // the paywall views
purchases-ios 5.16.0 이상이 필요합니다. SDK는 앱 실행 시점에 이미 설정되어 있어야 합니다
(1단계의 사전 조건 참고).
presentPaywallIfNeeded로 접근 제어하기
페이월을 표시하는 가장 간단한 방법은 presentPaywallIfNeeded 뷰 모디파이어입니다. 이 모디파이어는
사용자에게 entitlement가 없을 때만 offering의 페이월을 표시하고, entitlement가 생기면 자동으로 닫힙니다.
import SwiftUI
import RevenueCat
import RevenueCatUI
struct RootView: View {
var body: some View {
ContentView()
.presentPaywallIfNeeded(
requiredEntitlementIdentifier: "pro",
// For a hard paywall, add: presentationMode: .fullScreen
purchaseCompleted: { customerInfo in
print("Purchased: \(customerInfo.entitlements.active.keys)")
},
restoreCompleted: { customerInfo in
// Dismisses automatically if "pro" is now active.
print("Restored: \(customerInfo.entitlements.active.keys)")
}
)
}
}
구매 전에는 넘어갈 방법이 없는 하드 페이월을 만들려면
presentationMode: .fullScreen으로 전체 화면에 표시합니다. 데이터상 하드 페이월은 프리미엄보다
전환율이 훨씬 높아서, 구독이 필요한 콘텐츠에서 흔히 선택합니다.
시트로 PaywallView 표시하기
예를 들어 "Upgrade" 버튼처럼 필요할 때 페이월을 표시하고 싶다면, 시트에 PaywallView를 표시합니다.
현재 offering의 페이월을 렌더링합니다.
struct SettingsView: View {
@State private var showPaywall = false
var body: some View {
Button("Upgrade to Pro") { showPaywall = true }
.sheet(isPresented: $showPaywall) {
// No need to wrap this in a ScrollView, the paywall handles scrolling.
PaywallView()
}
}
}
PaywallView()는 현재 offering을 사용합니다. 특정 offering을 표시하려면 PaywallView(offering: yourOffering)처럼
명시적으로 넘깁니다. 다만 이 경우 A/B 실험 라우팅에서는 제외됩니다.
displayCloseButton 파라미터는 V2
페이월에서 아무 효과가 없습니다. 닫기 버튼이 필요하면 에디터에서 컴포넌트로 추가하세요.
완료와 닫기 처리하기
구매, 복원, 그리고 사용자가 페이월을 닫으려는 요청에 반응하도록 핸들러를 붙입니다.
.sheet(isPresented: $showPaywall) {
PaywallView()
.onPurchaseCompleted { customerInfo in
// Verify access before unlocking; receiving CustomerInfo is not proof of entitlement.
if customerInfo.entitlements.active["pro"]?.isActive == true {
showPaywall = false
}
}
.onRestoreCompleted { customerInfo in
if customerInfo.entitlements.active["pro"]?.isActive == true {
showPaywall = false
}
}
.onRequestedDismissal {
// Fires on close-button tap (or purchase completion).
showPaywall = false
}
}
CustomerInfo를 준다고 해서
그 자체로 사용자에게 자격이 있다는 뜻은 아닙니다. 콘텐츠를 열기 전에
customerInfo.entitlements.active["pro"]?.isActive를 확인하세요. PaywallView는
onRequestedDismissal을 씁니다(여기에는 onDismiss 모디파이어가 없습니다).
다른 플랫폼에서 표시하기
같은 대시보드 페이월이 모든 플랫폼에서 렌더링됩니다. 표시 API는 다음과 같습니다.
React Native
import RevenueCatUI, { PAYWALL_RESULT } from 'react-native-purchases-ui';
const result = await RevenueCatUI.presentPaywallIfNeeded({
requiredEntitlementIdentifier: 'pro',
});
// result is PURCHASED, RESTORED, CANCELLED, ERROR, or NOT_PRESENTED
Android (Jetpack Compose)
// paywallActivityLauncher is a PaywallActivityLauncher created in onCreate.
// Launch the paywall only if the entitlement is missing:
paywallActivityLauncher.launchIfNeeded(requiredEntitlementIdentifier = "pro")
Flutter
import 'package:purchases_ui_flutter/purchases_ui_flutter.dart';
final result = await RevenueCatUI.presentPaywallIfNeeded("pro");
// result is a PaywallResult: notPresented, error, cancelled, purchased, restored
테스트, A/B, 마무리
테스트
pro가 없는 사용자로 앱을 실행하고, 두 플랜과 강조된 연간 옵션, 체험 문구, "Continue" CTA가 담긴 페이월이 뜨는지 확인합니다.- 샌드박스 구매를 완료하고, 페이월이 닫히며 콘텐츠가 열리는지 확인합니다.
- 대시보드에서 페이월을 수정하고 게시한 뒤 다시 실행합니다. 새 빌드 없이 변경이 반영됩니다.
다음은 A/B 테스트
진짜 성과는 테스트에서 나옵니다. 페이월이 offering에 연결되므로, 다른 페이월을 담은 두 번째 offering을 만들어 RevenueCat Experiment로 어느 쪽 전환율이 더 높은지 확인할 수 있습니다. 그런 다음 Targeting과 Placements로 대상별로 다른 페이월을 노출할 수 있습니다.
무엇을 만들었나요
데이터 기반 두 플랜 레이아웃을 갖춘 원격 노코드 네이티브 페이월을 만들었습니다.
presentPaywallIfNeeded와 PaywallView로 SwiftUI에 표시했고, 모든 플랫폼에서
렌더링하고 실험으로 이어질 준비까지 마쳤습니다.
계속 학습하기
- iOS 인앱 구매 & 페이월: SDK 연동 전 과정을 다루는 코드랩입니다.
- 상품과 가격 가져오기와 인트로 자격 확인하기: offering의 구성 요소입니다.
- RevenueCat: Paywalls와 Displaying Paywalls.