@adstage/web-sdk 2.2.2 → 2.3.0

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 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) {
@@ -916,7 +919,7 @@ class CarouselSliderManager {
916
919
  let currentSlide = 0;
917
920
  const totalSlides = advertisements.length;
918
921
  const autoSlideInterval = (options?.autoSlideInterval || 3) * 1000; // 기본 3초
919
- // 슬라이드 이동 함수 (무한 루프 지원)
922
+ // 슬라이드 이동 함수 (무한 루프 지원 + 동적 높이 조정)
920
923
  const moveToSlide = (index, instant = false) => {
921
924
  currentSlide = index;
922
925
  // 애니메이션 임시 비활성화 (무한 루프용)
@@ -928,6 +931,33 @@ class CarouselSliderManager {
928
931
  }
929
932
  // 항상 퍼센트 기반으로 이동
930
933
  slideContainer.style.transform = `translateX(-${(100 / extendedAds.length) * currentSlide}%)`;
934
+ // 🆕 동적 높이 조정: 현재 슬라이드의 이미지 높이에 맞춰 컨테이너 높이 조정
935
+ if (!instant && !slot.height && !slot.width) { // 사용자가 크기를 지정하지 않은 경우에만
936
+ const currentSlideElement = slideContainer.children[currentSlide];
937
+ if (currentSlideElement) {
938
+ const currentAdElement = currentSlideElement.children[0];
939
+ if (currentAdElement) {
940
+ // 이미지 요소 찾기
941
+ const imgElement = currentAdElement.querySelector('img');
942
+ if (imgElement) {
943
+ // 이미지 로드 완료 후 높이 조정
944
+ const adjustHeight = () => {
945
+ const imgHeight = imgElement.getBoundingClientRect().height;
946
+ if (imgHeight > 0) {
947
+ sliderWrapper.style.height = `${imgHeight}px`;
948
+ sliderWrapper.style.transition = instant ? 'none' : 'height 0.4s ease-out';
949
+ }
950
+ };
951
+ if (imgElement.complete && imgElement.naturalHeight > 0) {
952
+ adjustHeight();
953
+ }
954
+ else {
955
+ imgElement.addEventListener('load', adjustHeight, { once: true });
956
+ }
957
+ }
958
+ }
959
+ }
960
+ }
931
961
  // 도트 업데이트 (무채색 스타일) - 실제 광고 인덱스 기준, 텍스트 광고가 아닐 때만
932
962
  const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
933
963
  if (dotContainer) {
@@ -3252,53 +3282,14 @@ class AdStage {
3252
3282
  * 편의성을 위한 정적 모듈 접근자들
3253
3283
  */
3254
3284
  static get ads() {
3255
- // 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
3256
- if (!AdStage.instance?._isInitialized) {
3257
- AdStage.autoInit();
3258
- }
3259
3285
  return AdStage.getInstance().ads;
3260
3286
  }
3261
3287
  static get events() {
3262
- if (!AdStage.instance?._isInitialized) {
3263
- AdStage.autoInit();
3264
- }
3265
3288
  return AdStage.getInstance().events;
3266
3289
  }
3267
3290
  static get config() {
3268
- if (!AdStage.instance?._isInitialized) {
3269
- AdStage.autoInit();
3270
- }
3271
3291
  return AdStage.getInstance().config;
3272
3292
  }
3273
- /**
3274
- * 🆕 자동 초기화 - 환경변수 기반
3275
- */
3276
- static autoInit() {
3277
- if (typeof window === 'undefined')
3278
- return; // 서버 사이드에서는 실행 안함
3279
- try {
3280
- // 환경변수에서 API 키 자동 로드
3281
- const apiKey = window.__ADSTAGE_API_KEY__ || // 런타임 설정
3282
- process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
3283
- process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
3284
- process.env.VITE_ADSTAGE_API_KEY || // Vite
3285
- 'demo-api-key'; // 개발용 fallback
3286
- const isDevelopment = "production" === 'development' ||
3287
- window.__ADSTAGE_DEBUG__ === true;
3288
- AdStage.init({
3289
- apiKey: apiKey,
3290
- debug: isDevelopment,
3291
- modules: ['ads', 'config']
3292
- });
3293
- if (isDevelopment) {
3294
- console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
3295
- }
3296
- }
3297
- catch (error) {
3298
- console.warn('AdStage auto-initialization failed:', error);
3299
- console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
3300
- }
3301
- }
3302
3293
  /**
3303
3294
  * SDK 리셋 (테스트용)
3304
3295
  */
@@ -3310,6 +3301,82 @@ class AdStage {
3310
3301
  }
3311
3302
  }
3312
3303
 
3304
+ const AdStageContext = react.createContext(null);
3305
+ function AdStageProvider({ children, config }) {
3306
+ const [isInitialized, setIsInitialized] = react.useState(false);
3307
+ const [currentConfig, setCurrentConfig] = react.useState(null);
3308
+ const [error, setError] = react.useState(null);
3309
+ const initialize = (newConfig) => {
3310
+ try {
3311
+ setError(null);
3312
+ // 기존 인스턴스가 있으면 리셋
3313
+ if (isInitialized) {
3314
+ AdStage.reset();
3315
+ }
3316
+ AdStage.init(newConfig);
3317
+ setCurrentConfig(newConfig);
3318
+ setIsInitialized(true);
3319
+ if (newConfig.debug) {
3320
+ console.log('✅ AdStage SDK initialized successfully via React Provider');
3321
+ }
3322
+ }
3323
+ catch (err) {
3324
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
3325
+ setError(errorMessage);
3326
+ console.error('❌ AdStage SDK initialization failed:', err);
3327
+ setIsInitialized(false);
3328
+ setCurrentConfig(null);
3329
+ }
3330
+ };
3331
+ const reset = () => {
3332
+ try {
3333
+ AdStage.reset();
3334
+ setIsInitialized(false);
3335
+ setCurrentConfig(null);
3336
+ setError(null);
3337
+ }
3338
+ catch (err) {
3339
+ console.error('❌ AdStage SDK reset failed:', err);
3340
+ }
3341
+ };
3342
+ // 자동 초기화
3343
+ react.useEffect(() => {
3344
+ if (config && !isInitialized) {
3345
+ initialize(config);
3346
+ }
3347
+ }, [config, isInitialized]);
3348
+ const contextValue = {
3349
+ isInitialized,
3350
+ config: currentConfig,
3351
+ initialize,
3352
+ reset,
3353
+ error
3354
+ };
3355
+ return (jsxRuntime.jsx(AdStageContext.Provider, { value: contextValue, children: children }));
3356
+ }
3357
+ /**
3358
+ * AdStage Context Hook
3359
+ * AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
3360
+ */
3361
+ function useAdStage() {
3362
+ const context = react.useContext(AdStageContext);
3363
+ if (!context) {
3364
+ throw new Error('useAdStage must be used within an AdStageProvider');
3365
+ }
3366
+ return context;
3367
+ }
3368
+ /**
3369
+ * AdStage SDK Hook
3370
+ * 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
3371
+ */
3372
+ function useAdStageSDK() {
3373
+ const { isInitialized } = useAdStage();
3374
+ if (!isInitialized) {
3375
+ throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
3376
+ }
3377
+ return AdStage;
3378
+ }
3379
+
3313
3380
  /**
3314
3381
  * AdStage Web SDK
3315
3382
  * 네임스페이스 아키텍처 기반 SDK
@@ -3320,5 +3387,8 @@ const SDK_VERSION = '2.0.0';
3320
3387
  const SUPPORTED_MODULES = ['ads', 'events', 'config'];
3321
3388
 
3322
3389
  exports.AdStage = AdStage;
3390
+ exports.AdStageProvider = AdStageProvider;
3323
3391
  exports.SDK_VERSION = SDK_VERSION;
3324
3392
  exports.SUPPORTED_MODULES = SUPPORTED_MODULES;
3393
+ exports.useAdStage = useAdStage;
3394
+ 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) {
@@ -914,7 +917,7 @@ class CarouselSliderManager {
914
917
  let currentSlide = 0;
915
918
  const totalSlides = advertisements.length;
916
919
  const autoSlideInterval = (options?.autoSlideInterval || 3) * 1000; // 기본 3초
917
- // 슬라이드 이동 함수 (무한 루프 지원)
920
+ // 슬라이드 이동 함수 (무한 루프 지원 + 동적 높이 조정)
918
921
  const moveToSlide = (index, instant = false) => {
919
922
  currentSlide = index;
920
923
  // 애니메이션 임시 비활성화 (무한 루프용)
@@ -926,6 +929,33 @@ class CarouselSliderManager {
926
929
  }
927
930
  // 항상 퍼센트 기반으로 이동
928
931
  slideContainer.style.transform = `translateX(-${(100 / extendedAds.length) * currentSlide}%)`;
932
+ // 🆕 동적 높이 조정: 현재 슬라이드의 이미지 높이에 맞춰 컨테이너 높이 조정
933
+ if (!instant && !slot.height && !slot.width) { // 사용자가 크기를 지정하지 않은 경우에만
934
+ const currentSlideElement = slideContainer.children[currentSlide];
935
+ if (currentSlideElement) {
936
+ const currentAdElement = currentSlideElement.children[0];
937
+ if (currentAdElement) {
938
+ // 이미지 요소 찾기
939
+ const imgElement = currentAdElement.querySelector('img');
940
+ if (imgElement) {
941
+ // 이미지 로드 완료 후 높이 조정
942
+ const adjustHeight = () => {
943
+ const imgHeight = imgElement.getBoundingClientRect().height;
944
+ if (imgHeight > 0) {
945
+ sliderWrapper.style.height = `${imgHeight}px`;
946
+ sliderWrapper.style.transition = instant ? 'none' : 'height 0.4s ease-out';
947
+ }
948
+ };
949
+ if (imgElement.complete && imgElement.naturalHeight > 0) {
950
+ adjustHeight();
951
+ }
952
+ else {
953
+ imgElement.addEventListener('load', adjustHeight, { once: true });
954
+ }
955
+ }
956
+ }
957
+ }
958
+ }
929
959
  // 도트 업데이트 (무채색 스타일) - 실제 광고 인덱스 기준, 텍스트 광고가 아닐 때만
930
960
  const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
931
961
  if (dotContainer) {
@@ -3250,53 +3280,14 @@ class AdStage {
3250
3280
  * 편의성을 위한 정적 모듈 접근자들
3251
3281
  */
3252
3282
  static get ads() {
3253
- // 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
3254
- if (!AdStage.instance?._isInitialized) {
3255
- AdStage.autoInit();
3256
- }
3257
3283
  return AdStage.getInstance().ads;
3258
3284
  }
3259
3285
  static get events() {
3260
- if (!AdStage.instance?._isInitialized) {
3261
- AdStage.autoInit();
3262
- }
3263
3286
  return AdStage.getInstance().events;
3264
3287
  }
3265
3288
  static get config() {
3266
- if (!AdStage.instance?._isInitialized) {
3267
- AdStage.autoInit();
3268
- }
3269
3289
  return AdStage.getInstance().config;
3270
3290
  }
3271
- /**
3272
- * 🆕 자동 초기화 - 환경변수 기반
3273
- */
3274
- static autoInit() {
3275
- if (typeof window === 'undefined')
3276
- return; // 서버 사이드에서는 실행 안함
3277
- try {
3278
- // 환경변수에서 API 키 자동 로드
3279
- const apiKey = window.__ADSTAGE_API_KEY__ || // 런타임 설정
3280
- process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
3281
- process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
3282
- process.env.VITE_ADSTAGE_API_KEY || // Vite
3283
- 'demo-api-key'; // 개발용 fallback
3284
- const isDevelopment = "production" === 'development' ||
3285
- window.__ADSTAGE_DEBUG__ === true;
3286
- AdStage.init({
3287
- apiKey: apiKey,
3288
- debug: isDevelopment,
3289
- modules: ['ads', 'config']
3290
- });
3291
- if (isDevelopment) {
3292
- console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
3293
- }
3294
- }
3295
- catch (error) {
3296
- console.warn('AdStage auto-initialization failed:', error);
3297
- console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
3298
- }
3299
- }
3300
3291
  /**
3301
3292
  * SDK 리셋 (테스트용)
3302
3293
  */
@@ -3308,6 +3299,82 @@ class AdStage {
3308
3299
  }
3309
3300
  }
3310
3301
 
3302
+ const AdStageContext = createContext(null);
3303
+ function AdStageProvider({ children, config }) {
3304
+ const [isInitialized, setIsInitialized] = useState(false);
3305
+ const [currentConfig, setCurrentConfig] = useState(null);
3306
+ const [error, setError] = useState(null);
3307
+ const initialize = (newConfig) => {
3308
+ try {
3309
+ setError(null);
3310
+ // 기존 인스턴스가 있으면 리셋
3311
+ if (isInitialized) {
3312
+ AdStage.reset();
3313
+ }
3314
+ AdStage.init(newConfig);
3315
+ setCurrentConfig(newConfig);
3316
+ setIsInitialized(true);
3317
+ if (newConfig.debug) {
3318
+ console.log('✅ AdStage SDK initialized successfully via React Provider');
3319
+ }
3320
+ }
3321
+ catch (err) {
3322
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
3323
+ setError(errorMessage);
3324
+ console.error('❌ AdStage SDK initialization failed:', err);
3325
+ setIsInitialized(false);
3326
+ setCurrentConfig(null);
3327
+ }
3328
+ };
3329
+ const reset = () => {
3330
+ try {
3331
+ AdStage.reset();
3332
+ setIsInitialized(false);
3333
+ setCurrentConfig(null);
3334
+ setError(null);
3335
+ }
3336
+ catch (err) {
3337
+ console.error('❌ AdStage SDK reset failed:', err);
3338
+ }
3339
+ };
3340
+ // 자동 초기화
3341
+ useEffect(() => {
3342
+ if (config && !isInitialized) {
3343
+ initialize(config);
3344
+ }
3345
+ }, [config, isInitialized]);
3346
+ const contextValue = {
3347
+ isInitialized,
3348
+ config: currentConfig,
3349
+ initialize,
3350
+ reset,
3351
+ error
3352
+ };
3353
+ return (jsx(AdStageContext.Provider, { value: contextValue, children: children }));
3354
+ }
3355
+ /**
3356
+ * AdStage Context Hook
3357
+ * AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
3358
+ */
3359
+ function useAdStage() {
3360
+ const context = useContext(AdStageContext);
3361
+ if (!context) {
3362
+ throw new Error('useAdStage must be used within an AdStageProvider');
3363
+ }
3364
+ return context;
3365
+ }
3366
+ /**
3367
+ * AdStage SDK Hook
3368
+ * 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
3369
+ */
3370
+ function useAdStageSDK() {
3371
+ const { isInitialized } = useAdStage();
3372
+ if (!isInitialized) {
3373
+ throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
3374
+ }
3375
+ return AdStage;
3376
+ }
3377
+
3311
3378
  /**
3312
3379
  * AdStage Web SDK
3313
3380
  * 네임스페이스 아키텍처 기반 SDK
@@ -3317,4 +3384,4 @@ class AdStage {
3317
3384
  const SDK_VERSION = '2.0.0';
3318
3385
  const SUPPORTED_MODULES = ['ads', 'events', 'config'];
3319
3386
 
3320
- export { AdStage, SDK_VERSION, SUPPORTED_MODULES };
3387
+ export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStage, useAdStageSDK };
@@ -914,7 +914,7 @@ class CarouselSliderManager {
914
914
  let currentSlide = 0;
915
915
  const totalSlides = advertisements.length;
916
916
  const autoSlideInterval = (options?.autoSlideInterval || 3) * 1000; // 기본 3초
917
- // 슬라이드 이동 함수 (무한 루프 지원)
917
+ // 슬라이드 이동 함수 (무한 루프 지원 + 동적 높이 조정)
918
918
  const moveToSlide = (index, instant = false) => {
919
919
  currentSlide = index;
920
920
  // 애니메이션 임시 비활성화 (무한 루프용)
@@ -926,6 +926,33 @@ class CarouselSliderManager {
926
926
  }
927
927
  // 항상 퍼센트 기반으로 이동
928
928
  slideContainer.style.transform = `translateX(-${(100 / extendedAds.length) * currentSlide}%)`;
929
+ // 🆕 동적 높이 조정: 현재 슬라이드의 이미지 높이에 맞춰 컨테이너 높이 조정
930
+ if (!instant && !slot.height && !slot.width) { // 사용자가 크기를 지정하지 않은 경우에만
931
+ const currentSlideElement = slideContainer.children[currentSlide];
932
+ if (currentSlideElement) {
933
+ const currentAdElement = currentSlideElement.children[0];
934
+ if (currentAdElement) {
935
+ // 이미지 요소 찾기
936
+ const imgElement = currentAdElement.querySelector('img');
937
+ if (imgElement) {
938
+ // 이미지 로드 완료 후 높이 조정
939
+ const adjustHeight = () => {
940
+ const imgHeight = imgElement.getBoundingClientRect().height;
941
+ if (imgHeight > 0) {
942
+ sliderWrapper.style.height = `${imgHeight}px`;
943
+ sliderWrapper.style.transition = instant ? 'none' : 'height 0.4s ease-out';
944
+ }
945
+ };
946
+ if (imgElement.complete && imgElement.naturalHeight > 0) {
947
+ adjustHeight();
948
+ }
949
+ else {
950
+ imgElement.addEventListener('load', adjustHeight, { once: true });
951
+ }
952
+ }
953
+ }
954
+ }
955
+ }
929
956
  // 도트 업데이트 (무채색 스타일) - 실제 광고 인덱스 기준, 텍스트 광고가 아닐 때만
930
957
  const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
931
958
  if (dotContainer) {
@@ -3250,53 +3277,14 @@ class AdStage {
3250
3277
  * 편의성을 위한 정적 모듈 접근자들
3251
3278
  */
3252
3279
  static get ads() {
3253
- // 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
3254
- if (!AdStage.instance?._isInitialized) {
3255
- AdStage.autoInit();
3256
- }
3257
3280
  return AdStage.getInstance().ads;
3258
3281
  }
3259
3282
  static get events() {
3260
- if (!AdStage.instance?._isInitialized) {
3261
- AdStage.autoInit();
3262
- }
3263
3283
  return AdStage.getInstance().events;
3264
3284
  }
3265
3285
  static get config() {
3266
- if (!AdStage.instance?._isInitialized) {
3267
- AdStage.autoInit();
3268
- }
3269
3286
  return AdStage.getInstance().config;
3270
3287
  }
3271
- /**
3272
- * 🆕 자동 초기화 - 환경변수 기반
3273
- */
3274
- static autoInit() {
3275
- if (typeof window === 'undefined')
3276
- return; // 서버 사이드에서는 실행 안함
3277
- try {
3278
- // 환경변수에서 API 키 자동 로드
3279
- const apiKey = window.__ADSTAGE_API_KEY__ || // 런타임 설정
3280
- process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
3281
- process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
3282
- process.env.VITE_ADSTAGE_API_KEY || // Vite
3283
- 'demo-api-key'; // 개발용 fallback
3284
- const isDevelopment = "production" === 'development' ||
3285
- window.__ADSTAGE_DEBUG__ === true;
3286
- AdStage.init({
3287
- apiKey: apiKey,
3288
- debug: isDevelopment,
3289
- modules: ['ads', 'config']
3290
- });
3291
- if (isDevelopment) {
3292
- console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
3293
- }
3294
- }
3295
- catch (error) {
3296
- console.warn('AdStage auto-initialization failed:', error);
3297
- console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
3298
- }
3299
- }
3300
3288
  /**
3301
3289
  * SDK 리셋 (테스트용)
3302
3290
  */
@@ -3308,6 +3296,157 @@ class AdStage {
3308
3296
  }
3309
3297
  }
3310
3298
 
3299
+ var jsxRuntime = {exports: {}};
3300
+
3301
+ var reactJsxRuntime_production_min = {};
3302
+
3303
+ var react = {exports: {}};
3304
+
3305
+ var react_production_min = {};
3306
+
3307
+ /**
3308
+ * @license React
3309
+ * react.production.min.js
3310
+ *
3311
+ * Copyright (c) Facebook, Inc. and its affiliates.
3312
+ *
3313
+ * This source code is licensed under the MIT license found in the
3314
+ * LICENSE file in the root directory of this source tree.
3315
+ */
3316
+
3317
+ var hasRequiredReact_production_min;
3318
+
3319
+ function requireReact_production_min () {
3320
+ if (hasRequiredReact_production_min) return react_production_min;
3321
+ hasRequiredReact_production_min = 1;
3322
+ 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}
3323
+ 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={};
3324
+ 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;
3325
+ 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};
3326
+ 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}}
3327
+ 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)}
3328
+ 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=
3329
+ 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}
3330
+ 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;}
3331
+ 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.");}
3332
+ 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;
3333
+ react_production_min.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=W;react_production_min.act=X;
3334
+ 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);
3335
+ 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}};
3336
+ 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)};
3337
+ 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)};
3338
+ 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";
3339
+ return react_production_min;
3340
+ }
3341
+
3342
+ {
3343
+ react.exports = requireReact_production_min();
3344
+ }
3345
+
3346
+ var reactExports = react.exports;
3347
+
3348
+ /**
3349
+ * @license React
3350
+ * react-jsx-runtime.production.min.js
3351
+ *
3352
+ * Copyright (c) Facebook, Inc. and its affiliates.
3353
+ *
3354
+ * This source code is licensed under the MIT license found in the
3355
+ * LICENSE file in the root directory of this source tree.
3356
+ */
3357
+
3358
+ var hasRequiredReactJsxRuntime_production_min;
3359
+
3360
+ function requireReactJsxRuntime_production_min () {
3361
+ if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min;
3362
+ hasRequiredReactJsxRuntime_production_min = 1;
3363
+ 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};
3364
+ 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;
3365
+ return reactJsxRuntime_production_min;
3366
+ }
3367
+
3368
+ {
3369
+ jsxRuntime.exports = requireReactJsxRuntime_production_min();
3370
+ }
3371
+
3372
+ var jsxRuntimeExports = jsxRuntime.exports;
3373
+
3374
+ const AdStageContext = reactExports.createContext(null);
3375
+ function AdStageProvider({ children, config }) {
3376
+ const [isInitialized, setIsInitialized] = reactExports.useState(false);
3377
+ const [currentConfig, setCurrentConfig] = reactExports.useState(null);
3378
+ const [error, setError] = reactExports.useState(null);
3379
+ const initialize = (newConfig) => {
3380
+ try {
3381
+ setError(null);
3382
+ // 기존 인스턴스가 있으면 리셋
3383
+ if (isInitialized) {
3384
+ AdStage.reset();
3385
+ }
3386
+ AdStage.init(newConfig);
3387
+ setCurrentConfig(newConfig);
3388
+ setIsInitialized(true);
3389
+ if (newConfig.debug) {
3390
+ console.log('✅ AdStage SDK initialized successfully via React Provider');
3391
+ }
3392
+ }
3393
+ catch (err) {
3394
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
3395
+ setError(errorMessage);
3396
+ console.error('❌ AdStage SDK initialization failed:', err);
3397
+ setIsInitialized(false);
3398
+ setCurrentConfig(null);
3399
+ }
3400
+ };
3401
+ const reset = () => {
3402
+ try {
3403
+ AdStage.reset();
3404
+ setIsInitialized(false);
3405
+ setCurrentConfig(null);
3406
+ setError(null);
3407
+ }
3408
+ catch (err) {
3409
+ console.error('❌ AdStage SDK reset failed:', err);
3410
+ }
3411
+ };
3412
+ // 자동 초기화
3413
+ reactExports.useEffect(() => {
3414
+ if (config && !isInitialized) {
3415
+ initialize(config);
3416
+ }
3417
+ }, [config, isInitialized]);
3418
+ const contextValue = {
3419
+ isInitialized,
3420
+ config: currentConfig,
3421
+ initialize,
3422
+ reset,
3423
+ error
3424
+ };
3425
+ return (jsxRuntimeExports.jsx(AdStageContext.Provider, { value: contextValue, children: children }));
3426
+ }
3427
+ /**
3428
+ * AdStage Context Hook
3429
+ * AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
3430
+ */
3431
+ function useAdStage() {
3432
+ const context = reactExports.useContext(AdStageContext);
3433
+ if (!context) {
3434
+ throw new Error('useAdStage must be used within an AdStageProvider');
3435
+ }
3436
+ return context;
3437
+ }
3438
+ /**
3439
+ * AdStage SDK Hook
3440
+ * 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
3441
+ */
3442
+ function useAdStageSDK() {
3443
+ const { isInitialized } = useAdStage();
3444
+ if (!isInitialized) {
3445
+ throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
3446
+ }
3447
+ return AdStage;
3448
+ }
3449
+
3311
3450
  /**
3312
3451
  * AdStage Web SDK
3313
3452
  * 네임스페이스 아키텍처 기반 SDK
@@ -3317,4 +3456,4 @@ class AdStage {
3317
3456
  const SDK_VERSION = '2.0.0';
3318
3457
  const SUPPORTED_MODULES = ['ads', 'events', 'config'];
3319
3458
 
3320
- export { AdStage, SDK_VERSION, SUPPORTED_MODULES };
3459
+ 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.2",
4
- "description": "AdStage Web SDK - Production-ready marketing platform SDK with auto-initialization, dynamic sizing, centered image positioning, and user-defined size support",
3
+ "version": "2.3.0",
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",
@@ -102,61 +102,17 @@ export class AdStage {
102
102
  * 편의성을 위한 정적 모듈 접근자들
103
103
  */
