@adstage/web-sdk 2.4.2 โ†’ 2.4.5

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
@@ -607,13 +607,32 @@ class AdvertisementEventTracker {
607
607
  riskLevel: additionalData.riskLevel,
608
608
  }),
609
609
  };
610
- await fetch(`${this.baseUrl}/advertisements/events/${adId}/${eventType}`, {
610
+ const url = `${this.baseUrl}/advertisements/events/${adId}/${eventType}`;
611
+ const headers = ApiHeaders.createForEvents(this.apiKey, eventData);
612
+ if (this.debug) {
613
+ console.log(`๐Ÿš€ Sending advertisement event: ${eventType} for ad ${adId}`, {
614
+ url,
615
+ headers,
616
+ eventData
617
+ });
618
+ }
619
+ const response = await fetch(url, {
611
620
  method: 'POST',
612
- headers: ApiHeaders.createForEvents(this.apiKey, eventData),
621
+ headers,
613
622
  body: JSON.stringify(eventData),
614
623
  });
615
624
  if (this.debug) {
616
- console.log(`Tracked advertisement event: ${eventType} for ad ${adId}`, eventData);
625
+ console.log(`๐Ÿ“ก API Response Status: ${response.status} ${response.statusText}`, {
626
+ url,
627
+ ok: response.ok
628
+ });
629
+ }
630
+ if (!response.ok) {
631
+ const errorText = await response.text();
632
+ throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`);
633
+ }
634
+ if (this.debug) {
635
+ console.log(`โœ… Successfully tracked advertisement event: ${eventType} for ad ${adId}`);
617
636
  }
618
637
  }
619
638
  catch (error) {
@@ -948,6 +967,7 @@ const API_PATHS = {
948
967
  /** ๊ด‘๊ณ  ๊ด€๋ จ */
949
968
  advertisements: {
950
969
  list: '/advertisements/list',
970
+ detail: '/advertisements',
951
971
  events: '/advertisements/events'
952
972
  },
953
973
  /** ์ด๋ฒคํŠธ ๊ด€๋ จ */
@@ -966,6 +986,7 @@ class EndpointBuilder {
966
986
  */
967
987
  this.advertisements = {
968
988
  list: () => `${this.baseUrl}${API_PATHS.advertisements.list}`,
989
+ detail: (adId) => `${this.baseUrl}${API_PATHS.advertisements.detail}/${adId}`,
969
990
  events: (adId, eventType) => `${this.baseUrl}${API_PATHS.advertisements.events}/${adId}/${eventType}`
970
991
  };
971
992
  /**
@@ -1206,7 +1227,7 @@ class TextAdRenderer extends BaseAdRenderer {
1206
1227
  // ๊ธฐ๋ณธ ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ
1207
1228
  const containerStyles = {
1208
1229
  ...this.getBaseContainerStyles(slot),
1209
- padding: '20px',
1230
+ padding: '4px',
1210
1231
  background: 'transparent',
1211
1232
  display: 'flex',
1212
1233
  'align-items': 'center',
@@ -1234,7 +1255,7 @@ class TextAdRenderer extends BaseAdRenderer {
1234
1255
  }
1235
1256
  // ํ…์ŠคํŠธ ์ฝ˜ํ…์ธ  ์ƒ์„ฑ
1236
1257
  const textContent = this.createTextElement(ad.textContent, 'div', {
1237
- 'font-size': '16px',
1258
+ 'font-size': '14px',
1238
1259
  'font-weight': '500',
1239
1260
  color: '#212529',
1240
1261
  width: '100%', // ์ „์ฒด ๋„ˆ๋น„ ์‚ฌ์šฉํ•˜์—ฌ ํ…์ŠคํŠธ ์ •๋ ฌ์ด ์ ์šฉ๋˜๋„๋ก ํ•จ
@@ -2285,8 +2306,6 @@ class AdRenderer {
2285
2306
  case AdType.TEXT: {
2286
2307
  const textDiv = document.createElement('div');
2287
2308
  textDiv.innerHTML = `
2288
- <h3>${ad.title}</h3>
2289
- ${ad.description ? `<p>${ad.description}</p>` : ''}
2290
2309
  ${ad.textContent ? `<div>${ad.textContent}</div>` : ''}
2291
2310
  `;
2292
2311
  adElement.appendChild(textDiv);
@@ -2412,7 +2431,7 @@ class AdRenderer {
2412
2431
  case AdType.BANNER:
2413
2432
  return '250px'; // ์ผ๋ฐ˜ ๋ฐฐ๋„ˆ
2414
2433
  case AdType.TEXT:
2415
- return '120px'; // ํ…์ŠคํŠธ๋Š” ์ข€ ๋” ์ž‘๊ฒŒ
2434
+ return '60px'; // ํ…์ŠคํŠธ๋Š” ์ข€ ๋” ์ž‘๊ฒŒ
2416
2435
  case AdType.VIDEO:
2417
2436
  return '360px'; // ๋น„๋””์˜ค๋Š” 16:9 ๋น„์œจ ๊ณ ๋ ค
2418
2437
  case AdType.NATIVE:
@@ -2682,6 +2701,8 @@ class AdsModule {
2682
2701
  autoSlide: options?.autoSlide || false,
2683
2702
  slideInterval: options?.slideInterval || 5000,
2684
2703
  onClick: options?.onClick,
2704
+ // ํŠน์ • ๊ด‘๊ณ  ID
2705
+ adId: options?.adId,
2685
2706
  // ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค ์ „๋‹ฌ
2686
2707
  language: options?.language,
2687
2708
  deviceType: options?.deviceType,
@@ -2698,6 +2719,8 @@ class AdsModule {
2698
2719
  maxLines: options?.maxLines || 3,
2699
2720
  style: options?.style || 'default',
2700
2721
  onClick: options?.onClick,
2722
+ // ํŠน์ • ๊ด‘๊ณ  ID
2723
+ adId: options?.adId,
2701
2724
  // ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค ์ „๋‹ฌ
2702
2725
  language: options?.language,
2703
2726
  deviceType: options?.deviceType,
@@ -2897,8 +2920,8 @@ class AdsModule {
2897
2920
  // ๊ธฐ๋ณธ fraud ๊ฒ€์‚ฌ
2898
2921
  const fraudDetector = new BasicFraudDetector();
2899
2922
  // viewability ์ถ”์ 
2900
- const tracker = new ViewabilityTracker(element, slot.adType, (metrics) => {
2901
- this.handleViewableEvent(ad, slot, metrics, fraudDetector);
2923
+ const tracker = new ViewabilityTracker(element, slot.adType, async (metrics) => {
2924
+ await this.handleViewableEvent(ad, slot, metrics, fraudDetector);
2902
2925
  });
2903
2926
  // ์ •๋ฆฌ๋ฅผ ์œ„ํ•ด ์ €์žฅ
2904
2927
  slot.viewabilityTracker = tracker;
@@ -2952,7 +2975,27 @@ class AdsModule {
2952
2975
  if (!this._config?.apiKey) {
2953
2976
  throw new Error('API key not configured');
2954
2977
  }
2955
- // GET ์š”์ฒญ์šฉ query parameters ๊ตฌ์„ฑ
2978
+ // ํŠน์ • ๊ด‘๊ณ  ID๊ฐ€ ์ง€์ •๋œ ๊ฒฝ์šฐ, ํ•ด๋‹น ๊ด‘๊ณ ๋งŒ ์š”์ฒญ
2979
+ if (options.adId) {
2980
+ const url = `${endpoints.advertisements.detail(options.adId)}`;
2981
+ const response = await fetch(url, {
2982
+ method: 'GET',
2983
+ headers: ApiHeaders.create(this._config.apiKey)
2984
+ });
2985
+ if (!response.ok) {
2986
+ if (response.status === 404) {
2987
+ console.warn(`๐Ÿšซ Advertisement not found with ID: ${options.adId}`);
2988
+ return [];
2989
+ }
2990
+ throw new Error(`Failed to fetch ad data: ${response.status}`);
2991
+ }
2992
+ const advertisement = await response.json();
2993
+ if (this._config?.debug) {
2994
+ console.log(`๐Ÿ“Š Fetched specific ad with ID: ${options.adId}`, advertisement);
2995
+ }
2996
+ return [advertisement];
2997
+ }
2998
+ // ์ผ๋ฐ˜์ ์ธ ๊ด‘๊ณ  ๋ชฉ๋ก ์š”์ฒญ
2956
2999
  const params = new URLSearchParams();
2957
3000
  params.append('adType', type);
2958
3001
  // ๋ฐฑ์—”๋“œ API์—์„œ ์‹ค์ œ ์ง€์›ํ•˜๋Š” ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค๋งŒ ์ถ”๊ฐ€
@@ -3371,21 +3414,22 @@ function AdStageProvider({ children, config }) {
3371
3414
  * AdStage Context Hook
3372
3415
  * AdStageProvider ๋‚ด์—์„œ SDK ์ƒํƒœ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
3373
3416
  */
3374
- function useAdStage() {
3417
+ function useAdStageContext() {
3375
3418
  const context = react.useContext(AdStageContext);
3376
3419
  if (!context) {
3377
- throw new Error('useAdStage must be used within an AdStageProvider');
3420
+ throw new Error('useAdStageContext must be used within an AdStageProvider');
3378
3421
  }
3379
3422
  return context;
3380
3423
  }
3381
3424
  /**
3382
- * AdStage SDK Hook
3425
+ * AdStage Instance Hook
3383
3426
  * ์ดˆ๊ธฐํ™”๋œ AdStage ์ธ์Šคํ„ด์Šค์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
3384
3427
  */
3385
- function useAdStageSDK() {
3386
- const { isInitialized } = useAdStage();
3428
+ function useAdStageInstance() {
3429
+ const { isInitialized } = useAdStageContext();
3387
3430
  if (!isInitialized) {
3388
- throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
3431
+ console.warn('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
3432
+ return null;
3389
3433
  }
3390
3434
  return AdStage;
3391
3435
  }
@@ -3403,4 +3447,5 @@ exports.AdStage = AdStage;
3403
3447
  exports.AdStageProvider = AdStageProvider;
3404
3448
  exports.SDK_VERSION = SDK_VERSION;
3405
3449
  exports.SUPPORTED_MODULES = SUPPORTED_MODULES;
3406
- exports.useAdStage = useAdStageSDK;
3450
+ exports.useAdStageContext = useAdStageContext;
3451
+ exports.useAdStageInstance = useAdStageInstance;
package/dist/index.d.ts CHANGED
@@ -220,6 +220,7 @@ interface AdOptions {
220
220
  autoplay?: boolean;
221
221
  muted?: boolean;
222
222
  onClick?: (adData: any) => void;
223
+ adId?: string;
223
224
  language?: 'ko' | 'en' | 'ja' | 'zh';
224
225
  deviceType?: 'MOBILE' | 'DESKTOP';
225
226
  country?: 'KR' | 'US' | 'JP' | 'CN' | 'DE';
@@ -486,6 +487,13 @@ declare class AdStage {
486
487
  static reset(): void;
487
488
  }
488
489
 
490
+ interface AdStageContextType {
491
+ isInitialized: boolean;
492
+ config: AdStageConfig | null;
493
+ initialize: (config: AdStageConfig) => void;
494
+ reset: () => void;
495
+ error: string | null;
496
+ }
489
497
  interface AdStageProviderProps {
490
498
  children: ReactNode;
491
499
  /**
@@ -496,10 +504,15 @@ interface AdStageProviderProps {
496
504
  }
497
505
  declare function AdStageProvider({ children, config }: AdStageProviderProps): react_jsx_runtime.JSX.Element;
498
506
  /**
499
- * AdStage SDK Hook
507
+ * AdStage Context Hook
508
+ * AdStageProvider ๋‚ด์—์„œ SDK ์ƒํƒœ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
509
+ */
510
+ declare function useAdStageContext(): AdStageContextType;
511
+ /**
512
+ * AdStage Instance Hook
500
513
  * ์ดˆ๊ธฐํ™”๋œ AdStage ์ธ์Šคํ„ด์Šค์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
501
514
  */
502
- declare function useAdStageSDK(): typeof AdStage;
515
+ declare function useAdStageInstance(): typeof AdStage | null;
503
516
 
504
517
  /**
505
518
  * AdStage Web SDK
@@ -509,4 +522,4 @@ declare function useAdStageSDK(): typeof AdStage;
509
522
  declare const SDK_VERSION = "2.0.0";
510
523
  declare const SUPPORTED_MODULES: readonly ["ads", "events", "config"];
511
524
 
512
- export { AdEventType, AdOptions, AdSlot, AdStage, AdStageConfig, AdStageProvider, AdType, Advertisement, ApiResponse, BaseModule, EventProperties, ModuleName, OrganizationInfo, PageData, SDK_VERSION, SUPPORTED_MODULES, useAdStageSDK as useAdStage };
525
+ export { AdEventType, AdOptions, AdSlot, AdStage, AdStageConfig, AdStageProvider, AdType, Advertisement, ApiResponse, BaseModule, EventProperties, ModuleName, OrganizationInfo, PageData, SDK_VERSION, SUPPORTED_MODULES, useAdStageContext, useAdStageInstance };
package/dist/index.esm.js CHANGED
@@ -605,13 +605,32 @@ class AdvertisementEventTracker {
605
605
  riskLevel: additionalData.riskLevel,
606
606
  }),
607
607
  };
608
- await fetch(`${this.baseUrl}/advertisements/events/${adId}/${eventType}`, {
608
+ const url = `${this.baseUrl}/advertisements/events/${adId}/${eventType}`;
609
+ const headers = ApiHeaders.createForEvents(this.apiKey, eventData);
610
+ if (this.debug) {
611
+ console.log(`๐Ÿš€ Sending advertisement event: ${eventType} for ad ${adId}`, {
612
+ url,
613
+ headers,
614
+ eventData
615
+ });
616
+ }
617
+ const response = await fetch(url, {
609
618
  method: 'POST',
610
- headers: ApiHeaders.createForEvents(this.apiKey, eventData),
619
+ headers,
611
620
  body: JSON.stringify(eventData),
612
621
  });
613
622
  if (this.debug) {
614
- console.log(`Tracked advertisement event: ${eventType} for ad ${adId}`, eventData);
623
+ console.log(`๐Ÿ“ก API Response Status: ${response.status} ${response.statusText}`, {
624
+ url,
625
+ ok: response.ok
626
+ });
627
+ }
628
+ if (!response.ok) {
629
+ const errorText = await response.text();
630
+ throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`);
631
+ }
632
+ if (this.debug) {
633
+ console.log(`โœ… Successfully tracked advertisement event: ${eventType} for ad ${adId}`);
615
634
  }
616
635
  }
