Unity IAP JNI 에러 해결 사례
Unity를 사용해 인앱 결제를 개발하면서 다양한 상황에서 문제가 발생할 수 있습니다. 특히 Android 환경에서 Unity IAP를 활용하는 과정 중 JNI: Init'd AndroidJavaObject with null ptr! 에러를 만나게 되면 문제를 파악하고 해결하는 데 시간이 걸릴 수 있습니다. 이번 포스트에서는 제가 겪었던 문제와 이를 해결한 사례를 공유하고자 합니다.
문제 상황: Unity IAP에서 발생한 JNI 에러
Unity IAP를 사용해 결제를 처리하던 중, android logcat에서 아래와 같은 JNI 관련 에러를 확인했습니다.
Error Unity Exception: JNI: Init'd AndroidJavaObject with null ptr!
Error Unity at UnityEngine.AndroidJavaObject..ctor (System.IntPtr jobject) [0x0001d] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJava.cs:344
Error Unity at UnityEngine._AndroidJNIHelper.GetSignature (System.Object obj) [0x001b6] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJava.cs:1781
Error Unity at UnityEngine._AndroidJNIHelper.GetSignature (System.Object[] args) [0x00039] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJava.cs:1839
Error Unity at UnityEngine._AndroidJNIHelper.GetMethodID (System.IntPtr jclass, System.String methodName, System.Object[] args, System.Boolean isStatic) [0x00004] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJava.cs:1639
Error Unity at UnityEngine.AndroidJNIHelper.GetMethodID (System.IntPtr jclass, System.String methodName, System.Object[] args, System.Boolean isStatic) [0x00005] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJNI.bindings.cs:166
Error Unity at UnityEngine.
이 에러는 결제 트랜잭션을 처리하고 Pending 상태를 완료하는 과정에서 발생했습니다. 특히, IAP 자체는 정상적으로 작동했지만, 서버에 영수증을 저장하고 완료하는 과정에서 문제가 발생했습니다.
결제 프로세스는 아래와 같은 흐름으로 설계되었습니다:
- 사용자가 결제를 진행하고 Google Play로부터 영수증을 획득합니다.
- 서버와 통신하여 영수증을 저장하고 검증합니다.
- 검증이 완료되면 Pending 상태를 ConfirmPendingPurchase로 완료합니다.
문제 증상
- 결제 과정에서 발생한 트랜잭션을 완료하지 못했습니다.
- Unity IAP가 "이미 보유하고 있는 아이템입니다"라는 문구를 노출하며 결제 트랜잭션을 처리하지 못했습니다.
- 비동기 소켓 통신 중 스레드 풀(Thread Pool)에서 Unity IAP와 Android 간 충돌이 발생했습니다.
원인 분석: Unity IAP와 Android JNI 충돌
Unity IAP는 내부적으로 Android JNI(Java Native Interface)를 사용하여 Google Play와 통신합니다. 이 과정에서 Unity와 Android Java 객체 간의 상호작용은 반드시 메인 스레드(Main Thread)에서 이루어져야 합니다. 하지만 비동기 통신 중 스레드 풀에서 Unity IAP 메서드를 호출하면서 JNI 충돌이 발생한 것입니다.
해결 방법: MainThreadDispatcher 구현
이 문제를 해결하기 위해 MainThreadDispatcher를 구현하여 Unity IAP 호출을 메인 스레드에서 실행하도록 변경했습니다. Unity의 API는 기본적으로 메인 스레드에서 실행되어야 하기 때문에, 아래와 같은 단계를 통해 문제를 해결했습니다.
MainThreadDispatcher 구현
- MainThreadDispatcher 스크립트 생성
using System;
using System.Collections.Concurrent;
using UnityEngine;
public class MainThreadDispatcher : MonoBehaviour
{
private static readonly ConcurrentQueue<Action> _executionQueue = new ConcurrentQueue<Action>();
public static void Enqueue(Action action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
_executionQueue.Enqueue(action);
}
private void Update()
{
while (_executionQueue.TryDequeue(out var action))
{
action.Invoke();
}
}
}
- MainThreadDispatcher 초기화
- Unity의 Scene에 빈 GameObject를 생성하고, MainThreadDispatcher 스크립트를 추가합니다.
- 이 스크립트는 Update() 메서드를 통해 메인 스레드에서 큐에 담긴 작업을 실행합니다.
- IAP 호출을 메인 스레드로 이동
결제 프로세스의 Pending 처리를 다음과 같이 수정했습니다:
public void ProcessPendingPurchase(Product purchasedProduct)
{
MainThreadDispatcher.Enqueue(() =>
{
Debug.Log($"ProcessPendingPurchase, ProductID: {purchasedProduct.definition.id}");
controller.ConfirmPendingPurchase(purchasedProduct);
});
}
결과
이 방식을 적용한 후 JNI 관련 에러가 더 이상 발생하지 않았습니다. 결제 과정에서 비동기 통신과 Unity IAP 호출이 충돌하지 않도록 안전하게 처리할 수 있었습니다. 이제 결제 트랜잭션이 정상적으로 완료되고, 사용자 경험도 개선되었습니다.
교훈 및 팁
- Unity API는 반드시 메인 스레드에서 호출해야 합니다.
- Unity의 API는 대부분 메인 스레드에서 실행되도록 설계되어 있습니다. 비동기 작업을 사용할 때 메인 스레드로 작업을 전환해야 JNI 충돌을 방지할 수 있습니다.
- 결제 프로세스 설계에서 Pending 상태를 고려해야 합니다.
- Google Play의 결제 시스템은 Pending 상태를 활용합니다. 트랜잭션을 적절히 완료하지 못하면, 사용자에게 "이미 보유하고 있는 아이템" 메시지가 표시될 수 있습니다.
- MainThreadDispatcher는 다양한 비동기 작업에서 활용 가능합니다.
- 결제 처리뿐 아니라, 다른 비동기 소켓 통신, 네트워크 요청, 또는 데이터 처리에서도 메인 스레드 전환을 손쉽게 구현할 수 있습니다.
이번 사례를 통해 비동기 작업과 Unity API 호출 간의 관계를 이해하고, 문제를 해결하는 데 도움되길 바랍니다. Unity IAP를 사용하면서 비슷한 문제를 겪는 분들께 유용한 가이드가 되길 바랍니다!
'Unity > Solution' 카테고리의 다른 글
Unity URP로 전환 시 발생하는 분홍색 Material 문제 해결 방법 (1) | 2024.11.18 |
---|