유니티 플러그인 / 세팅된 Vite Wrapper 레포지토리(git) 만들었습니다

혹시 필요하신 분 계시면 받아다 쓰시면 될 것 같습니다.

수정 필요한 부분 이슈나 피드백 주시면 감사드리겠습니다.

플러그인

Wrapper

7개의 좋아요

와우 감사합니다. 너무 좋네요. 혹시 전면,인앱광고등 부분도 가능하실까요? 해당 부분도 추가해 주시면 더욱 감사하겠습니다.

인앱결재 부분도 추가되었으면 좋겠습니다! 감사합니다! 파이팅!!:+1:

너무 대단하시네요ㄷㄷ
잘 쓰겠습니다ㅎㅎ

UPM에서 요거 한번 시도해보시길 바랍니다.

https://github.com/Hoongcha/Unity-App-in-TossAPI.git?path=Packages/com.hoongcha.toss-api#Exp
  • UnityCanvas

UPM에서 버전 0.3.1 확인해주시고
일단 전면형 광고개발 id로 테스트했을땐 호출되는거 확인했습니다. 한번 해보시고 궁금한 점이나 성공적으로 되시면 피드백부탁드립니다.:slightly_smiling_face:

1개의 좋아요

인앱 결제는 지금 저희팀이 mTLS 서버구축 여유가 안되서 :sweat_smile: 나중에 때가 되면 업데이트 하게될것같습니다.

1개의 좋아요

으앗! 너무 감사드립니다. 작업해서 테스트 후 피드백드릴께요~

읔. 피드백 드립니다. 뭘 잘못했는지 AdMob not available 요게 자꾸 뜨네요.

혹시 AdMob Not Supported 인가요?

AdMob not available 이라고 나옵니다.

일단 AdMob not available은 토스쪽에서 제공한 문서에서는 안내되지 않는 에러여서:sweat_smile: 상황파악후에 한번 토스에 물어봐야할 사항인거 같습니다.

물어보기전에 몇가지 사항좀 확인해보겠습니다.

  1. 테스트 광고 id로는 작동 했나요?
  2. 테스트 환경이 어떻게 될까요?

앗 이 문구는 제가 토스광고 브릿지로 만든 jslib에서 나온 에러 메세지였네요. 테스트id이고 테스트는 콘솔에 올려서 테스트하기 눌러서 실 기기에서 진행했었습니다.

프로그램이 서툴러서 여러모로 빡시네요. 혼란을드려 죄송합니다. jslib내용은 아래와 같습니다.