617
636
  catch (error) {
@@ -946,6 +965,7 @@ const API_PATHS = {
946
965
  /** ๊ด‘๊ณ  ๊ด€๋ จ */
947
966
  advertisements: {
948
967
  list: '/advertisements/list',
968
+ detail: '/advertisements',
949
969
  events: '/advertisements/events'
950
970
  },
951
971
  /** ์ด๋ฒคํŠธ ๊ด€๋ จ */
@@ -964,6 +984,7 @@ class EndpointBuilder {
964
984
  */
965
985
  this.advertisements = {
966
986
  list: () => `${this.baseUrl}${API_PATHS.advertisements.list}`,
987
+ detail: (adId) => `${this.baseUrl}${API_PATHS.advertisements.detail}/${adId}`,
967
988
  events: (adId, eventType) => `${this.baseUrl}${API_PATHS.advertisements.events}/${adId}/${eventType}`
968
989
  };
969
990
  /**
@@ -1204,7 +1225,7 @@ class TextAdRenderer extends BaseAdRenderer {
1204
1225
  // ๊ธฐ๋ณธ ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ
1205
1226
  const containerStyles = {
1206
1227
  ...this.getBaseContainerStyles(slot),
1207
- padding: '20px',
1228
+ padding: '4px',
1208
1229
  background: 'transparent',
1209
1230
  display: 'flex',
1210
1231
  'align-items': 'center',
@@ -1232,7 +1253,7 @@ class TextAdRenderer extends BaseAdRenderer {
1232
1253
  }
1233
1254
  // ํ…์ŠคํŠธ ์ฝ˜ํ…์ธ  ์ƒ์„ฑ
1234
1255
  const textContent = this.createTextElement(ad.textContent, 'div', {
1235
- 'font-size': '16px',
1256
+ 'font-size': '14px',
1236
1257
  'font-weight': '500',
1237
1258
  color: '#212529',
1238
1259
  width: '100%', // ์ „์ฒด ๋„ˆ๋น„ ์‚ฌ์šฉํ•˜์—ฌ ํ…์ŠคํŠธ ์ •๋ ฌ์ด ์ ์šฉ๋˜๋„๋ก ํ•จ
@@ -2283,8 +2304,6 @@ class AdRenderer {
2283
2304
  case AdType.TEXT: {
2284
2305
  const textDiv = document.createElement('div');
2285
2306
  textDiv.innerHTML = `
2286
- <h3>${ad.title}</h3>
2287
- ${ad.description ? `<p>${ad.description}</p>` : ''}
2288
2307
  ${ad.textContent ? `<div>${ad.textContent}</div>` : ''}
2289
2308
  `;
2290
2309
  adElement.appendChild(textDiv);
@@ -2410,7 +2429,7 @@ class AdRenderer {
2410
2429
  case AdType.BANNER:
2411
2430
  return '250px'; // ์ผ๋ฐ˜ ๋ฐฐ๋„ˆ
2412
2431
  case AdType.TEXT:
2413
- return '120px'; // ํ…์ŠคํŠธ๋Š” ์ข€ ๋” ์ž‘๊ฒŒ
2432
+ return '60px'; // ํ…์ŠคํŠธ๋Š” ์ข€ ๋” ์ž‘๊ฒŒ
2414
2433
  case AdType.VIDEO:
2415
2434
  return '360px'; // ๋น„๋””์˜ค๋Š” 16:9 ๋น„์œจ ๊ณ ๋ ค
2416
2435
  case AdType.NATIVE:
@@ -2680,6 +2699,8 @@ class AdsModule {
2680
2699
  autoSlide: options?.autoSlide || false,
2681
2700
  slideInterval: options?.slideInterval || 5000,
2682
2701
  onClick: options?.onClick,
2702
+ // ํŠน์ • ๊ด‘๊ณ  ID
2703
+ adId: options?.adId,
2683
2704
  // ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค ์ „๋‹ฌ
2684
2705
  language: options?.language,
2685
2706
  deviceType: options?.deviceType,
@@ -2696,6 +2717,8 @@ class AdsModule {
2696
2717
  maxLines: options?.maxLines || 3,
2697
2718
  style: options?.style || 'default',
2698
2719
  onClick: options?.onClick,
2720
+ // ํŠน์ • ๊ด‘๊ณ  ID
2721
+ adId: options?.adId,
2699
2722
  // ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค ์ „๋‹ฌ
2700
2723
  language: options?.language,
2701
2724
  deviceType: options?.deviceType,
@@ -2895,8 +2918,8 @@ class AdsModule {
2895
2918
  // ๊ธฐ๋ณธ fraud ๊ฒ€์‚ฌ
2896
2919
  const fraudDetector = new BasicFraudDetector();
2897
2920
  // viewability ์ถ”์ 
2898
- const tracker = new ViewabilityTracker(element, slot.adType, (metrics) => {
2899
- this.handleViewableEvent(ad, slot, metrics, fraudDetector);
2921
+ const tracker = new ViewabilityTracker(element, slot.adType, async (metrics) => {
2922
+ await this.handleViewableEvent(ad, slot, metrics, fraudDetector);
2900
2923
  });
2901
2924
  // ์ •๋ฆฌ๋ฅผ ์œ„ํ•ด ์ €์žฅ
2902
2925
  slot.viewabilityTracker = tracker;
@@ -2950,7 +2973,27 @@ class AdsModule {
2950
2973
  if (!this._config?.apiKey) {
2951
2974
  throw new Error('API key not configured');
2952
2975
  }
2953
- // GET ์š”์ฒญ์šฉ query parameters ๊ตฌ์„ฑ
2976
+ // ํŠน์ • ๊ด‘๊ณ  ID๊ฐ€ ์ง€์ •๋œ ๊ฒฝ์šฐ, ํ•ด๋‹น ๊ด‘๊ณ ๋งŒ ์š”์ฒญ
2977
+ if (options.adId) {
2978
+ const url = `${endpoints.advertisements.detail(options.adId)}`;
2979
+ const response = await fetch(url, {
2980
+ method: 'GET',
2981
+ headers: ApiHeaders.create(this._config.apiKey)
2982
+ });
2983
+ if (!response.ok) {
2984
+ if (response.status === 404) {
2985
+ console.warn(`๐Ÿšซ Advertisement not found with ID: ${options.adId}`);
2986
+ return [];
2987
+ }
2988
+ throw new Error(`Failed to fetch ad data: ${response.status}`);
2989
+ }
2990
+ const advertisement = await response.json();
2991
+ if (this._config?.debug) {
2992
+ console.log(`๐Ÿ“Š Fetched specific ad with ID: ${options.adId}`, advertisement);
2993
+ }
2994
+ return [advertisement];
2995
+ }
2996
+ // ์ผ๋ฐ˜์ ์ธ ๊ด‘๊ณ  ๋ชฉ๋ก ์š”์ฒญ
2954
2997
  const params = new URLSearchParams();
2955
2998
  params.append('adType', type);
2956
2999
  // ๋ฐฑ์—”๋“œ API์—์„œ ์‹ค์ œ ์ง€์›ํ•˜๋Š” ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค๋งŒ ์ถ”๊ฐ€
@@ -3369,21 +3412,22 @@ function AdStageProvider({ children, config }) {
3369
3412
  * AdStage Context Hook
3370
3413
  * AdStageProvider ๋‚ด์—์„œ SDK ์ƒํƒœ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
3371
3414
  */
3372
- function useAdStage() {
3415
+ function useAdStageContext() {
3373
3416
  const context = useContext(AdStageContext);
3374
3417
  if (!context) {
3375
- throw new Error('useAdStage must be used within an AdStageProvider');
3418
+ throw new Error('useAdStageContext must be used within an AdStageProvider');
3376
3419
  }
3377
3420
  return context;
3378
3421
  }
3379
3422
  /**
3380
- * AdStage SDK Hook
3423
+ * AdStage Instance Hook
3381
3424
  * ์ดˆ๊ธฐํ™”๋œ AdStage ์ธ์Šคํ„ด์Šค์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
3382
3425
  */
3383
- function useAdStageSDK() {
3384
- const { isInitialized } = useAdStage();
3426
+ function useAdStageInstance() {
3427
+ const { isInitialized } = useAdStageContext();
3385
3428
  if (!isInitialized) {
3386
- throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
3429
+ console.warn('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
3430
+ return null;
3387
3431
  }
3388
3432
  return AdStage;
3389
3433
  }
@@ -3397,4 +3441,4 @@ function useAdStageSDK() {
3397
3441
  const SDK_VERSION = '2.0.0';
3398
3442
  const SUPPORTED_MODULES = ['ads', 'events', 'config'];
3399
3443
 
3400
- export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStageSDK as useAdStage };
3444
+ export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStageContext, useAdStageInstance };
@@ -602,13 +602,32 @@ class AdvertisementEventTracker {
602
602
  riskLevel: additionalData.riskLevel,
603
603
  }),
604
604
  };
605
- await fetch(`${this.baseUrl}/advertisements/events/${adId}/${eventType}`, {
605
+ const url = `${this.baseUrl}/advertisements/events/${adId}/${eventType}`;
606
+ const headers = ApiHeaders.createForEvents(this.apiKey, eventData);
607
+ if (this.debug) {
608
+ console.log(`๐Ÿš€ Sending advertisement event: ${eventType} for ad ${adId}`, {
609
+ url,
610
+ headers,
611
+ eventData
612
+ });
613
+ }
614
+ const response = await fetch(url, {
606
615
  method: 'POST',
607
- headers: ApiHeaders.createForEvents(this.apiKey, eventData),
616
+ headers,
608
617
  body: JSON.stringify(eventData),
609
618
  });
610
619
  if (this.debug) {
611
- console.log(`Tracked advertisement event: ${eventType} for ad ${adId}`, eventData);
620
+ console.log(`๐Ÿ“ก API Response Status: ${response.status} ${response.statusText}`, {
621
+ url,
622
+ ok: response.ok
623
+ });
624
+ }
625
+ if (!response.ok) {
626
+ const errorText = await response.text();
627
+ throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`);
628
+ }
629
+ if (this.debug) {
630
+ console.log(`โœ… Successfully tracked advertisement event: ${eventType} for ad ${adId}`);
612
631
  }
613
632
  }
614
633
  catch (error) {
@@ -943,6 +962,7 @@ const API_PATHS = {
943
962
  /** ๊ด‘๊ณ  ๊ด€๋ จ */
944
963
  advertisements: {
945
964
  list: '/advertisements/list',
965
+ detail: '/advertisements',
946
966
  events: '/advertisements/events'
947
967
  },
948
968
  /** ์ด๋ฒคํŠธ ๊ด€๋ จ */
@@ -961,6 +981,7 @@ class EndpointBuilder {
961
981
  */
962
982
  this.advertisements = {
963
983
  list: () => `${this.baseUrl}${API_PATHS.advertisements.list}`,
984
+ detail: (adId) => `${this.baseUrl}${API_PATHS.advertisements.detail}/${adId}`,
964
985
  events: (adId, eventType) => `${this.baseUrl}${API_PATHS.advertisements.events}/${adId}/${eventType}`
965
986
  };
966
987
  /**
@@ -1201,7 +1222,7 @@ class TextAdRenderer extends BaseAdRenderer {
1201
1222
  // ๊ธฐ๋ณธ ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ
1202
1223
  const containerStyles = {
1203
1224
  ...this.getBaseContainerStyles(slot),
1204
- padding: '20px',
1225
+ padding: '4px',
1205
1226
  background: 'transparent',
1206
1227
  display: 'flex',
1207
1228
  'align-items': 'center',
@@ -1229,7 +1250,7 @@ class TextAdRenderer extends BaseAdRenderer {
1229
1250
  }
1230
1251
  // ํ…์ŠคํŠธ ์ฝ˜ํ…์ธ  ์ƒ์„ฑ
1231
1252
  const textContent = this.createTextElement(ad.textContent, 'div', {
1232
- 'font-size': '16px',
1253
+ 'font-size': '14px',
1233
1254
  'font-weight': '500',
1234
1255
  color: '#212529',
1235
1256
  width: '100%', // ์ „์ฒด ๋„ˆ๋น„ ์‚ฌ์šฉํ•˜์—ฌ ํ…์ŠคํŠธ ์ •๋ ฌ์ด ์ ์šฉ๋˜๋„๋ก ํ•จ
@@ -2280,8 +2301,6 @@ class AdRenderer {
2280
2301
  case AdType.TEXT: {
2281
2302
  const textDiv = document.createElement('div');
2282
2303
  textDiv.innerHTML = `
2283
- <h3>${ad.title}</h3>
2284
- ${ad.description ? `<p>${ad.description}</p>` : ''}
2285
2304
  ${ad.textContent ? `<div>${ad.textContent}</div>` : ''}
2286
2305
  `;
2287
2306
  adElement.appendChild(textDiv);
@@ -2407,7 +2426,7 @@ class AdRenderer {
2407
2426
  case AdType.BANNER:
2408
2427
  return '250px'; // ์ผ๋ฐ˜ ๋ฐฐ๋„ˆ
2409
2428
  case AdType.TEXT:
2410
- return '120px'; // ํ…์ŠคํŠธ๋Š” ์ข€ ๋” ์ž‘๊ฒŒ
2429
+ return '60px'; // ํ…์ŠคํŠธ๋Š” ์ข€ ๋” ์ž‘๊ฒŒ
2411
2430
  case AdType.VIDEO:
2412
2431
  return '360px'; // ๋น„๋””์˜ค๋Š” 16:9 ๋น„์œจ ๊ณ ๋ ค
2413
2432
  case AdType.NATIVE:
@@ -2677,6 +2696,8 @@ class AdsModule {
2677
2696
  autoSlide: options?.autoSlide || false,
2678
2697
  slideInterval: options?.slideInterval || 5000,
2679
2698
  onClick: options?.onClick,
2699
+ // ํŠน์ • ๊ด‘๊ณ  ID
2700
+ adId: options?.adId,
2680
2701
  // ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค ์ „๋‹ฌ
2681
2702
  language: options?.language,
2682
2703
  deviceType: options?.deviceType,
@@ -2693,6 +2714,8 @@ class AdsModule {
2693
2714
  maxLines: options?.maxLines || 3,
2694
2715
  style: options?.style || 'default',
2695
2716
  onClick: options?.onClick,
2717
+ // ํŠน์ • ๊ด‘๊ณ  ID
2718
+ adId: options?.adId,
2696
2719
  // ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค ์ „๋‹ฌ
2697
2720
  language: options?.language,
2698
2721
  deviceType: options?.deviceType,
@@ -2892,8 +2915,8 @@ class AdsModule {
2892
2915
  // ๊ธฐ๋ณธ fraud ๊ฒ€์‚ฌ
2893
2916
  const fraudDetector = new BasicFraudDetector();
2894
2917
  // viewability ์ถ”์ 
2895
- const tracker = new ViewabilityTracker(element, slot.adType, (metrics) => {
2896
- this.handleViewableEvent(ad, slot, metrics, fraudDetector);
2918
+ const tracker = new ViewabilityTracker(element, slot.adType, async (metrics) => {
2919
+ await this.handleViewableEvent(ad, slot, metrics, fraudDetector);
2897
2920
  });
2898
2921
  // ์ •๋ฆฌ๋ฅผ ์œ„ํ•ด ์ €์žฅ
2899
2922
  slot.viewabilityTracker = tracker;
@@ -2947,7 +2970,27 @@ class AdsModule {
2947
2970
  if (!this._config?.apiKey) {
2948
2971
  throw new Error('API key not configured');
2949
2972
  }
2950
- // GET ์š”์ฒญ์šฉ query parameters ๊ตฌ์„ฑ
2973
+ // ํŠน์ • ๊ด‘๊ณ  ID๊ฐ€ ์ง€์ •๋œ ๊ฒฝ์šฐ, ํ•ด๋‹น ๊ด‘๊ณ ๋งŒ ์š”์ฒญ
2974
+ if (options.adId) {
2975
+ const url = `${endpoints.advertisements.detail(options.adId)}`;
2976
+ const response = await fetch(url, {
2977
+ method: 'GET',
2978
+ headers: ApiHeaders.create(this._config.apiKey)
2979
+ });
2980
+ if (!response.ok) {
2981
+ if (response.status === 404) {
2982
+ console.warn(`๐Ÿšซ Advertisement not found with ID: ${options.adId}`);
2983
+ return [];
2984
+ }
2985
+ throw new Error(`Failed to fetch ad data: ${response.status}`);
2986
+ }
2987
+ const advertisement = await response.json();
2988
+ if (this._config?.debug) {
2989
+ console.log(`๐Ÿ“Š Fetched specific ad with ID: ${options.adId}`, advertisement);
2990
+ }
2991
+ return [advertisement];
2992
+ }
2993
+ // ์ผ๋ฐ˜์ ์ธ ๊ด‘๊ณ  ๋ชฉ๋ก ์š”์ฒญ
2951
2994
  const params = new URLSearchParams();
2952
2995
  params.append('adType', type);
2953
2996
  // ๋ฐฑ์—”๋“œ API์—์„œ ์‹ค์ œ ์ง€์›ํ•˜๋Š” ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค๋งŒ ์ถ”๊ฐ€
@@ -3441,21 +3484,22 @@ function AdStageProvider({ children, config }) {
3441
3484
  * AdStage Context Hook
3442
3485
  * AdStageProvider ๋‚ด์—์„œ SDK ์ƒํƒœ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
3443
3486
  */
3444
- function useAdStage() {
3487
+ function useAdStageContext() {
3445
3488
  const context = reactExports.useContext(AdStageContext);
3446
3489
  if (!context) {
3447
- throw new Error('useAdStage must be used within an AdStageProvider');
3490
+ throw new Error('useAdStageContext must be used within an AdStageProvider');
3448
3491
  }
3449
3492
  return context;
3450
3493
  }
3451
3494
  /**
3452
- * AdStage SDK Hook
3495
+ * AdStage Instance Hook
3453
3496
  * ์ดˆ๊ธฐํ™”๋œ AdStage ์ธ์Šคํ„ด์Šค์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
3454
3497
  */
3455
- function useAdStageSDK() {
3456
- const { isInitialized } = useAdStage();
3498
+ function useAdStageInstance() {
3499
+ const { isInitialized } = useAdStageContext();
3457
3500
  if (!isInitialized) {
3458
- throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
3501
+ console.warn('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
3502
+ return null;
3459
3503
  }
3460
3504
  return AdStage;
3461
3505
  }
@@ -3469,4 +3513,4 @@ function useAdStageSDK() {
3469
3513
  const SDK_VERSION = '2.0.0';
3470
3514
  const SUPPORTED_MODULES = ['ads', 'events', 'config'];
3471
3515
 
3472
- export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStageSDK as useAdStage };
3516
+ export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStageContext, useAdStageInstance };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adstage/web-sdk",
3
- "version": "2.4.2",
3
+ "version": "2.4.5",
4
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",
@@ -26,6 +26,7 @@ export const API_PATHS = {
26
26
  /** ๊ด‘๊ณ  ๊ด€๋ จ */
27
27
  advertisements: {
28
28
  list: '/advertisements/list',
29
+ detail: '/advertisements',
29
30
  events: '/advertisements/events'
30
31
  },
31
32
 
@@ -67,6 +68,7 @@ export class EndpointBuilder {
67
68
  */
68
69
  advertisements = {
69
70
  list: () => `${this.baseUrl}${API_PATHS.advertisements.list}`,
71
+ detail: (adId: string) => `${this.baseUrl}${API_PATHS.advertisements.detail}/${adId}`,
70
72
  events: (adId: string, eventType: string) =>
71
73
  `${this.baseUrl}${API_PATHS.advertisements.events}/${adId}/${eventType}`
72
74
  };
package/src/index.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  export { default as AdStage } from './core/AdStage';
8
8
 
9
9
  // React ํ†ตํ•ฉ
10
- export { AdStageProvider, useAdStage } from './react';
10
+ export { AdStageProvider, useAdStageContext, useAdStageInstance } from './react';
11
11
 
12
12
  // ์„ค์ • ๋ฐ ํƒ€์ž…
13
13
  export type { AdStageConfig, ModuleName, BaseModule, ApiResponse, OrganizationInfo } from './types/config';
@@ -107,17 +107,37 @@ export class AdvertisementEventTracker {
107
107
  };
108
108
 
109
109
 
110
- await fetch(
111
- `${this.baseUrl}/advertisements/events/${adId}/${eventType}`,
112
- {
113
- method: 'POST',
114
- headers: ApiHeaders.createForEvents(this.apiKey, eventData),
115
- body: JSON.stringify(eventData),
116
- }
117
- );
110
+ const url = `${this.baseUrl}/advertisements/events/${adId}/${eventType}`;
111
+ const headers = ApiHeaders.createForEvents(this.apiKey, eventData);
112
+
113
+ if (this.debug) {
114
+ console.log(`๐Ÿš€ Sending advertisement event: ${eventType} for ad ${adId}`, {
115
+ url,
116
+ headers,
117
+ eventData
118
+ });
119
+ }
120
+
121
+ const response = await fetch(url, {
122
+ method: 'POST',
123
+ headers,
124
+ body: JSON.stringify(eventData),
125
+ });
126
+
127
+ if (this.debug) {
128
+ console.log(`๐Ÿ“ก API Response Status: ${response.status} ${response.statusText}`, {
129
+ url,
130
+ ok: response.ok
131
+ });
132
+ }
133
+
134
+ if (!response.ok) {
135
+ const errorText = await response.text();
136
+ throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`);
137
+ }
118
138
 
119
139
  if (this.debug) {
120
- console.log(`Tracked advertisement event: ${eventType} for ad ${adId}`, eventData);
140
+ console.log(`โœ… Successfully tracked advertisement event: ${eventType} for ad ${adId}`);
121
141
  }
122
142
  } catch (error) {
123
143
  console.error('Failed to track advertisement event:', error);
@@ -345,8 +345,6 @@ export class AdRenderer {
345
345
  case AdType.TEXT: {
346
346
  const textDiv = document.createElement('div');
347
347
  textDiv.innerHTML = `
348
- <h3>${ad.title}</h3>
349
- ${ad.description ? `<p>${ad.description}</p>` : ''}
350
348
  ${ad.textContent ? `<div>${ad.textContent}</div>` : ''}
351
349
  `;
352
350
  adElement.appendChild(textDiv);
@@ -480,7 +478,7 @@ export class AdRenderer {
480
478
  case AdType.BANNER:
481
479
  return '250px'; // ์ผ๋ฐ˜ ๋ฐฐ๋„ˆ
482
480
  case AdType.TEXT:
483
- return '120px'; // ํ…์ŠคํŠธ๋Š” ์ข€ ๋” ์ž‘๊ฒŒ
481
+ return '60px'; // ํ…์ŠคํŠธ๋Š” ์ข€ ๋” ์ž‘๊ฒŒ
484
482
  case AdType.VIDEO:
485
483
  return '360px'; // ๋น„๋””์˜ค๋Š” 16:9 ๋น„์œจ ๊ณ ๋ ค
486
484
  case AdType.NATIVE:
@@ -26,6 +26,8 @@ export interface AdOptions {
26
26
  autoplay?: boolean;
27
27
  muted?: boolean;
28
28
  onClick?: (adData: any) => void;
29
+ // ํŠน์ • ๊ด‘๊ณ  ID ์ง€์ •
30
+ adId?: string;
29
31
  // ๊ด‘๊ณ  ํ•„ํ„ฐ๋ง ์˜ต์…˜ (๋ฐฑ์—”๋“œ API์—์„œ ์‹ค์ œ ์ง€์›ํ•˜๋Š” ๊ฒƒ๋“ค๋งŒ)
30
32
  language?: 'ko' | 'en' | 'ja' | 'zh';
31
33
  deviceType?: 'MOBILE' | 'DESKTOP';
@@ -91,6 +93,8 @@ export class AdsModule implements BaseModule {
91
93
  autoSlide: options?.autoSlide || false,
92
94
  slideInterval: options?.slideInterval || 5000,
93
95
  onClick: options?.onClick,
96
+ // ํŠน์ • ๊ด‘๊ณ  ID
97
+ adId: options?.adId,
94
98
  // ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค ์ „๋‹ฌ
95
99
  language: options?.language,
96
100
  deviceType: options?.deviceType,
@@ -110,6 +114,8 @@ export class AdsModule implements BaseModule {
110
114
  maxLines: options?.maxLines || 3,
111
115
  style: options?.style || 'default',
112
116
  onClick: options?.onClick,
117
+ // ํŠน์ • ๊ด‘๊ณ  ID
118
+ adId: options?.adId,
113
119
  // ํ•„ํ„ฐ๋ง ์˜ต์…˜๋“ค ์ „๋‹ฌ
114
120
  language: options?.language,
115
121
  deviceType: options?.deviceType,
@@ -351,8 +357,8 @@ export class AdsModule implements BaseModule {
351
357
  const fraudDetector = new BasicFraudDetector();
352
358
 
353
359
  // viewability ์ถ”์ 
354
- const tracker = new ViewabilityTracker(element, slot.adType, (metrics) => {
355
- this.handleViewableEvent(ad, slot, metrics, fraudDetector);
360
+ const tracker = new ViewabilityTracker(element, slot.adType, async (metrics) => {
361
+ await this.handleViewableEvent(ad, slot, metrics, fraudDetector);
356
362
  });
357
363
 
358
364
  // ์ •๋ฆฌ๋ฅผ ์œ„ํ•ด ์ €์žฅ
@@ -426,7 +432,33 @@ export class AdsModule implements BaseModule {
426
432
  throw new Error('API key not configured');
427
433
  }
428
434
 
429
- // GET ์š”์ฒญ์šฉ query parameters ๊ตฌ์„ฑ
435
+ // ํŠน์ • ๊ด‘๊ณ  ID๊ฐ€ ์ง€์ •๋œ ๊ฒฝ์šฐ, ํ•ด๋‹น ๊ด‘๊ณ ๋งŒ ์š”์ฒญ
436
+ if (options.adId) {
437
+ const url = `${endpoints.advertisements.detail(options.adId)}`;
438
+
439
+ const response = await fetch(url, {
440
+ method: 'GET',
441
+ headers: ApiHeaders.create(this._config.apiKey)
442
+ });
443
+
444
+ if (!response.ok) {
445
+ if (response.status === 404) {
446
+ console.warn(`๐Ÿšซ Advertisement not found with ID: ${options.adId}`);
447
+ return [];
448
+ }
449
+ throw new Error(`Failed to fetch ad data: ${response.status}`);
450
+ }
451
+
452
+ const advertisement = await response.json();
453
+
454
+ if (this._config?.debug) {
455
+ console.log(`๐Ÿ“Š Fetched specific ad with ID: ${options.adId}`, advertisement);
456
+ }
457
+
458
+ return [advertisement];
459
+ }
460
+
461
+ // ์ผ๋ฐ˜์ ์ธ ๊ด‘๊ณ  ๋ชฉ๋ก ์š”์ฒญ
430
462
  const params = new URLSearchParams();
431
463
  params.append('adType', type);
432
464
 
@@ -94,23 +94,24 @@ export function AdStageProvider({ children, config }: AdStageProviderProps) {
94
94
  * AdStage Context Hook
95
95
  * AdStageProvider ๋‚ด์—์„œ SDK ์ƒํƒœ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
96
96
  */
97
- export function useAdStage(): AdStageContextType {
97
+ export function useAdStageContext(): AdStageContextType {
98
98
  const context = useContext(AdStageContext);
99
99
  if (!context) {
100
- throw new Error('useAdStage must be used within an AdStageProvider');
100
+ throw new Error('useAdStageContext must be used within an AdStageProvider');
101
101
  }
102
102
  return context;
103
103
  }
104
104
 
105
105
  /**
106
- * AdStage SDK Hook
106
+ * AdStage Instance Hook
107
107
  * ์ดˆ๊ธฐํ™”๋œ AdStage ์ธ์Šคํ„ด์Šค์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
108
108
  */
109
- export function useAdStageSDK() {
110
- const { isInitialized } = useAdStage();
109
+ export function useAdStageInstance() {
110
+ const { isInitialized } = useAdStageContext();
111
111
 
112
112
  if (!isInitialized) {
113
- throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
113
+ console.warn('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
114
+ return null;
114
115
  }
115
116
 
116
117
  return AdStage;
@@ -3,7 +3,7 @@
3
3
  * React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ์ปดํฌ๋„ŒํŠธ์™€ ํ›…๋“ค
4
4
  */
5
5
 
6
- export { AdStageProvider, useAdStage as useAdStageContext, useAdStageSDK as useAdStage } from './AdStageProvider';
6
+ export { AdStageProvider, useAdStageContext, useAdStageInstance } from './AdStageProvider';
7
7
 
8
8
  // ํƒ€์ž…๋“ค๋„ ์žฌexport
9
9
  export type { AdStageConfig, ModuleName, BaseModule } from '../types/config';
@@ -16,7 +16,7 @@ export class TextAdRenderer extends BaseAdRenderer {
16
16
  // ๊ธฐ๋ณธ ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ
17
17
  const containerStyles: Record<string, string> = {
18
18
  ...this.getBaseContainerStyles(slot),
19
- padding: '20px',
19
+ padding: '4px',
20
20
  background: 'transparent',
21
21
  display: 'flex',
22
22
  'align-items': 'center',
@@ -52,7 +52,7 @@ export class TextAdRenderer extends BaseAdRenderer {
52
52
  ad.textContent,
53
53
  'div',
54
54
  {
55
- 'font-size': '16px',
55
+ 'font-size': '14px',
56
56
  'font-weight': '500',
57
57
  color: '#212529',
58
58
  width: '100%', // ์ „์ฒด ๋„ˆ๋น„ ์‚ฌ์šฉํ•˜์—ฌ ํ…์ŠคํŠธ ์ •๋ ฌ์ด ์ ์šฉ๋˜๋„๋ก ํ•จ