104
104
  public static get ads() {
105
- // 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
106
- if (!AdStage.instance?._isInitialized) {
107
- AdStage.autoInit();
108
- }
109
105
  return AdStage.getInstance().ads;
110
106
  }
111
107
 
112
108
  public static get events() {
113
- if (!AdStage.instance?._isInitialized) {
114
- AdStage.autoInit();
115
- }
116
109
  return AdStage.getInstance().events;
117
110
  }
118
111
 
119
112
  public static get config() {
120
- if (!AdStage.instance?._isInitialized) {
121
- AdStage.autoInit();
122
- }
123
113
  return AdStage.getInstance().config;
124
114
  }
125
115
 
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
116
  /**
161
117
  * SDK 리셋 (테스트용)
162
118
  */
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
 
@@ -184,7 +184,7 @@ export class CarouselSliderManager {
184
184
  const totalSlides = advertisements.length;
185
185
  const autoSlideInterval = (options?.autoSlideInterval || 3) * 1000; // 기본 3초
186
186
 
187
- // 슬라이드 이동 함수 (무한 루프 지원)
187
+ // 슬라이드 이동 함수 (무한 루프 지원 + 동적 높이 조정)
188
188
  const moveToSlide = (index: number, instant = false) => {
189
189
  currentSlide = index;
190
190
 
@@ -198,6 +198,36 @@ export class CarouselSliderManager {
198
198
  // 항상 퍼센트 기반으로 이동
199
199
  slideContainer.style.transform = `translateX(-${(100 / extendedAds.length) * currentSlide}%)`;
200
200
 
201
+ // 🆕 동적 높이 조정: 현재 슬라이드의 이미지 높이에 맞춰 컨테이너 높이 조정
202
+ if (!instant && !slot.height && !slot.width) { // 사용자가 크기를 지정하지 않은 경우에만
203
+ const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
204
+ const currentSlideElement = slideContainer.children[currentSlide] as HTMLElement;
205
+
206
+ if (currentSlideElement) {
207
+ const currentAdElement = currentSlideElement.children[0] as HTMLElement;
208
+ if (currentAdElement) {
209
+ // 이미지 요소 찾기
210
+ const imgElement = currentAdElement.querySelector('img');
211
+ if (imgElement) {
212
+ // 이미지 로드 완료 후 높이 조정
213
+ const adjustHeight = () => {
214
+ const imgHeight = imgElement.getBoundingClientRect().height;
215
+ if (imgHeight > 0) {
216
+ sliderWrapper.style.height = `${imgHeight}px`;
217
+ sliderWrapper.style.transition = instant ? 'none' : 'height 0.4s ease-out';
218
+ }
219
+ };
220
+
221
+ if (imgElement.complete && imgElement.naturalHeight > 0) {
222
+ adjustHeight();
223
+ } else {
224
+ imgElement.addEventListener('load', adjustHeight, { once: true });
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+
201
231
  // 도트 업데이트 (무채색 스타일) - 실제 광고 인덱스 기준, 텍스트 광고가 아닐 때만
202
232
  const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
203
233
  if (dotContainer) {
@@ -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';