mergeInto(LibraryManager.library, {

// ==============================
//  1) 광고 로드 (LoadAppsInTossAdMob)
// ==============================
RequestLoadAD: function (objNamePtr, methodPtr, adGroupIdPtr) {
    var objName = UTF8ToString(objNamePtr);
    var method = UTF8ToString(methodPtr);
    var adGroupId = UTF8ToString(adGroupIdPtr);

    console.log("[TossAdsBridge] RequestLoadAD:", objName, method, adGroupId);

    var admob = null;
    if (typeof window !== "undefined" &&
        window.AppsInToss &&
        window.AppsInToss.ads &&
        window.AppsInToss.ads.admob) {
        admob = window.AppsInToss.ads.admob;
    }

    if (!admob || typeof admob.loadAppsInTossAdMob !== "function") {
        console.warn("[TossAdsBridge] loadAppsInTossAdMob not available");

        SendMessage(objName, method, JSON.stringify({
            status: "error",
            message: "AdMob not available"
        }));
        return;
    }

    admob.loadAppsInTossAdMob({ adGroupId: adGroupId })
        .then(function () {
            console.log("[TossAdsBridge] Load success:", adGroupId);
            SendMessage(objName, method, JSON.stringify({
                status: "success",
                message: "load-success:" + adGroupId
            }));
        })
        .catch(function (err) {
            console.error("[TossAdsBridge] Load error:", err);
            SendMessage(objName, method, JSON.stringify({
                status: "failedToShow",
                message: String(err)
            }));
        });
},

// ==============================
//  2) 광고 표시 (ShowAppsInTossAdMob)
// ==============================
RequesShowAD: function (objNamePtr, methodPtr, adGroupIdPtr) {
    var objName = UTF8ToString(objNamePtr);
    var method = UTF8ToString(methodPtr);
    var adGroupId = UTF8ToString(adGroupIdPtr);

    console.log("[TossAdsBridge] RequesShowAD:", objName, method, adGroupId);

    var admob = null;
    if (typeof window !== "undefined" &&
        window.AppsInToss &&
        window.AppsInToss.ads &&
        window.AppsInToss.ads.admob) {
        admob = window.AppsInToss.ads.admob;
    }

    if (!admob || typeof admob.showAppsInTossAdMob !== "function") {
        console.warn("[TossAdsBridge] showAppsInTossAdMob not available");

        SendMessage(objName, method, JSON.stringify({
            status: "failedToShow",
            message: "AdMob not available"
        }));
        return;
    }

    // kind는 AppsInToss에서 AD_GROUP_ID 설정에 따라 알아서 처리하도록,
    // 여기서는 adGroupId만 넘긴다.
    admob.showAppsInTossAdMob({ adGroupId: adGroupId })
        .then(function (res) {
            console.log("[TossAdsBridge] Show success:", res);

            // res에서 뭘 주는지 몰라도, 기본값 세팅
            var status = "show";
            var unitType = "";
            var unitAmount = 0;

            // res가 있다면 적당히 꺼내 쓰기 (optional chaining 못 쓰므로 if로 체크)
            if (res) {
                if (typeof res.status === "string") {
                    status = res.status;
                }
                if (typeof res.unitType === "string") {
                    unitType = res.unitType;
                }
                if (typeof res.unitAmount === "number") {
                    unitAmount = res.unitAmount;
                }
            }

            // 보상형인 경우 AppsInToss에서 status를 "userEarnedReward"로 내려준다고 가정
            // 아니면 최소한 show/dismissed 정도는 들어올 것.
            SendMessage(objName, method, JSON.stringify({
                status: status,
                message: "ok",
                unitType: unitType,
                unitAmount: unitAmount
            }));
        })
        .catch(function (err) {
            console.error("[TossAdsBridge] Show error:", err);
            SendMessage(objName, method, JSON.stringify({
                status: "failedToShow",
                message: String(err)
            }));
        });
}

});

