리워드 광고 loadFullScreenAd 호출 후 loaded/onError 없이 타임아웃되는 문제>> 추가 확인 결과 공유3

이 글의 성격은 무엇인가요?

질문 / 문제 해결

내용을 설명해주세요

안녕하세요

코드 첨부가 되지않아 다시 보내드립니다

[현재 광고 로드 코드]

const loadSupported = safeSupported(loadFullScreenAd);

await new Promise((resolve, reject) => {
const timer = setTimeout(() => {
cleanupTimer();
reject(new Error(“광고 준비 시간이 초과됐어요.”));
}, 90_000);

function cleanupTimer() {
clearTimeout(timer);
}

loadUnsubscribe = loadFullScreenAd({
options: { adGroupId },

onEvent: (event) => {
console.info(“[rewarded-ad] load 이벤트”, event?.type);

if (event.type === “loaded”) {
cleanupTimer();
resolve();
}
},

onError: (error) => {
console.warn(“[rewarded-ad] load onError:”, error);
cleanupTimer();
reject(error);
},
});
});

[광고 ID 설정]

const TEST_AD_GROUP_ID = “ait-ad-test-rewarded-id”;
const LIVE_AD_GROUP_ID = “ait.v2.live.7b5c8e4d351a42d3”;

현재 테스트에서는 TEST_AD_GROUP_ID만 사용 중입니다.

공식 문서 흐름대로
loadFullScreenAd → loaded 확인 → showFullScreenAd
순서로 구현했고,
광고 필요 화면 진입 시 미리 loadFullScreenAd를 호출합니다.

혹시 위 코드에서 잘못 사용한 부분이 있는지,
또는 WebView SDK 3.x + 토스 앱 5.276.0 환경에서
loaded / onError 둘 다 오지 않는 알려진 이슈가 있는지 확인 부탁드립니다.

감사합니다.

안녕하세요
안드로이드, iOS 둘 다 발생하는 증상인가요?

전달 주신 코드로 재현해봤을때, 정상적으로 loaded 이벤트를 수신했습니다.
광고 load 호출부를 아래 코드처럼 바꿔서 화면 로그를 공유주실 수 있나요 ?

import { useState } from 'react';
  import { loadFullScreenAd } from '@apps-in-toss/web-framework';

  export function RewardedAdDebug() {
    const [debugLogs, setDebugLogs] = useState<string[]>([]);

    async function loadRewardedAdForDebug(adGroupId: string) {
      const startedAt = Date.now();
      const loadSupported = loadFullScreenAd.isSupported();

      setDebugLogs((prev) => [
        `[rewarded-ad] load start id=${adGroupId}`,
        `[rewarded-ad] supported=${String(loadSupported)}, visibility=${document.visibilityState}`,
        ...prev,
      ]);

      if (!loadSupported) {
        setDebugLogs((prev) => ['[rewarded-ad] load unsupported', ...prev]);
        throw new Error('loadFullScreenAd is not supported');
      }

      await new Promise<void>((resolve, reject) => {
        let loadUnsubscribe: (() => void) | undefined;

        const timer = setTimeout(() => {
          setDebugLogs((prev) => [
            `[rewarded-ad] load timeout elapsedMs=${Date.now() - startedAt}, visibility=${
              document.visibilityState
            }, hasUnsubscribe=${String(typeof loadUnsubscribe === 'function')}`,
            ...prev,
          ]);

          cleanupTimer();
          reject(new Error('광고 준비 시간이 초과됐어요.'));
        }, 90_000);

        function cleanupTimer() {
          clearTimeout(timer);
        }

        try {
          loadUnsubscribe = loadFullScreenAd({
            options: { adGroupId },

            onEvent: (event) => {
              setDebugLogs((prev) => [
                `[rewarded-ad] load event type=${event?.type}, elapsedMs=${Date.now() - startedAt}`,
                ...prev,
              ]);

              if (event?.type === 'loaded') {
                cleanupTimer();
                resolve();
              }
            },

            onError: (error) => {
              setDebugLogs((prev) => [
                `[rewarded-ad] load onError ${
                  error instanceof Error ? error.message : String(error)
                }`,
                ...prev,
              ]);

              cleanupTimer();
              reject(error);
            },
          });

          setDebugLogs((prev) => [
            `[rewarded-ad] load registered hasUnsubscribe=${String(
              typeof loadUnsubscribe === 'function',
            )}`,
            ...prev,
          ]);
        } catch (error) {
          setDebugLogs((prev) => [
            `[rewarded-ad] load threw ${error instanceof Error ? error.message : String(error)}`,
            ...prev,
          ]);

          cleanupTimer();
          reject(error);
        }
      });

      setDebugLogs((prev) => ['[rewarded-ad] load resolved', ...prev]);
    }

    return (
      <div>
        <button type="button" onClick={() => loadRewardedAdForDebug('ait-ad-test-rewarded-id')}>
          리워드 광고 로드
        </button>

        <pre style={{ whiteSpace: 'pre-wrap' }}>{debugLogs.join('\n')}</pre>
      </div>
    );
  }

현재 ios에서만 확인 되었습니다. 안드로이드로는 확인 해보지 못했습니다.

안녕하세요. 전달해주신 디버그 코드를 적용해서
토스 앱 5.276.0 / QR 테스트에서 다시 확인했습니다.

그런데 리워드 광고 로드 버튼을 누르면
loaded 이벤트까지 가지 않고 아래 오류가 바로 발생합니다.

undefined is not an object
(evaluating ‘window.__appsInTossConstants[e]’)

화면 로그 캡처도 함께 첨부드립니다.

현재 기존 화면에서는
bridge: detected
loadFullScreenAd: called
상태까지 갔지만 loaded/onError 없이 timeout 되었고,

전달해주신 코드에서
loadFullScreenAd.isSupported()를 직접 호출하니
위 __appsInTossConstants 관련 오류가 확인되었습니다.

혹시 현재 .ait 실행 환경에서
__appsInTossConstants가 정상 주입되지 않은 상태인지,
또는 WebView SDK 구성/번들 방식에서 확인해야 할 부분이 있는지
확인 부탁드립니다.

현재 미니앱은 Lovable에서 만든 웹앱을 기반으로 하고 있으며,
Publish된 웹 화면을 여는 형태의 .ait 번들을 사용하고 있습니다.

감사합니다.

혹시 외부 인프라에 배포되어있는 웹을 미니앱에서 불러오는 형식일까요 ?
그럴 경우, 앱인토스 SDK 를 이용할 수 없습니다.