- Published on
React Native 9장: 인앱 결제 및 구독 시스템 구현
[9장] 인앱 결제 및 구독 시스템 구현 (react-native-iap)
1. 인앱 결제란?
구분 | 설명 |
---|---|
소비성 상품 | 1회 구매 후 소모됨 (예: 포인트, 아이템) |
비소비성 상품 | 1회 구매 후 영구 보유 (예: 광고 제거) |
구독 상품 | 주기적으로 과금 (예: 월간 프리미엄) |
Google/Apple 모두 자체 결제 시스템 필수 (외부 결제는 금지)
2. 필수 준비 사항
- 개발자 계정
- 앱 등록 → 인앱 상품 등록
- 상품 ID는 고유 문자열 (예: remove_ads, premium_monthly)
- 테스트 계정 설정
3. 라이브러리 설치 및 초기 설정
npm install react-native-iap
npx pod-install
iOS 설정
- Capabilities > In-App Purchase 활성화
- StoreKit 시뮬레이션 구성 가능
Android 설정
- billing permission 자동 추가됨
4. 상품 정보 불러오기
import * as RNIap from 'react-native-iap';
const itemSkus = ['premium_monthly', 'remove_ads'];
useEffect(() => {
RNIap.initConnection().then(async () => {
const products = await RNIap.getProducts(itemSkus);
setProducts(products);
});
}, []);
- 상품 ID는 콘솔에 등록된 것과 정확히 일치해야 함
5. 결제 실행 및 처리
const purchase = await RNIap.requestPurchase({
sku: 'premium_monthly',
});
- 이후 purchaseUpdatedListener로 결과 확인
useEffect(() => {
const purchaseUpdateSubscription = RNIap.purchaseUpdatedListener((purchase) => {
if (purchase.transactionReceipt) {
RNIap.finishTransaction(purchase);
}
});
return () => purchaseUpdateSubscription.remove();
}, []);
6. 구매 복원 (Restore)
iOS에선 필수 기능 (같은 계정으로 재구매 방지)
const restored = await RNIap.getAvailablePurchases();
// 사용자 상태 복구 (광고 제거, 프리미엄 등)
7. 영수증 검증 & 보안
- 보안을 위해 구매 내역을 서버에서 검증하는 게 원칙
- iOS: Apple 서버에 영수증 전송
- Android: Google Play Developer API 사용
서버 없이 처리하면 결제 위변조 위험 있음 → Node.js + Firebase 등 백엔드 구성 필요
8. UI/UX 구성 팁
- 구매 버튼은 눈에 띄게 + 보상 설명 명확하게
- 로딩/실패 처리 필수 (에러 메시지 제공)
- 구매 후 자동 반영 (앱 재시작 없이)
요약
- 인앱 결제는 소비성, 비소비성, 구독형으로 나뉜다
- react-native-iap은 크로스 플랫폼으로 구매 요청, 상태 추적, 복원 모두 지원
- 구매 성공 시 반드시 finishTransaction() 호출
- 보안을 위해 서버 검증 필수 (실제 앱 운영 시)
심화학습
Q1. 구매 버튼을 여러 번 눌렀을 때 중복 결제가 되지 않게 하려면?
A1. 구매 중 상태를 useState로 관리하고 버튼을 비활성화하거나, 중복 호출을 차단하는 debounce 처리 필요.
Q2. 영수증 검증을 서버 없이 하면 어떤 문제가 생기나요?
A2. 해킹된 디바이스에서 purchase 응답을 위조하거나 변조된 앱에서 기능 잠금을 풀 수 있다. 보안을 신뢰할 수 없게 된다.
Q3. 구독이 만료됐는지 앱에서 실시간으로 어떻게 알 수 있나요?
A3. 앱이 실행될 때마다 getAvailablePurchases()로 현재 상태를 확인하거나, 서버에서 실시간 구독 상태 Webhook을 받아 상태 동기화해야 한다.