혹시 필요하신 분 계시면 받아다 쓰시면 될 것 같습니다.
수정 필요한 부분 이슈나 피드백 주시면 감사드리겠습니다.
플러그인
Wrapper
혹시 필요하신 분 계시면 받아다 쓰시면 될 것 같습니다.
수정 필요한 부분 이슈나 피드백 주시면 감사드리겠습니다.
플러그인
Wrapper
와우 감사합니다. 너무 좋네요. 혹시 전면,인앱광고등 부분도 가능하실까요? 해당 부분도 추가해 주시면 더욱 감사하겠습니다.
인앱결재 부분도 추가되었으면 좋겠습니다! 감사합니다! 파이팅!!![]()
너무 대단하시네요ㄷㄷ
잘 쓰겠습니다ㅎㅎ
UPM에서 요거 한번 시도해보시길 바랍니다.
https://github.com/Hoongcha/Unity-App-in-TossAPI.git?path=Packages/com.hoongcha.toss-api#Exp
UPM에서 버전 0.3.1 확인해주시고
일단 전면형 광고개발 id로 테스트했을땐 호출되는거 확인했습니다. 한번 해보시고 궁금한 점이나 성공적으로 되시면 피드백부탁드립니다.![]()
인앱 결제는 지금 저희팀이 mTLS 서버구축 여유가 안되서
나중에 때가 되면 업데이트 하게될것같습니다.
으앗! 너무 감사드립니다. 작업해서 테스트 후 피드백드릴께요~
읔. 피드백 드립니다. 뭘 잘못했는지 AdMob not available 요게 자꾸 뜨네요.
혹시 AdMob Not Supported 인가요?
AdMob not available 이라고 나옵니다.
일단 AdMob not available은 토스쪽에서 제공한 문서에서는 안내되지 않는 에러여서
상황파악후에 한번 토스에 물어봐야할 사항인거 같습니다.
물어보기전에 몇가지 사항좀 확인해보겠습니다.
앗 이 문구는 제가 토스광고 브릿지로 만든 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 로더 스크립트 로드
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 로만 테스트 해주심됩니다. (*참고 : 이해하기 | 앱인토스 개발자센터 )
와우 감사합니다! 적용해서 테스트 했더니 테스트광고 뜨는 거 까지 성공했습니다!!