DateP
월간 갱신 로직 변경 및 문제 요약 04 26
Solo.dev
2025. 4. 27. 02:36
월간 갱신 로직 변경 및 문제 요약
개요
- 프로젝트: DateP
- 대상 파일:
purchase.tsx,startPurchase.ts,SubscriptionProvider.tsx - 변경 초점: 월간 갱신 로직 (
basic: 50회,premium: 100회 리셋) 안정성 및 명확성 개선 - 추가 문제: 구독 취소 후 복구, Firebase
update에러
변경된 주요 부분
- purchase.tsx:
handleStartPurchase:- Firebase 저장 로직 개선:
snapshot.exists()로 데이터 확인 후update/set. free플랜 선택 시 모달 닫기 추가 (setModalVisible(false)).
const snapshot = await get(subscriptionRef); const existingData = snapshot.val(); if (existingData) { await update(subscriptionRef, subscriptionData); } else { await set(subscriptionRef, subscriptionData); }- Firebase 저장 로직 개선:
handleRestorePurchases:- Firebase 저장 로직 동일 개선.
- 에러 처리에서 기본값 리셋 (
free,remainingUses: 1) 강화.
- startPurchase.ts:
startPurchase:- 월간 리셋 조건 명확화:
isExpired로 월 변경 확인. Alert.alert호출 제거 제안 (미적용).- 수정:
transaction선언을 조기에 이동하여TS2448,TS2454오류 해결. - 수정: Firebase
set에서undefined값 제거로transactionId에러 해결. - 수정:
subscriptionData객체 명시적 정의 및 유효성 검사 추가, 디버깅 로그 삽입. - 수정:
finishTransaction조건부 호출 추가 (isAcknowledgedAndroid확인).
if (Platform.OS !== 'android' || !extendedPurchase.isAcknowledgedAndroid) { await RNIap.finishTransaction({ purchase: extendedPurchase, isConsumable: false, }); console.log('✅ 구독 구매 완료'); } else { console.log('✅ 이미 확인된 구매, finishTransaction 생략'); } const transaction: Transaction = { purchaseToken: Platform.OS === 'ios' ? undefined : extendedPurchase.purchaseToken, productId: extendedPurchase.productId || targetProductId, transactionId: Platform.OS === 'ios' ? extendedPurchase.transactionId : undefined, purchaseDate: extendedPurchase.transactionDate ? new Date(extendedPurchase.transactionDate).toISOString() : new Date().toISOString(), }; const subscriptionData: SubscriptionData = { ...(Platform.OS === 'android' && extendedPurchase.purchaseToken && { purchaseToken: extendedPurchase.purchaseToken }), ...(Platform.OS === 'ios' && extendedPurchase.transactionId && { transactionId: extendedPurchase.transactionId }), plan, limit, remainingUses: limit, purchaseDate: transaction.purchaseDate, }; console.log('저장할 구독 데이터:', JSON.stringify(subscriptionData, null, 2)); await set(subscriptionRef, subscriptionData);- 월간 리셋 조건 명확화:
checkSubscriptionStatus:- 월간 리셋 조건 강화:
isExpiredOrMissing. - 수정:
undefined값 제거로 Firebaseset에러 해결. - 수정: 구독 취소 시 Firebase 데이터 삭제 로직 추가.
if (!isSubscribed || !activePurchase?.autoRenewing) { const key = Platform.OS === 'ios' ? activePurchase?.transactionId : activePurchase?.purchaseToken; if (key) { const sanitizedKey = sanitizePath(key); await set(ref(db, `subscriptions/${sanitizedKey}`), null); console.log('✅ 취소된 구독 데이터 삭제'); } return []; } const updatedSubscription: SubscriptionData = { ...(Platform.OS === 'android' && activePurchase.purchaseToken && { purchaseToken: activePurchase.purchaseToken }), ...(Platform.OS === 'ios' && activePurchase.transactionId && { transactionId: activePurchase.transactionId }), plan, limit, remainingUses: limit, purchaseDate, };- 월간 리셋 조건 강화:
- SubscriptionProvider.tsx:
checkAndResetUsage:- 월간 리셋 조건 명확화:
shouldReset. storedKey누락 시 기본값 리셋 제안 (미적용).
const shouldReset = !lastReset || ( plan !== 'free' && now.getMonth() !== new Date(lastReset).getMonth() );- 월간 리셋 조건 명확화:
추가 문제 및 해결
- 문제 1: 구독 취소 후 Basic 플랜 복구:
- 원인:
getAvailablePurchases가 캐시된 구매 반환, 구독 취소 처리 부족,basic플랜 기본값 설정. - 해결:
checkSubscriptionStatus에서 구독 상태 검증 강화, 취소 시 Firebase 데이터 삭제, 기본값free로 변경.
const activePurchase = purchases.find((p) => { const isTargetProduct = targetProductIds.includes(p.productId); const isValid = Platform.OS === 'android' ? p.autoRenewing && p.purchaseStateAndroid === 0 : true; return isTargetProduct && isValid; }); if (!isSubscribed || !activePurchase?.autoRenewing) { const key = Platform.OS === 'ios' ? activePurchase?.transactionId : activePurchase?.purchaseToken; if (key) { const sanitizedKey = sanitizePath(key); await set(ref(db, `subscriptions/${sanitizedKey}`), null); } } - 원인:
- 문제 2: Firebase
update에러:- 원인:
handleStartPurchase에서subscriptionData에transactionId: undefined포함. - 해결:
subscriptionData조건부 구성,undefined값 필터링.
const subscriptionData: SubscriptionData = { ...(Platform.OS === 'android' && result.data.purchaseToken && { purchaseToken: result.data.purchaseToken }), ...(Platform.OS === 'ios' && result.data.transactionId && { transactionId: result.data.transactionId }), plan: result.data.plan, limit: result.data.limit, remainingUses: result.data.remainingUses, purchaseDate: result.data.purchaseDate, }; const cleanSubscriptionData = Object.fromEntries( Object.entries(subscriptionData).filter(([_, value]) => value !== undefined) ); - 원인:
- 문제 3:
finishTransaction에러:- 원인: 이미 확인된 구매(
isAcknowledgedAndroid: true)에 대해finishTransaction호출. - 해결:
isAcknowledgedAndroid확인 후 조건부 호출, 에러를 경고로 처리.
if (Platform.OS !== 'android' || !extendedPurchase.isAcknowledgedAndroid) { try { await RNIap.finishTransaction({ purchase: extendedPurchase, isConsumable: false, }); console.log('✅ 구독 구매 완료'); } catch (finishError: unknown) { console.error('❌ finishTransaction 실패:', finishError); const error = finishError as Error; if (error.message?.includes('purchase is not suitable to be purchased')) { console.warn('⚠️ 구매가 이미 완료된 상태로 간주됨'); } else { throw error; } } } - 원인: 이미 확인된 구매(
요약
- 주요 변경:
- Firebase 저장 로직 개선:
snapshot.exists()로 데이터 확인. - 월간 리셋 조건 명확화:
purchaseDate와 현재 월 비교. - 에러 처리 개선:
checkSubscriptionStatus와startPurchase에서undefined값 제거.startPurchase에서transaction선언 순서 수정.startPurchase에서subscriptionData명시적 정의 및 유효성 검사 추가.finishTransaction조건부 호출로 에러 방지.
- Firebase 저장 로직 개선:
- 문제 여부: 월간 리셋 로직은
basic/premium플랜에서 정확히 동작 (remainingUses→50/100). - 권장사항:
- Firebase 트랜잭션 도입.
Alert.alert를purchase.tsx로 위임.- Google Play 캐시 동기화 확인.
테스트 권장
- 수동 테스트:
basic플랜 (purchaseDate: '2025-03-15') → 날짜2025-04-01로 변경 →remainingUses: 50확인.premium플랜 →remainingUses: 100확인.- Firebase 연결 끊김 모의 → AsyncStorage 기본값 저장 확인.
- 추가: Android에서
startPurchase호출 →transactionId없이purchaseToken정상 저장 확인. - 추가: 구독 취소 후
checkSubscriptionStatus호출 → Firebase 데이터 삭제 및free플랜 확인. - 추가: 재결제 시
handleStartPurchase→transactionId: undefined에러 없음 확인.
- 단위 테스트:
- Jest로
checkAndResetUsage테스트:lastResetMonthly: '2025-03-15', 현재 날짜2025-04-01→remainingUses리셋. - 추가:
startPurchase테스트: Android에서transactionId제외,set성공 확인. - 추가:
checkSubscriptionStatus테스트: 취소된 구독 → Firebase 데이터 삭제 확인. - 추가:
handleStartPurchase테스트:subscriptionData에undefined값 제외 확인.
- Jest로