@adstage/web-sdk 2.2.3 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +104 -42
- package/dist/index.d.ts +31 -5
- package/dist/index.esm.js +102 -43
- package/dist/index.standalone.js +174 -43
- package/package.json +3 -3
- package/src/core/AdStage.ts +9 -44
- package/src/index.ts +3 -0
- package/src/managers/ads/advertisement-event-tracker.ts +1 -0
- package/src/modules/ads/AdsModule.ts +19 -3
- package/src/react/AdStageProvider.tsx +117 -0
- package/src/react/index.ts +11 -0
package/dist/index.cjs.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
|
|
3
6
|
// 광고 타입 정의
|
|
4
7
|
var AdType;
|
|
5
8
|
(function (AdType) {
|
|
@@ -2799,7 +2802,7 @@ class AdsModule {
|
|
|
2799
2802
|
throw new Error(`Container not found: ${slot.containerId}`);
|
|
2800
2803
|
}
|
|
2801
2804
|
// 이벤트 추적 콜백 함수 (중복 노출 방지 포함)
|
|
2802
|
-
const trackEventCallback = (adId, slotId, eventType) => {
|
|
2805
|
+
const trackEventCallback = async (adId, slotId, eventType) => {
|
|
2803
2806
|
// 노출 이벤트인 경우 중복 확인
|
|
2804
2807
|
if (eventType === AdEventType.VIEWABLE) {
|
|
2805
2808
|
if (ViewableEventTracker.isDuplicateViewable(adId, slotId, this._config?.debug)) {
|
|
@@ -2812,8 +2815,20 @@ class AdsModule {
|
|
|
2812
2815
|
console.log(`✅ New viewable recorded for ad ${adId} in slot ${slotId}`);
|
|
2813
2816
|
}
|
|
2814
2817
|
}
|
|
2815
|
-
|
|
2816
|
-
|
|
2818
|
+
// 실제 API 호출로 이벤트 전송
|
|
2819
|
+
if (this.advertisementEventTracker) {
|
|
2820
|
+
try {
|
|
2821
|
+
await this.advertisementEventTracker.trackAdvertisementEvent(adId, slotId, eventType, {} // 기본 메타데이터
|
|
2822
|
+
);
|
|
2823
|
+
if (this._config?.debug) {
|
|
2824
|
+
console.log(`📊 Advertisement event tracked: ${eventType} for ad ${adId} in slot ${slotId}`);
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
catch (error) {
|
|
2828
|
+
if (this._config?.debug) {
|
|
2829
|
+
console.error(`❌ Failed to track ${eventType} event for ad ${adId}:`, error);
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2817
2832
|
}
|
|
2818
2833
|
};
|
|
2819
2834
|
let sliderElement;
|
|
@@ -3236,6 +3251,13 @@ class AdStage {
|
|
|
3236
3251
|
productionMode: false,
|
|
3237
3252
|
...config
|
|
3238
3253
|
};
|
|
3254
|
+
// baseUrl이 설정되었으면 endpoints에 적용
|
|
3255
|
+
if (config.baseUrl) {
|
|
3256
|
+
endpoints.setBaseUrl(config.baseUrl);
|
|
3257
|
+
if (config.debug) {
|
|
3258
|
+
console.log('🔄 API base URL set to:', config.baseUrl);
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3239
3261
|
// 모듈 동기 초기화
|
|
3240
3262
|
const enabledModules = instance._config.modules || ['ads', 'events', 'config'];
|
|
3241
3263
|
for (const moduleName of enabledModules) {
|
|
@@ -3279,53 +3301,14 @@ class AdStage {
|
|
|
3279
3301
|
* 편의성을 위한 정적 모듈 접근자들
|
|
3280
3302
|
*/
|
|
3281
3303
|
static get ads() {
|
|
3282
|
-
// 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
|
|
3283
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3284
|
-
AdStage.autoInit();
|
|
3285
|
-
}
|
|
3286
3304
|
return AdStage.getInstance().ads;
|
|
3287
3305
|
}
|
|
3288
3306
|
static get events() {
|
|
3289
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3290
|
-
AdStage.autoInit();
|
|
3291
|
-
}
|
|
3292
3307
|
return AdStage.getInstance().events;
|
|
3293
3308
|
}
|
|
3294
3309
|
static get config() {
|
|
3295
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3296
|
-
AdStage.autoInit();
|
|
3297
|
-
}
|
|
3298
3310
|
return AdStage.getInstance().config;
|
|
3299
3311
|
}
|
|
3300
|
-
/**
|
|
3301
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
3302
|
-
*/
|
|
3303
|
-
static autoInit() {
|
|
3304
|
-
if (typeof window === 'undefined')
|
|
3305
|
-
return; // 서버 사이드에서는 실행 안함
|
|
3306
|
-
try {
|
|
3307
|
-
// 환경변수에서 API 키 자동 로드
|
|
3308
|
-
const apiKey = window.__ADSTAGE_API_KEY__ || // 런타임 설정
|
|
3309
|
-
process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
|
|
3310
|
-
process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
|
|
3311
|
-
process.env.VITE_ADSTAGE_API_KEY || // Vite
|
|
3312
|
-
'demo-api-key'; // 개발용 fallback
|
|
3313
|
-
const isDevelopment = "production" === 'development' ||
|
|
3314
|
-
window.__ADSTAGE_DEBUG__ === true;
|
|
3315
|
-
AdStage.init({
|
|
3316
|
-
apiKey: apiKey,
|
|
3317
|
-
debug: isDevelopment,
|
|
3318
|
-
modules: ['ads', 'config']
|
|
3319
|
-
});
|
|
3320
|
-
if (isDevelopment) {
|
|
3321
|
-
console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
|
|
3322
|
-
}
|
|
3323
|
-
}
|
|
3324
|
-
catch (error) {
|
|
3325
|
-
console.warn('AdStage auto-initialization failed:', error);
|
|
3326
|
-
console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
|
|
3327
|
-
}
|
|
3328
|
-
}
|
|
3329
3312
|
/**
|
|
3330
3313
|
* SDK 리셋 (테스트용)
|
|
3331
3314
|
*/
|
|
@@ -3337,6 +3320,82 @@ class AdStage {
|
|
|
3337
3320
|
}
|
|
3338
3321
|
}
|
|
3339
3322
|
|
|
3323
|
+
const AdStageContext = react.createContext(null);
|
|
3324
|
+
function AdStageProvider({ children, config }) {
|
|
3325
|
+
const [isInitialized, setIsInitialized] = react.useState(false);
|
|
3326
|
+
const [currentConfig, setCurrentConfig] = react.useState(null);
|
|
3327
|
+
const [error, setError] = react.useState(null);
|
|
3328
|
+
const initialize = (newConfig) => {
|
|
3329
|
+
try {
|
|
3330
|
+
setError(null);
|
|
3331
|
+
// 기존 인스턴스가 있으면 리셋
|
|
3332
|
+
if (isInitialized) {
|
|
3333
|
+
AdStage.reset();
|
|
3334
|
+
}
|
|
3335
|
+
AdStage.init(newConfig);
|
|
3336
|
+
setCurrentConfig(newConfig);
|
|
3337
|
+
setIsInitialized(true);
|
|
3338
|
+
if (newConfig.debug) {
|
|
3339
|
+
console.log('✅ AdStage SDK initialized successfully via React Provider');
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
catch (err) {
|
|
3343
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
3344
|
+
setError(errorMessage);
|
|
3345
|
+
console.error('❌ AdStage SDK initialization failed:', err);
|
|
3346
|
+
setIsInitialized(false);
|
|
3347
|
+
setCurrentConfig(null);
|
|
3348
|
+
}
|
|
3349
|
+
};
|
|
3350
|
+
const reset = () => {
|
|
3351
|
+
try {
|
|
3352
|
+
AdStage.reset();
|
|
3353
|
+
setIsInitialized(false);
|
|
3354
|
+
setCurrentConfig(null);
|
|
3355
|
+
setError(null);
|
|
3356
|
+
}
|
|
3357
|
+
catch (err) {
|
|
3358
|
+
console.error('❌ AdStage SDK reset failed:', err);
|
|
3359
|
+
}
|
|
3360
|
+
};
|
|
3361
|
+
// 자동 초기화
|
|
3362
|
+
react.useEffect(() => {
|
|
3363
|
+
if (config && !isInitialized) {
|
|
3364
|
+
initialize(config);
|
|
3365
|
+
}
|
|
3366
|
+
}, [config, isInitialized]);
|
|
3367
|
+
const contextValue = {
|
|
3368
|
+
isInitialized,
|
|
3369
|
+
config: currentConfig,
|
|
3370
|
+
initialize,
|
|
3371
|
+
reset,
|
|
3372
|
+
error
|
|
3373
|
+
};
|
|
3374
|
+
return (jsxRuntime.jsx(AdStageContext.Provider, { value: contextValue, children: children }));
|
|
3375
|
+
}
|
|
3376
|
+
/**
|
|
3377
|
+
* AdStage Context Hook
|
|
3378
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
3379
|
+
*/
|
|
3380
|
+
function useAdStage() {
|
|
3381
|
+
const context = react.useContext(AdStageContext);
|
|
3382
|
+
if (!context) {
|
|
3383
|
+
throw new Error('useAdStage must be used within an AdStageProvider');
|
|
3384
|
+
}
|
|
3385
|
+
return context;
|
|
3386
|
+
}
|
|
3387
|
+
/**
|
|
3388
|
+
* AdStage SDK Hook
|
|
3389
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
3390
|
+
*/
|
|
3391
|
+
function useAdStageSDK() {
|
|
3392
|
+
const { isInitialized } = useAdStage();
|
|
3393
|
+
if (!isInitialized) {
|
|
3394
|
+
throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
|
|
3395
|
+
}
|
|
3396
|
+
return AdStage;
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3340
3399
|
/**
|
|
3341
3400
|
* AdStage Web SDK
|
|
3342
3401
|
* 네임스페이스 아키텍처 기반 SDK
|
|
@@ -3347,5 +3406,8 @@ const SDK_VERSION = '2.0.0';
|
|
|
3347
3406
|
const SUPPORTED_MODULES = ['ads', 'events', 'config'];
|
|
3348
3407
|
|
|
3349
3408
|
exports.AdStage = AdStage;
|
|
3409
|
+
exports.AdStageProvider = AdStageProvider;
|
|
3350
3410
|
exports.SDK_VERSION = SDK_VERSION;
|
|
3351
3411
|
exports.SUPPORTED_MODULES = SUPPORTED_MODULES;
|
|
3412
|
+
exports.useAdStage = useAdStage;
|
|
3413
|
+
exports.useAdStageSDK = useAdStageSDK;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* AdStage SDK 모듈 타입
|
|
3
6
|
*/
|
|
@@ -513,16 +516,39 @@ declare class AdStage {
|
|
|
513
516
|
static get ads(): AdsModule;
|
|
514
517
|
static get events(): EventsModule;
|
|
515
518
|
static get config(): ConfigModule;
|
|
516
|
-
/**
|
|
517
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
518
|
-
*/
|
|
519
|
-
private static autoInit;
|
|
520
519
|
/**
|
|
521
520
|
* SDK 리셋 (테스트용)
|
|
522
521
|
*/
|
|
523
522
|
static reset(): void;
|
|
524
523
|
}
|
|
525
524
|
|
|
525
|
+
interface AdStageContextType {
|
|
526
|
+
isInitialized: boolean;
|
|
527
|
+
config: AdStageConfig | null;
|
|
528
|
+
initialize: (config: AdStageConfig) => void;
|
|
529
|
+
reset: () => void;
|
|
530
|
+
error: string | null;
|
|
531
|
+
}
|
|
532
|
+
interface AdStageProviderProps {
|
|
533
|
+
children: ReactNode;
|
|
534
|
+
/**
|
|
535
|
+
* 자동 초기화를 위한 설정 (선택사항)
|
|
536
|
+
* 제공되면 Provider 마운트 시 자동으로 초기화됩니다.
|
|
537
|
+
*/
|
|
538
|
+
config?: AdStageConfig;
|
|
539
|
+
}
|
|
540
|
+
declare function AdStageProvider({ children, config }: AdStageProviderProps): react_jsx_runtime.JSX.Element;
|
|
541
|
+
/**
|
|
542
|
+
* AdStage Context Hook
|
|
543
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
544
|
+
*/
|
|
545
|
+
declare function useAdStage(): AdStageContextType;
|
|
546
|
+
/**
|
|
547
|
+
* AdStage SDK Hook
|
|
548
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
549
|
+
*/
|
|
550
|
+
declare function useAdStageSDK(): typeof AdStage;
|
|
551
|
+
|
|
526
552
|
/**
|
|
527
553
|
* AdStage Web SDK
|
|
528
554
|
* 네임스페이스 아키텍처 기반 SDK
|
|
@@ -531,4 +557,4 @@ declare class AdStage {
|
|
|
531
557
|
declare const SDK_VERSION = "2.0.0";
|
|
532
558
|
declare const SUPPORTED_MODULES: readonly ["ads", "events", "config"];
|
|
533
559
|
|
|
534
|
-
export { AdEventType, AdOptions, AdSlot, AdStage, AdStageConfig, AdType, Advertisement, ApiResponse, BaseModule, EventProperties, ModuleName, OrganizationInfo, PageData, SDK_VERSION, SUPPORTED_MODULES };
|
|
560
|
+
export { AdEventType, AdOptions, AdSlot, AdStage, AdStageConfig, AdStageProvider, AdType, Advertisement, ApiResponse, BaseModule, EventProperties, ModuleName, OrganizationInfo, PageData, SDK_VERSION, SUPPORTED_MODULES, useAdStage, useAdStageSDK };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { createContext, useState, useEffect, useContext } from 'react';
|
|
3
|
+
|
|
1
4
|
// 광고 타입 정의
|
|
2
5
|
var AdType;
|
|
3
6
|
(function (AdType) {
|
|
@@ -2797,7 +2800,7 @@ class AdsModule {
|
|
|
2797
2800
|
throw new Error(`Container not found: ${slot.containerId}`);
|
|
2798
2801
|
}
|
|
2799
2802
|
// 이벤트 추적 콜백 함수 (중복 노출 방지 포함)
|
|
2800
|
-
const trackEventCallback = (adId, slotId, eventType) => {
|
|
2803
|
+
const trackEventCallback = async (adId, slotId, eventType) => {
|
|
2801
2804
|
// 노출 이벤트인 경우 중복 확인
|
|
2802
2805
|
if (eventType === AdEventType.VIEWABLE) {
|
|
2803
2806
|
if (ViewableEventTracker.isDuplicateViewable(adId, slotId, this._config?.debug)) {
|
|
@@ -2810,8 +2813,20 @@ class AdsModule {
|
|
|
2810
2813
|
console.log(`✅ New viewable recorded for ad ${adId} in slot ${slotId}`);
|
|
2811
2814
|
}
|
|
2812
2815
|
}
|
|
2813
|
-
|
|
2814
|
-
|
|
2816
|
+
// 실제 API 호출로 이벤트 전송
|
|
2817
|
+
if (this.advertisementEventTracker) {
|
|
2818
|
+
try {
|
|
2819
|
+
await this.advertisementEventTracker.trackAdvertisementEvent(adId, slotId, eventType, {} // 기본 메타데이터
|
|
2820
|
+
);
|
|
2821
|
+
if (this._config?.debug) {
|
|
2822
|
+
console.log(`📊 Advertisement event tracked: ${eventType} for ad ${adId} in slot ${slotId}`);
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
catch (error) {
|
|
2826
|
+
if (this._config?.debug) {
|
|
2827
|
+
console.error(`❌ Failed to track ${eventType} event for ad ${adId}:`, error);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2815
2830
|
}
|
|
2816
2831
|
};
|
|
2817
2832
|
let sliderElement;
|
|
@@ -3234,6 +3249,13 @@ class AdStage {
|
|
|
3234
3249
|
productionMode: false,
|
|
3235
3250
|
...config
|
|
3236
3251
|
};
|
|
3252
|
+
// baseUrl이 설정되었으면 endpoints에 적용
|
|
3253
|
+
if (config.baseUrl) {
|
|
3254
|
+
endpoints.setBaseUrl(config.baseUrl);
|
|
3255
|
+
if (config.debug) {
|
|
3256
|
+
console.log('🔄 API base URL set to:', config.baseUrl);
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3237
3259
|
// 모듈 동기 초기화
|
|
3238
3260
|
const enabledModules = instance._config.modules || ['ads', 'events', 'config'];
|
|
3239
3261
|
for (const moduleName of enabledModules) {
|
|
@@ -3277,53 +3299,14 @@ class AdStage {
|
|
|
3277
3299
|
* 편의성을 위한 정적 모듈 접근자들
|
|
3278
3300
|
*/
|
|
3279
3301
|
static get ads() {
|
|
3280
|
-
// 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
|
|
3281
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3282
|
-
AdStage.autoInit();
|
|
3283
|
-
}
|
|
3284
3302
|
return AdStage.getInstance().ads;
|
|
3285
3303
|
}
|
|
3286
3304
|
static get events() {
|
|
3287
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3288
|
-
AdStage.autoInit();
|
|
3289
|
-
}
|
|
3290
3305
|
return AdStage.getInstance().events;
|
|
3291
3306
|
}
|
|
3292
3307
|
static get config() {
|
|
3293
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3294
|
-
AdStage.autoInit();
|
|
3295
|
-
}
|
|
3296
3308
|
return AdStage.getInstance().config;
|
|
3297
3309
|
}
|
|
3298
|
-
/**
|
|
3299
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
3300
|
-
*/
|
|
3301
|
-
static autoInit() {
|
|
3302
|
-
if (typeof window === 'undefined')
|
|
3303
|
-
return; // 서버 사이드에서는 실행 안함
|
|
3304
|
-
try {
|
|
3305
|
-
// 환경변수에서 API 키 자동 로드
|
|
3306
|
-
const apiKey = window.__ADSTAGE_API_KEY__ || // 런타임 설정
|
|
3307
|
-
process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
|
|
3308
|
-
process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
|
|
3309
|
-
process.env.VITE_ADSTAGE_API_KEY || // Vite
|
|
3310
|
-
'demo-api-key'; // 개발용 fallback
|
|
3311
|
-
const isDevelopment = "production" === 'development' ||
|
|
3312
|
-
window.__ADSTAGE_DEBUG__ === true;
|
|
3313
|
-
AdStage.init({
|
|
3314
|
-
apiKey: apiKey,
|
|
3315
|
-
debug: isDevelopment,
|
|
3316
|
-
modules: ['ads', 'config']
|
|
3317
|
-
});
|
|
3318
|
-
if (isDevelopment) {
|
|
3319
|
-
console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
|
|
3320
|
-
}
|
|
3321
|
-
}
|
|
3322
|
-
catch (error) {
|
|
3323
|
-
console.warn('AdStage auto-initialization failed:', error);
|
|
3324
|
-
console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
|
|
3325
|
-
}
|
|
3326
|
-
}
|
|
3327
3310
|
/**
|
|
3328
3311
|
* SDK 리셋 (테스트용)
|
|
3329
3312
|
*/
|
|
@@ -3335,6 +3318,82 @@ class AdStage {
|
|
|
3335
3318
|
}
|
|
3336
3319
|
}
|
|
3337
3320
|
|
|
3321
|
+
const AdStageContext = createContext(null);
|
|
3322
|
+
function AdStageProvider({ children, config }) {
|
|
3323
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
3324
|
+
const [currentConfig, setCurrentConfig] = useState(null);
|
|
3325
|
+
const [error, setError] = useState(null);
|
|
3326
|
+
const initialize = (newConfig) => {
|
|
3327
|
+
try {
|
|
3328
|
+
setError(null);
|
|
3329
|
+
// 기존 인스턴스가 있으면 리셋
|
|
3330
|
+
if (isInitialized) {
|
|
3331
|
+
AdStage.reset();
|
|
3332
|
+
}
|
|
3333
|
+
AdStage.init(newConfig);
|
|
3334
|
+
setCurrentConfig(newConfig);
|
|
3335
|
+
setIsInitialized(true);
|
|
3336
|
+
if (newConfig.debug) {
|
|
3337
|
+
console.log('✅ AdStage SDK initialized successfully via React Provider');
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
catch (err) {
|
|
3341
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
3342
|
+
setError(errorMessage);
|
|
3343
|
+
console.error('❌ AdStage SDK initialization failed:', err);
|
|
3344
|
+
setIsInitialized(false);
|
|
3345
|
+
setCurrentConfig(null);
|
|
3346
|
+
}
|
|
3347
|
+
};
|
|
3348
|
+
const reset = () => {
|
|
3349
|
+
try {
|
|
3350
|
+
AdStage.reset();
|
|
3351
|
+
setIsInitialized(false);
|
|
3352
|
+
setCurrentConfig(null);
|
|
3353
|
+
setError(null);
|
|
3354
|
+
}
|
|
3355
|
+
catch (err) {
|
|
3356
|
+
console.error('❌ AdStage SDK reset failed:', err);
|
|
3357
|
+
}
|
|
3358
|
+
};
|
|
3359
|
+
// 자동 초기화
|
|
3360
|
+
useEffect(() => {
|
|
3361
|
+
if (config && !isInitialized) {
|
|
3362
|
+
initialize(config);
|
|
3363
|
+
}
|
|
3364
|
+
}, [config, isInitialized]);
|
|
3365
|
+
const contextValue = {
|
|
3366
|
+
isInitialized,
|
|
3367
|
+
config: currentConfig,
|
|
3368
|
+
initialize,
|
|
3369
|
+
reset,
|
|
3370
|
+
error
|
|
3371
|
+
};
|
|
3372
|
+
return (jsx(AdStageContext.Provider, { value: contextValue, children: children }));
|
|
3373
|
+
}
|
|
3374
|
+
/**
|
|
3375
|
+
* AdStage Context Hook
|
|
3376
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
3377
|
+
*/
|
|
3378
|
+
function useAdStage() {
|
|
3379
|
+
const context = useContext(AdStageContext);
|
|
3380
|
+
if (!context) {
|
|
3381
|
+
throw new Error('useAdStage must be used within an AdStageProvider');
|
|
3382
|
+
}
|
|
3383
|
+
return context;
|
|
3384
|
+
}
|
|
3385
|
+
/**
|
|
3386
|
+
* AdStage SDK Hook
|
|
3387
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
3388
|
+
*/
|
|
3389
|
+
function useAdStageSDK() {
|
|
3390
|
+
const { isInitialized } = useAdStage();
|
|
3391
|
+
if (!isInitialized) {
|
|
3392
|
+
throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
|
|
3393
|
+
}
|
|
3394
|
+
return AdStage;
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3338
3397
|
/**
|
|
3339
3398
|
* AdStage Web SDK
|
|
3340
3399
|
* 네임스페이스 아키텍처 기반 SDK
|
|
@@ -3344,4 +3403,4 @@ class AdStage {
|
|
|
3344
3403
|
const SDK_VERSION = '2.0.0';
|
|
3345
3404
|
const SUPPORTED_MODULES = ['ads', 'events', 'config'];
|
|
3346
3405
|
|
|
3347
|
-
export { AdStage, SDK_VERSION, SUPPORTED_MODULES };
|
|
3406
|
+
export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStage, useAdStageSDK };
|
package/dist/index.standalone.js
CHANGED
|
@@ -2797,7 +2797,7 @@ class AdsModule {
|
|
|
2797
2797
|
throw new Error(`Container not found: ${slot.containerId}`);
|
|
2798
2798
|
}
|
|
2799
2799
|
// 이벤트 추적 콜백 함수 (중복 노출 방지 포함)
|
|
2800
|
-
const trackEventCallback = (adId, slotId, eventType) => {
|
|
2800
|
+
const trackEventCallback = async (adId, slotId, eventType) => {
|
|
2801
2801
|
// 노출 이벤트인 경우 중복 확인
|
|
2802
2802
|
if (eventType === AdEventType.VIEWABLE) {
|
|
2803
2803
|
if (ViewableEventTracker.isDuplicateViewable(adId, slotId, this._config?.debug)) {
|
|
@@ -2810,8 +2810,20 @@ class AdsModule {
|
|
|
2810
2810
|
console.log(`✅ New viewable recorded for ad ${adId} in slot ${slotId}`);
|
|
2811
2811
|
}
|
|
2812
2812
|
}
|
|
2813
|
-
|
|
2814
|
-
|
|
2813
|
+
// 실제 API 호출로 이벤트 전송
|
|
2814
|
+
if (this.advertisementEventTracker) {
|
|
2815
|
+
try {
|
|
2816
|
+
await this.advertisementEventTracker.trackAdvertisementEvent(adId, slotId, eventType, {} // 기본 메타데이터
|
|
2817
|
+
);
|
|
2818
|
+
if (this._config?.debug) {
|
|
2819
|
+
console.log(`📊 Advertisement event tracked: ${eventType} for ad ${adId} in slot ${slotId}`);
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
catch (error) {
|
|
2823
|
+
if (this._config?.debug) {
|
|
2824
|
+
console.error(`❌ Failed to track ${eventType} event for ad ${adId}:`, error);
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2815
2827
|
}
|
|
2816
2828
|
};
|
|
2817
2829
|
let sliderElement;
|
|
@@ -3234,6 +3246,13 @@ class AdStage {
|
|
|
3234
3246
|
productionMode: false,
|
|
3235
3247
|
...config
|
|
3236
3248
|
};
|
|
3249
|
+
// baseUrl이 설정되었으면 endpoints에 적용
|
|
3250
|
+
if (config.baseUrl) {
|
|
3251
|
+
endpoints.setBaseUrl(config.baseUrl);
|
|
3252
|
+
if (config.debug) {
|
|
3253
|
+
console.log('🔄 API base URL set to:', config.baseUrl);
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3237
3256
|
// 모듈 동기 초기화
|
|
3238
3257
|
const enabledModules = instance._config.modules || ['ads', 'events', 'config'];
|
|
3239
3258
|
for (const moduleName of enabledModules) {
|
|
@@ -3277,53 +3296,14 @@ class AdStage {
|
|
|
3277
3296
|
* 편의성을 위한 정적 모듈 접근자들
|
|
3278
3297
|
*/
|
|
3279
3298
|
static get ads() {
|
|
3280
|
-
// 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
|
|
3281
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3282
|
-
AdStage.autoInit();
|
|
3283
|
-
}
|
|
3284
3299
|
return AdStage.getInstance().ads;
|
|
3285
3300
|
}
|
|
3286
3301
|
static get events() {
|
|
3287
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3288
|
-
AdStage.autoInit();
|
|
3289
|
-
}
|
|
3290
3302
|
return AdStage.getInstance().events;
|
|
3291
3303
|
}
|
|
3292
3304
|
static get config() {
|
|
3293
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3294
|
-
AdStage.autoInit();
|
|
3295
|
-
}
|
|
3296
3305
|
return AdStage.getInstance().config;
|
|
3297
3306
|
}
|
|
3298
|
-
/**
|
|
3299
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
3300
|
-
*/
|
|
3301
|
-
static autoInit() {
|
|
3302
|
-
if (typeof window === 'undefined')
|
|
3303
|
-
return; // 서버 사이드에서는 실행 안함
|
|
3304
|
-
try {
|
|
3305
|
-
// 환경변수에서 API 키 자동 로드
|
|
3306
|
-
const apiKey = window.__ADSTAGE_API_KEY__ || // 런타임 설정
|
|
3307
|
-
process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
|
|
3308
|
-
process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
|
|
3309
|
-
process.env.VITE_ADSTAGE_API_KEY || // Vite
|
|
3310
|
-
'demo-api-key'; // 개발용 fallback
|
|
3311
|
-
const isDevelopment = "production" === 'development' ||
|
|
3312
|
-
window.__ADSTAGE_DEBUG__ === true;
|
|
3313
|
-
AdStage.init({
|
|
3314
|
-
apiKey: apiKey,
|
|
3315
|
-
debug: isDevelopment,
|
|
3316
|
-
modules: ['ads', 'config']
|
|
3317
|
-
});
|
|
3318
|
-
if (isDevelopment) {
|
|
3319
|
-
console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
|
|
3320
|
-
}
|
|
3321
|
-
}
|
|
3322
|
-
catch (error) {
|
|
3323
|
-
console.warn('AdStage auto-initialization failed:', error);
|
|
3324
|
-
console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
|
|
3325
|
-
}
|
|
3326
|
-
}
|
|
3327
3307
|
/**
|
|
3328
3308
|
* SDK 리셋 (테스트용)
|
|
3329
3309
|
*/
|
|
@@ -3335,6 +3315,157 @@ class AdStage {
|
|
|
3335
3315
|
}
|
|
3336
3316
|
}
|
|
3337
3317
|
|
|
3318
|
+
var jsxRuntime = {exports: {}};
|
|
3319
|
+
|
|
3320
|
+
var reactJsxRuntime_production_min = {};
|
|
3321
|
+
|
|
3322
|
+
var react = {exports: {}};
|
|
3323
|
+
|
|
3324
|
+
var react_production_min = {};
|
|
3325
|
+
|
|
3326
|
+
/**
|
|
3327
|
+
* @license React
|
|
3328
|
+
* react.production.min.js
|
|
3329
|
+
*
|
|
3330
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3331
|
+
*
|
|
3332
|
+
* This source code is licensed under the MIT license found in the
|
|
3333
|
+
* LICENSE file in the root directory of this source tree.
|
|
3334
|
+
*/
|
|
3335
|
+
|
|
3336
|
+
var hasRequiredReact_production_min;
|
|
3337
|
+
|
|
3338
|
+
function requireReact_production_min () {
|
|
3339
|
+
if (hasRequiredReact_production_min) return react_production_min;
|
|
3340
|
+
hasRequiredReact_production_min = 1;
|
|
3341
|
+
var l=Symbol.for("react.element"),n=Symbol.for("react.portal"),p=Symbol.for("react.fragment"),q=Symbol.for("react.strict_mode"),r=Symbol.for("react.profiler"),t=Symbol.for("react.provider"),u=Symbol.for("react.context"),v=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),x=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),z=Symbol.iterator;function A(a){if(null===a||"object"!==typeof a)return null;a=z&&a[z]||a["@@iterator"];return "function"===typeof a?a:null}
|
|
3342
|
+
var B={isMounted:function(){return !1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},C=Object.assign,D={};function E(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B;}E.prototype.isReactComponent={};
|
|
3343
|
+
E.prototype.setState=function(a,b){if("object"!==typeof a&&"function"!==typeof a&&null!=a)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,a,b,"setState");};E.prototype.forceUpdate=function(a){this.updater.enqueueForceUpdate(this,a,"forceUpdate");};function F(){}F.prototype=E.prototype;function G(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B;}var H=G.prototype=new F;
|
|
3344
|
+
H.constructor=G;C(H,E.prototype);H.isPureReactComponent=!0;var I=Array.isArray,J=Object.prototype.hasOwnProperty,K={current:null},L={key:!0,ref:!0,__self:!0,__source:!0};
|
|
3345
|
+
function M(a,b,e){var d,c={},k=null,h=null;if(null!=b)for(d in void 0!==b.ref&&(h=b.ref),void 0!==b.key&&(k=""+b.key),b)J.call(b,d)&&!L.hasOwnProperty(d)&&(c[d]=b[d]);var g=arguments.length-2;if(1===g)c.children=e;else if(1<g){for(var f=Array(g),m=0;m<g;m++)f[m]=arguments[m+2];c.children=f;}if(a&&a.defaultProps)for(d in g=a.defaultProps,g)void 0===c[d]&&(c[d]=g[d]);return {$$typeof:l,type:a,key:k,ref:h,props:c,_owner:K.current}}
|
|
3346
|
+
function N(a,b){return {$$typeof:l,type:a.type,key:b,ref:a.ref,props:a.props,_owner:a._owner}}function O(a){return "object"===typeof a&&null!==a&&a.$$typeof===l}function escape(a){var b={"=":"=0",":":"=2"};return "$"+a.replace(/[=:]/g,function(a){return b[a]})}var P=/\/+/g;function Q(a,b){return "object"===typeof a&&null!==a&&null!=a.key?escape(""+a.key):b.toString(36)}
|
|
3347
|
+
function R(a,b,e,d,c){var k=typeof a;if("undefined"===k||"boolean"===k)a=null;var h=!1;if(null===a)h=!0;else switch(k){case "string":case "number":h=!0;break;case "object":switch(a.$$typeof){case l:case n:h=!0;}}if(h)return h=a,c=c(h),a=""===d?"."+Q(h,0):d,I(c)?(e="",null!=a&&(e=a.replace(P,"$&/")+"/"),R(c,b,e,"",function(a){return a})):null!=c&&(O(c)&&(c=N(c,e+(!c.key||h&&h.key===c.key?"":(""+c.key).replace(P,"$&/")+"/")+a)),b.push(c)),1;h=0;d=""===d?".":d+":";if(I(a))for(var g=0;g<a.length;g++){k=
|
|
3348
|
+
a[g];var f=d+Q(k,g);h+=R(k,b,e,f,c);}else if(f=A(a),"function"===typeof f)for(a=f.call(a),g=0;!(k=a.next()).done;)k=k.value,f=d+Q(k,g++),h+=R(k,b,e,f,c);else if("object"===k)throw b=String(a),Error("Objects are not valid as a React child (found: "+("[object Object]"===b?"object with keys {"+Object.keys(a).join(", ")+"}":b)+"). If you meant to render a collection of children, use an array instead.");return h}
|
|
3349
|
+
function S(a,b,e){if(null==a)return a;var d=[],c=0;R(a,d,"","",function(a){return b.call(e,a,c++)});return d}function T(a){if(-1===a._status){var b=a._result;b=b();b.then(function(b){if(0===a._status||-1===a._status)a._status=1,a._result=b;},function(b){if(0===a._status||-1===a._status)a._status=2,a._result=b;});-1===a._status&&(a._status=0,a._result=b);}if(1===a._status)return a._result.default;throw a._result;}
|
|
3350
|
+
var U={current:null},V={transition:null},W={ReactCurrentDispatcher:U,ReactCurrentBatchConfig:V,ReactCurrentOwner:K};function X(){throw Error("act(...) is not supported in production builds of React.");}
|
|
3351
|
+
react_production_min.Children={map:S,forEach:function(a,b,e){S(a,function(){b.apply(this,arguments);},e);},count:function(a){var b=0;S(a,function(){b++;});return b},toArray:function(a){return S(a,function(a){return a})||[]},only:function(a){if(!O(a))throw Error("React.Children.only expected to receive a single React element child.");return a}};react_production_min.Component=E;react_production_min.Fragment=p;react_production_min.Profiler=r;react_production_min.PureComponent=G;react_production_min.StrictMode=q;react_production_min.Suspense=w;
|
|
3352
|
+
react_production_min.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=W;react_production_min.act=X;
|
|
3353
|
+
react_production_min.cloneElement=function(a,b,e){if(null===a||void 0===a)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+a+".");var d=C({},a.props),c=a.key,k=a.ref,h=a._owner;if(null!=b){void 0!==b.ref&&(k=b.ref,h=K.current);void 0!==b.key&&(c=""+b.key);if(a.type&&a.type.defaultProps)var g=a.type.defaultProps;for(f in b)J.call(b,f)&&!L.hasOwnProperty(f)&&(d[f]=void 0===b[f]&&void 0!==g?g[f]:b[f]);}var f=arguments.length-2;if(1===f)d.children=e;else if(1<f){g=Array(f);
|
|
3354
|
+
for(var m=0;m<f;m++)g[m]=arguments[m+2];d.children=g;}return {$$typeof:l,type:a.type,key:c,ref:k,props:d,_owner:h}};react_production_min.createContext=function(a){a={$$typeof:u,_currentValue:a,_currentValue2:a,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null};a.Provider={$$typeof:t,_context:a};return a.Consumer=a};react_production_min.createElement=M;react_production_min.createFactory=function(a){var b=M.bind(null,a);b.type=a;return b};react_production_min.createRef=function(){return {current:null}};
|
|
3355
|
+
react_production_min.forwardRef=function(a){return {$$typeof:v,render:a}};react_production_min.isValidElement=O;react_production_min.lazy=function(a){return {$$typeof:y,_payload:{_status:-1,_result:a},_init:T}};react_production_min.memo=function(a,b){return {$$typeof:x,type:a,compare:void 0===b?null:b}};react_production_min.startTransition=function(a){var b=V.transition;V.transition={};try{a();}finally{V.transition=b;}};react_production_min.unstable_act=X;react_production_min.useCallback=function(a,b){return U.current.useCallback(a,b)};react_production_min.useContext=function(a){return U.current.useContext(a)};
|
|
3356
|
+
react_production_min.useDebugValue=function(){};react_production_min.useDeferredValue=function(a){return U.current.useDeferredValue(a)};react_production_min.useEffect=function(a,b){return U.current.useEffect(a,b)};react_production_min.useId=function(){return U.current.useId()};react_production_min.useImperativeHandle=function(a,b,e){return U.current.useImperativeHandle(a,b,e)};react_production_min.useInsertionEffect=function(a,b){return U.current.useInsertionEffect(a,b)};react_production_min.useLayoutEffect=function(a,b){return U.current.useLayoutEffect(a,b)};
|
|
3357
|
+
react_production_min.useMemo=function(a,b){return U.current.useMemo(a,b)};react_production_min.useReducer=function(a,b,e){return U.current.useReducer(a,b,e)};react_production_min.useRef=function(a){return U.current.useRef(a)};react_production_min.useState=function(a){return U.current.useState(a)};react_production_min.useSyncExternalStore=function(a,b,e){return U.current.useSyncExternalStore(a,b,e)};react_production_min.useTransition=function(){return U.current.useTransition()};react_production_min.version="18.3.1";
|
|
3358
|
+
return react_production_min;
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
{
|
|
3362
|
+
react.exports = requireReact_production_min();
|
|
3363
|
+
}
|
|
3364
|
+
|
|
3365
|
+
var reactExports = react.exports;
|
|
3366
|
+
|
|
3367
|
+
/**
|
|
3368
|
+
* @license React
|
|
3369
|
+
* react-jsx-runtime.production.min.js
|
|
3370
|
+
*
|
|
3371
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3372
|
+
*
|
|
3373
|
+
* This source code is licensed under the MIT license found in the
|
|
3374
|
+
* LICENSE file in the root directory of this source tree.
|
|
3375
|
+
*/
|
|
3376
|
+
|
|
3377
|
+
var hasRequiredReactJsxRuntime_production_min;
|
|
3378
|
+
|
|
3379
|
+
function requireReactJsxRuntime_production_min () {
|
|
3380
|
+
if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min;
|
|
3381
|
+
hasRequiredReactJsxRuntime_production_min = 1;
|
|
3382
|
+
var f=reactExports,k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};
|
|
3383
|
+
function q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=""+g);void 0!==a.key&&(e=""+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return {$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}reactJsxRuntime_production_min.Fragment=l;reactJsxRuntime_production_min.jsx=q;reactJsxRuntime_production_min.jsxs=q;
|
|
3384
|
+
return reactJsxRuntime_production_min;
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
{
|
|
3388
|
+
jsxRuntime.exports = requireReactJsxRuntime_production_min();
|
|
3389
|
+
}
|
|
3390
|
+
|
|
3391
|
+
var jsxRuntimeExports = jsxRuntime.exports;
|
|
3392
|
+
|
|
3393
|
+
const AdStageContext = reactExports.createContext(null);
|
|
3394
|
+
function AdStageProvider({ children, config }) {
|
|
3395
|
+
const [isInitialized, setIsInitialized] = reactExports.useState(false);
|
|
3396
|
+
const [currentConfig, setCurrentConfig] = reactExports.useState(null);
|
|
3397
|
+
const [error, setError] = reactExports.useState(null);
|
|
3398
|
+
const initialize = (newConfig) => {
|
|
3399
|
+
try {
|
|
3400
|
+
setError(null);
|
|
3401
|
+
// 기존 인스턴스가 있으면 리셋
|
|
3402
|
+
if (isInitialized) {
|
|
3403
|
+
AdStage.reset();
|
|
3404
|
+
}
|
|
3405
|
+
AdStage.init(newConfig);
|
|
3406
|
+
setCurrentConfig(newConfig);
|
|
3407
|
+
setIsInitialized(true);
|
|
3408
|
+
if (newConfig.debug) {
|
|
3409
|
+
console.log('✅ AdStage SDK initialized successfully via React Provider');
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
catch (err) {
|
|
3413
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
3414
|
+
setError(errorMessage);
|
|
3415
|
+
console.error('❌ AdStage SDK initialization failed:', err);
|
|
3416
|
+
setIsInitialized(false);
|
|
3417
|
+
setCurrentConfig(null);
|
|
3418
|
+
}
|
|
3419
|
+
};
|
|
3420
|
+
const reset = () => {
|
|
3421
|
+
try {
|
|
3422
|
+
AdStage.reset();
|
|
3423
|
+
setIsInitialized(false);
|
|
3424
|
+
setCurrentConfig(null);
|
|
3425
|
+
setError(null);
|
|
3426
|
+
}
|
|
3427
|
+
catch (err) {
|
|
3428
|
+
console.error('❌ AdStage SDK reset failed:', err);
|
|
3429
|
+
}
|
|
3430
|
+
};
|
|
3431
|
+
// 자동 초기화
|
|
3432
|
+
reactExports.useEffect(() => {
|
|
3433
|
+
if (config && !isInitialized) {
|
|
3434
|
+
initialize(config);
|
|
3435
|
+
}
|
|
3436
|
+
}, [config, isInitialized]);
|
|
3437
|
+
const contextValue = {
|
|
3438
|
+
isInitialized,
|
|
3439
|
+
config: currentConfig,
|
|
3440
|
+
initialize,
|
|
3441
|
+
reset,
|
|
3442
|
+
error
|
|
3443
|
+
};
|
|
3444
|
+
return (jsxRuntimeExports.jsx(AdStageContext.Provider, { value: contextValue, children: children }));
|
|
3445
|
+
}
|
|
3446
|
+
/**
|
|
3447
|
+
* AdStage Context Hook
|
|
3448
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
3449
|
+
*/
|
|
3450
|
+
function useAdStage() {
|
|
3451
|
+
const context = reactExports.useContext(AdStageContext);
|
|
3452
|
+
if (!context) {
|
|
3453
|
+
throw new Error('useAdStage must be used within an AdStageProvider');
|
|
3454
|
+
}
|
|
3455
|
+
return context;
|
|
3456
|
+
}
|
|
3457
|
+
/**
|
|
3458
|
+
* AdStage SDK Hook
|
|
3459
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
3460
|
+
*/
|
|
3461
|
+
function useAdStageSDK() {
|
|
3462
|
+
const { isInitialized } = useAdStage();
|
|
3463
|
+
if (!isInitialized) {
|
|
3464
|
+
throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
|
|
3465
|
+
}
|
|
3466
|
+
return AdStage;
|
|
3467
|
+
}
|
|
3468
|
+
|
|
3338
3469
|
/**
|
|
3339
3470
|
* AdStage Web SDK
|
|
3340
3471
|
* 네임스페이스 아키텍처 기반 SDK
|
|
@@ -3344,4 +3475,4 @@ class AdStage {
|
|
|
3344
3475
|
const SDK_VERSION = '2.0.0';
|
|
3345
3476
|
const SUPPORTED_MODULES = ['ads', 'events', 'config'];
|
|
3346
3477
|
|
|
3347
|
-
export { AdStage, SDK_VERSION, SUPPORTED_MODULES };
|
|
3478
|
+
export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStage, useAdStageSDK };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adstage/web-sdk",
|
|
3
|
-
"version": "2.2
|
|
4
|
-
"description": "AdStage Web SDK - Production-ready marketing platform SDK with
|
|
3
|
+
"version": "2.3.2",
|
|
4
|
+
"description": "AdStage Web SDK - Production-ready marketing platform SDK with React Provider support for seamless integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs.js",
|
|
7
7
|
"module": "dist/index.esm.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"ads",
|
|
40
40
|
"banner",
|
|
41
41
|
"text",
|
|
42
|
-
"video",
|
|
42
|
+
"video",
|
|
43
43
|
"native",
|
|
44
44
|
"interstitial",
|
|
45
45
|
"sdk",
|
package/src/core/AdStage.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { AdStageConfig, ModuleName } from '../types/config';
|
|
|
7
7
|
import { AdsModule } from '../modules/ads/AdsModule';
|
|
8
8
|
import { ConfigModule } from '../modules/config/ConfigModule';
|
|
9
9
|
import { EventsModule } from '../modules/events/EventsModule';
|
|
10
|
+
import { endpoints } from '../constants/endpoints';
|
|
10
11
|
|
|
11
12
|
export class AdStage {
|
|
12
13
|
private static instance: AdStage;
|
|
@@ -52,6 +53,14 @@ export class AdStage {
|
|
|
52
53
|
...config
|
|
53
54
|
};
|
|
54
55
|
|
|
56
|
+
// baseUrl이 설정되었으면 endpoints에 적용
|
|
57
|
+
if (config.baseUrl) {
|
|
58
|
+
endpoints.setBaseUrl(config.baseUrl);
|
|
59
|
+
if (config.debug) {
|
|
60
|
+
console.log('🔄 API base URL set to:', config.baseUrl);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
// 모듈 동기 초기화
|
|
56
65
|
const enabledModules = instance._config.modules || ['ads', 'events', 'config'];
|
|
57
66
|
|
|
@@ -102,61 +111,17 @@ export class AdStage {
|
|
|
102
111
|
* 편의성을 위한 정적 모듈 접근자들
|
|
103
112
|
*/
|
|
104
113
|
public static get ads() {
|
|
105
|
-
// 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
|
|
106
|
-
if (!AdStage.instance?._isInitialized) {
|
|
107
|
-
AdStage.autoInit();
|
|
108
|
-
}
|
|
109
114
|
return AdStage.getInstance().ads;
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
public static get events() {
|
|
113
|
-
if (!AdStage.instance?._isInitialized) {
|
|
114
|
-
AdStage.autoInit();
|
|
115
|
-
}
|
|
116
118
|
return AdStage.getInstance().events;
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
public static get config() {
|
|
120
|
-
if (!AdStage.instance?._isInitialized) {
|
|
121
|
-
AdStage.autoInit();
|
|
122
|
-
}
|
|
123
122
|
return AdStage.getInstance().config;
|
|
124
123
|
}
|
|
125
124
|
|
|
126
|
-
/**
|
|
127
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
128
|
-
*/
|
|
129
|
-
private static autoInit(): void {
|
|
130
|
-
if (typeof window === 'undefined') return; // 서버 사이드에서는 실행 안함
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
// 환경변수에서 API 키 자동 로드
|
|
134
|
-
const apiKey =
|
|
135
|
-
(window as any).__ADSTAGE_API_KEY__ || // 런타임 설정
|
|
136
|
-
process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
|
|
137
|
-
process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
|
|
138
|
-
process.env.VITE_ADSTAGE_API_KEY || // Vite
|
|
139
|
-
'demo-api-key'; // 개발용 fallback
|
|
140
|
-
|
|
141
|
-
const isDevelopment =
|
|
142
|
-
process.env.NODE_ENV === 'development' ||
|
|
143
|
-
(window as any).__ADSTAGE_DEBUG__ === true;
|
|
144
|
-
|
|
145
|
-
AdStage.init({
|
|
146
|
-
apiKey: apiKey,
|
|
147
|
-
debug: isDevelopment,
|
|
148
|
-
modules: ['ads', 'config']
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (isDevelopment) {
|
|
152
|
-
console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
|
|
153
|
-
}
|
|
154
|
-
} catch (error) {
|
|
155
|
-
console.warn('AdStage auto-initialization failed:', error);
|
|
156
|
-
console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
125
|
/**
|
|
161
126
|
* SDK 리셋 (테스트용)
|
|
162
127
|
*/
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
// 메인 네임스페이스 클래스
|
|
7
7
|
export { default as AdStage } from './core/AdStage';
|
|
8
8
|
|
|
9
|
+
// React 통합 (선택적)
|
|
10
|
+
export { AdStageProvider, useAdStage, useAdStageSDK } from './react';
|
|
11
|
+
|
|
9
12
|
// 설정 및 타입
|
|
10
13
|
export type { AdStageConfig, ModuleName, BaseModule, ApiResponse, OrganizationInfo } from './types/config';
|
|
11
14
|
|
|
@@ -929,7 +929,7 @@ export class AdsModule implements BaseModule {
|
|
|
929
929
|
}
|
|
930
930
|
|
|
931
931
|
// 이벤트 추적 콜백 함수 (중복 노출 방지 포함)
|
|
932
|
-
const trackEventCallback = (adId: string, slotId: string, eventType: AdEventType) => {
|
|
932
|
+
const trackEventCallback = async (adId: string, slotId: string, eventType: AdEventType) => {
|
|
933
933
|
// 노출 이벤트인 경우 중복 확인
|
|
934
934
|
if (eventType === AdEventType.VIEWABLE) {
|
|
935
935
|
if (ViewableEventTracker.isDuplicateViewable(adId, slotId, this._config?.debug)) {
|
|
@@ -944,8 +944,24 @@ export class AdsModule implements BaseModule {
|
|
|
944
944
|
}
|
|
945
945
|
}
|
|
946
946
|
|
|
947
|
-
|
|
948
|
-
|
|
947
|
+
// 실제 API 호출로 이벤트 전송
|
|
948
|
+
if (this.advertisementEventTracker) {
|
|
949
|
+
try {
|
|
950
|
+
await this.advertisementEventTracker.trackAdvertisementEvent(
|
|
951
|
+
adId,
|
|
952
|
+
slotId,
|
|
953
|
+
eventType,
|
|
954
|
+
{} // 기본 메타데이터
|
|
955
|
+
);
|
|
956
|
+
|
|
957
|
+
if (this._config?.debug) {
|
|
958
|
+
console.log(`📊 Advertisement event tracked: ${eventType} for ad ${adId} in slot ${slotId}`);
|
|
959
|
+
}
|
|
960
|
+
} catch (error) {
|
|
961
|
+
if (this._config?.debug) {
|
|
962
|
+
console.error(`❌ Failed to track ${eventType} event for ad ${adId}:`, error);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
949
965
|
}
|
|
950
966
|
};
|
|
951
967
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdStage SDK - React Provider
|
|
3
|
+
* React 애플리케이션에서 AdStage SDK를 쉽게 사용할 수 있도록 하는 Provider 컴포넌트
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
|
7
|
+
import AdStage from '../core/AdStage';
|
|
8
|
+
import { AdStageConfig } from '../types/config';
|
|
9
|
+
|
|
10
|
+
interface AdStageContextType {
|
|
11
|
+
isInitialized: boolean;
|
|
12
|
+
config: AdStageConfig | null;
|
|
13
|
+
initialize: (config: AdStageConfig) => void;
|
|
14
|
+
reset: () => void;
|
|
15
|
+
error: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const AdStageContext = createContext<AdStageContextType | null>(null);
|
|
19
|
+
|
|
20
|
+
interface AdStageProviderProps {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* 자동 초기화를 위한 설정 (선택사항)
|
|
24
|
+
* 제공되면 Provider 마운트 시 자동으로 초기화됩니다.
|
|
25
|
+
*/
|
|
26
|
+
config?: AdStageConfig;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function AdStageProvider({ children, config }: AdStageProviderProps) {
|
|
30
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
31
|
+
const [currentConfig, setCurrentConfig] = useState<AdStageConfig | null>(null);
|
|
32
|
+
const [error, setError] = useState<string | null>(null);
|
|
33
|
+
|
|
34
|
+
const initialize = (newConfig: AdStageConfig) => {
|
|
35
|
+
try {
|
|
36
|
+
setError(null);
|
|
37
|
+
|
|
38
|
+
// 기존 인스턴스가 있으면 리셋
|
|
39
|
+
if (isInitialized) {
|
|
40
|
+
AdStage.reset();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
AdStage.init(newConfig);
|
|
44
|
+
|
|
45
|
+
setCurrentConfig(newConfig);
|
|
46
|
+
setIsInitialized(true);
|
|
47
|
+
|
|
48
|
+
if (newConfig.debug) {
|
|
49
|
+
console.log('✅ AdStage SDK initialized successfully via React Provider');
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
53
|
+
setError(errorMessage);
|
|
54
|
+
console.error('❌ AdStage SDK initialization failed:', err);
|
|
55
|
+
setIsInitialized(false);
|
|
56
|
+
setCurrentConfig(null);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const reset = () => {
|
|
61
|
+
try {
|
|
62
|
+
AdStage.reset();
|
|
63
|
+
setIsInitialized(false);
|
|
64
|
+
setCurrentConfig(null);
|
|
65
|
+
setError(null);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error('❌ AdStage SDK reset failed:', err);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// 자동 초기화
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (config && !isInitialized) {
|
|
74
|
+
initialize(config);
|
|
75
|
+
}
|
|
76
|
+
}, [config, isInitialized]);
|
|
77
|
+
|
|
78
|
+
const contextValue: AdStageContextType = {
|
|
79
|
+
isInitialized,
|
|
80
|
+
config: currentConfig,
|
|
81
|
+
initialize,
|
|
82
|
+
reset,
|
|
83
|
+
error
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<AdStageContext.Provider value={contextValue}>
|
|
88
|
+
{children}
|
|
89
|
+
</AdStageContext.Provider>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* AdStage Context Hook
|
|
95
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
96
|
+
*/
|
|
97
|
+
export function useAdStage(): AdStageContextType {
|
|
98
|
+
const context = useContext(AdStageContext);
|
|
99
|
+
if (!context) {
|
|
100
|
+
throw new Error('useAdStage must be used within an AdStageProvider');
|
|
101
|
+
}
|
|
102
|
+
return context;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* AdStage SDK Hook
|
|
107
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
108
|
+
*/
|
|
109
|
+
export function useAdStageSDK() {
|
|
110
|
+
const { isInitialized } = useAdStage();
|
|
111
|
+
|
|
112
|
+
if (!isInitialized) {
|
|
113
|
+
throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return AdStage;
|
|
117
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdStage Web SDK - React Integration
|
|
3
|
+
* React 애플리케이션을 위한 컴포넌트와 훅들
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { AdStageProvider, useAdStage, useAdStageSDK } from './AdStageProvider';
|
|
7
|
+
|
|
8
|
+
// 타입들도 재export
|
|
9
|
+
export type { AdStageConfig, ModuleName, BaseModule } from '../types/config';
|
|
10
|
+
export type { AdOptions } from '../modules/ads/AdsModule';
|
|
11
|
+
export type { AdType, AdEventType, Advertisement, AdSlot } from '../types/advertisement';
|