음… 지금 이코드만 보기에는 판단할순 없는데
if (!admob || typeof admob.showAppsInTossAdMob !== "function") {

이게 문제긴 하네요 위 객체가 리액트에 선언되어 적용되어있는지 먼저 확인 해보셔야 할것 같습니다.

혹시 적용에 어려움이 있다면 리액트에 적용법 말씀드리면

UnityCanvas(혹은 유니티 랜더 컴포넌트)에

- 상단 import
import { GoogleAdMob } from '@apps-in-toss/web-framework';

...

// 전역 선언
declare global {
  interface Window {
    TossLoadAD?: (adGroupID:string, callback: (event: any) => void) => void;
    TossShowAD?: (adGroupID:string, callback: (event: any) => void) => void;
  }
}

...

// 콜백 전역 선언
let currentLoadAdCallback: ((event: any) => void) | null = null;
let currentShowAdCallback: ((event: any) => void) | null = null;
// loadAd 리스너 해제용 cleanup 함수
let loadAdCleanup: (() => void) | null = null;

...

const UnityCanvas = () => {
  useEffect(() => {
// 인앱 광고 로드
    window.TossLoadAD = (adGroupID: string, callback: (event: any) => void) => {
      // 콜백 등록
      currentLoadAdCallback = callback;

      // 이전에 등록된 리스너 있으면 정리
      if (loadAdCleanup) {
        try {
          loadAdCleanup();
        } catch (e) {
          console.warn('이전 loadAd cleanup 중 오류:', e);
        }
        loadAdCleanup = null;
      }
      
      if (GoogleAdMob.loadAppsInTossAdMob.isSupported() !== true) {
        // 현재 실행 중인 앱(예: 토스 앱, 개발용 샌드박스 앱 등)에서 Google AdMob 광고 기능을 지원하는지 확인
        callback({ type: 'error', message: 'AdMob Not Supported' });
        return;
      }
      
      loadAdCleanup = GoogleAdMob.loadAppsInTossAdMob({
        options: { adGroupId: adGroupID },
        onEvent: (event) => {
          console.log(event.type);
          if (currentLoadAdCallback) {
            // 성공
            currentLoadAdCallback(event);
          }
        },
        onError: (error) => {
          if (currentLoadAdCallback) {
            // 실패
            currentLoadAdCallback({ type: 'error', message: error.toString() });
          }
        },
      });
    };

    // 인앱 광고 표기
    window.TossShowAD = (adGroupID: string, callback: (event: any) => void) => {
      // 콜백 등록
      currentShowAdCallback = callback;
      
      if (GoogleAdMob.showAppsInTossAdMob.isSupported() !== true) {
        // 현재 실행 중인 앱(예: 토스 앱, 개발용 샌드박스 앱 등)에서 Google AdMob 광고 기능을 지원하는지 확인
        callback({ type: 'error', message: 'AdMob Not Supported' });
        return;
      }
      
      GoogleAdMob.showAppsInTossAdMob({
        options: { adGroupId: adGroupID },
        onEvent: (event) => {
          if (currentShowAdCallback) {
            currentShowAdCallback(event);
          }
        },
        onError: (error) => {
          if (currentShowAdCallback) {
            // 실패
            currentShowAdCallback({ type: 'error', message: error.toString() });
          }
        },
      });
    };
...
// Unity 로더 스크립트 로드
  • jslib
mergeInto(LibraryManager.library, {

...

RequestLoadAD: function(gameObjectName, callbackMethod, AD_GROUP_ID) {
    var objName = UTF8ToString(gameObjectName);
    var method = UTF8ToString(callbackMethod);
    var adID = UTF8ToString(AD_GROUP_ID);
    
    if (window.TossLoadAD) {
      try {
        window.TossLoadAD(adID, function(result) {
          if (result.type === 'loaded') {
            SendMessage(objName, method, JSON.stringify({
              status: 'success',
              message: '광고 로드 성공'
            }));
          }else{
            SendMessage(objName, method, JSON.stringify({
              status: 'error',
              message: result.message
            }));
          }
        });
      } catch (error) {
        SendMessage(objName, method, JSON.stringify({
          status: 'error',
          message: error.toString()
        }));
      }
    } else {
      console.error('TossLoadAD 함수가 정의되지 않았습니다.');
      SendMessage(objName, method, JSON.stringify({
        status: 'error',
        message: 'React bridge function not found'
      }));
    }
  },
  RequesShowAD: function(gameObjectName, callbackMethod, AD_GROUP_ID) {
    var objName = UTF8ToString(gameObjectName);
    var method = UTF8ToString(callbackMethod);
    var adID = UTF8ToString(AD_GROUP_ID);
    
    if (window.TossShowAD) {
      try {
        window.TossShowAD(adID, function(result) {
          switch (result.type) {
            case 'requested':
              SendMessage(objName, method, JSON.stringify({
                status: 'requested',
                message: '광고 보여주기 요청 완료'
              }));
              break;
            case 'clicked':
              SendMessage(objName, method, JSON.stringify({
                status: 'clicked',
                message: '광고 클릭'
              }));
              break;
            case 'dismissed':
              SendMessage(objName, method, JSON.stringify({
                status: 'dismissed',
                message: '광고 닫힘'
              }));
              break;
            case 'impression':
              SendMessage(objName, method, JSON.stringify({
                status: 'impression',
                message: '광고 노출'
              }));
              break;
            case 'userEarnedReward':
              SendMessage(objName, method, JSON.stringify({
                status: 'userEarnedReward',
                unitType: result.data.unitType,
                unitAmount: result.data.unitAmount,
                message: '사용자가 광고 시청을 완료했음'
              }));
              break;
            case 'show':
              SendMessage(objName, method, JSON.stringify({
                status: 'show',
                message: '광고 컨텐츠 보여졌음'
              }));
              break;
            case 'failedToShow':
              SendMessage(objName, method, JSON.stringify({
                status: 'failedToShow',
                message: '광고 보여주기 실패'
              }));
              break;
            default:
              SendMessage(objName, method, JSON.stringify({
                status: 'error',
                message: 'status Something Weird'
              }));
              break;
          }
        });
      } catch (error) {
        SendMessage(objName, method, JSON.stringify({
          status: 'error',
          message: error.toString()
        }));
      }
    } else {
      console.error('TossShowAD 함수가 정의되지 않았습니다.');
      SendMessage(objName, method, JSON.stringify({
        status: 'error',
        message: 'React bridge function not found'
      }));
    }
  }

...

이렇게만 넣어주시면 작동할것 같습니다.
테스트 id 로만 테스트 해주심됩니다. (*참고 : 이해하기 | 앱인토스 개발자센터 )

와우 감사합니다! 적용해서 테스트 했더니 테스트광고 뜨는 거 까지 성공했습니다!!

1개의 좋아요