@adstage/web-sdk 1.1.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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/index.cjs.js +2304 -0
  4. package/dist/index.d.ts +416 -0
  5. package/dist/index.esm.js +2288 -0
  6. package/dist/index.standalone.js +2331 -0
  7. package/examples/README.md +33 -0
  8. package/examples/banner-ads.html +512 -0
  9. package/examples/index.html +338 -0
  10. package/examples/native-ads.html +634 -0
  11. package/examples/react-app/README.md +70 -0
  12. package/examples/react-app/index.html +13 -0
  13. package/examples/react-app/package-lock.json +3042 -0
  14. package/examples/react-app/package.json +26 -0
  15. package/examples/react-app/pnpm-lock.yaml +1857 -0
  16. package/examples/react-app/public/index.standalone.js +2331 -0
  17. package/examples/react-app/src/App.tsx +226 -0
  18. package/examples/react-app/src/index.css +37 -0
  19. package/examples/react-app/src/main.tsx +10 -0
  20. package/examples/react-app/tsconfig.json +25 -0
  21. package/examples/react-app/tsconfig.node.json +10 -0
  22. package/examples/react-app/vite.config.ts +15 -0
  23. package/examples/react-nextjs/app/globals.css +200 -0
  24. package/examples/react-nextjs/app/layout.tsx +27 -0
  25. package/examples/react-nextjs/app/page.tsx +258 -0
  26. package/examples/react-nextjs/next.config.js +9 -0
  27. package/examples/react-nextjs/package.json +22 -0
  28. package/examples/react-nextjs/pnpm-lock.yaml +343 -0
  29. package/examples/react-nextjs/tsconfig.json +34 -0
  30. package/examples/text-ads.html +597 -0
  31. package/examples/video-ads.html +739 -0
  32. package/package.json +83 -0
  33. package/src/global.d.ts +20 -0
  34. package/src/index.ts +350 -0
  35. package/src/managers/device-info-collector.ts +127 -0
  36. package/src/managers/event-tracker.ts +131 -0
  37. package/src/managers/fade-slider-manager.ts +276 -0
  38. package/src/managers/impression-tracker.ts +88 -0
  39. package/src/managers/slider-manager.ts +405 -0
  40. package/src/react/components/AdErrorBoundary.tsx +75 -0
  41. package/src/react/components/AdSlot.tsx +144 -0
  42. package/src/react/components/BannerAd.tsx +24 -0
  43. package/src/react/components/InterstitialAd.tsx +24 -0
  44. package/src/react/components/NativeAd.tsx +24 -0
  45. package/src/react/components/TextAd.tsx +24 -0
  46. package/src/react/components/VideoAd.tsx +24 -0
  47. package/src/react/components/index.ts +8 -0
  48. package/src/react/hooks/index.ts +4 -0
  49. package/src/react/hooks/useAdSlot.ts +83 -0
  50. package/src/react/hooks/useAdStage.ts +14 -0
  51. package/src/react/hooks/useAdTracking.ts +61 -0
  52. package/src/react/index.ts +4 -0
  53. package/src/react/providers/AdStageProvider.tsx +86 -0
  54. package/src/react/providers/index.ts +2 -0
  55. package/src/renderers/banner-renderer.ts +35 -0
  56. package/src/renderers/base-renderer.ts +207 -0
  57. package/src/renderers/index.ts +71 -0
  58. package/src/renderers/interstitial-renderer.ts +70 -0
  59. package/src/renderers/native-renderer.ts +35 -0
  60. package/src/renderers/text-renderer.ts +94 -0
  61. package/src/renderers/video-renderer.ts +63 -0
  62. package/src/types/advertisement.ts +197 -0
  63. package/src/types/api.ts +173 -0
  64. package/src/types/config.ts +174 -0
  65. package/src/types/events.ts +60 -0
  66. package/src/types/index.ts +6 -0
  67. package/src/utils/dom-utils.ts +237 -0
  68. package/src/utils/sdk-utils.ts +134 -0
@@ -0,0 +1,33 @@
1
+ # API Key 설정 가이드
2
+
3
+ 이 예제들을 실행하기 전에 API 키를 설정해야 합니다.
4
+
5
+ ## React App 예제
6
+ `sdk/examples/react-app/.env` 파일의 API 키를 설정하세요:
7
+ ```
8
+ REACT_APP_ADSTAGE_API_KEY=your_api_key_here
9
+ ```
10
+
11
+ ## Next.js 예제
12
+ `sdk/examples/react-nextjs/.env` 파일의 API 키를 설정하세요:
13
+ ```
14
+ NEXT_PUBLIC_ADSTAGE_API_KEY=your_api_key_here
15
+ ```
16
+
17
+ ## 실행 방법
18
+
19
+ ### React App 예제
20
+ ```bash
21
+ cd sdk/examples/react-app
22
+ pnpm install
23
+ pnpm start
24
+ ```
25
+
26
+ ### Next.js 예제
27
+ ```bash
28
+ cd sdk/examples/react-nextjs
29
+ pnpm install
30
+ pnpm dev
31
+ ```
32
+
33
+ 보안상 .env 파일은 Git에 커밋되지 않습니다. 실제 API 키는 AdStage 대시보드에서 발급받으세요.
@@ -0,0 +1,512 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>배너 광고 예제 - AdStage SDK</title>
7
+ <style>
8
+ * {
9
+ box-sizing: border-box;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
16
+ line-height: 1.6;
17
+ color: #333;
18
+ background: #f8f9fa;
19
+ padding: 20px;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1200px;
24
+ margin: 0 auto;
25
+ background: white;
26
+ border-radius: 12px;
27
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
28
+ overflow: hidden;
29
+ }
30
+
31
+ .header {
32
+ background: linear-gradient(135deg, #667eea, #764ba2);
33
+ color: white;
34
+ padding: 30px;
35
+ text-align: center;
36
+ }
37
+
38
+ .header h1 {
39
+ font-size: 2rem;
40
+ margin-bottom: 10px;
41
+ }
42
+
43
+ .header p {
44
+ opacity: 0.9;
45
+ }
46
+
47
+ .nav {
48
+ background: #f8f9fa;
49
+ padding: 15px 30px;
50
+ border-bottom: 1px solid #dee2e6;
51
+ }
52
+
53
+ .back-link {
54
+ color: #667eea;
55
+ text-decoration: none;
56
+ font-weight: 500;
57
+ }
58
+
59
+ .back-link:hover {
60
+ text-decoration: underline;
61
+ }
62
+
63
+ .content {
64
+ padding: 30px;
65
+ }
66
+
67
+ .section {
68
+ margin-bottom: 40px;
69
+ }
70
+
71
+ .section h2 {
72
+ color: #495057;
73
+ margin-bottom: 20px;
74
+ padding-bottom: 10px;
75
+ border-bottom: 2px solid #667eea;
76
+ }
77
+
78
+ .ad-grid {
79
+ display: grid;
80
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
81
+ gap: 30px;
82
+ margin-bottom: 30px;
83
+ }
84
+
85
+ .ad-container {
86
+ border: 2px solid #e9ecef;
87
+ border-radius: 12px;
88
+ padding: 20px;
89
+ background: #f8f9fa;
90
+ transition: all 0.3s ease;
91
+ }
92
+
93
+ .ad-container:hover {
94
+ border-color: #667eea;
95
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
96
+ }
97
+
98
+ .ad-container h3 {
99
+ margin-bottom: 15px;
100
+ color: #667eea;
101
+ font-size: 1.1rem;
102
+ }
103
+
104
+ .ad-slot {
105
+ background: white;
106
+ border: 2px dashed #dee2e6;
107
+ border-radius: 8px;
108
+ padding: 20px;
109
+ text-align: center;
110
+ min-height: 120px;
111
+ display: flex;
112
+ align-items: center;
113
+ justify-content: center;
114
+ position: relative;
115
+ transition: all 0.3s ease;
116
+ }
117
+
118
+ .ad-slot:hover {
119
+ border-color: #667eea;
120
+ background: #f8f9ff;
121
+ }
122
+
123
+ .ad-slot.loading::before {
124
+ content: "📡 광고 로딩 중...";
125
+ color: #6c757d;
126
+ font-size: 0.9rem;
127
+ }
128
+
129
+ .ad-slot.loaded::before {
130
+ content: "✅ 광고 로딩 완료";
131
+ color: #28a745;
132
+ font-size: 0.9rem;
133
+ }
134
+
135
+ .ad-slot.error::before {
136
+ content: "❌ 광고 로딩 실패";
137
+ color: #dc3545;
138
+ font-size: 0.9rem;
139
+ }
140
+
141
+ .ad-info {
142
+ margin-top: 15px;
143
+ padding: 10px;
144
+ background: #e9ecef;
145
+ border-radius: 6px;
146
+ font-size: 0.9rem;
147
+ color: #6c757d;
148
+ }
149
+
150
+ .controls {
151
+ display: flex;
152
+ gap: 10px;
153
+ margin-top: 15px;
154
+ flex-wrap: wrap;
155
+ }
156
+
157
+ .btn {
158
+ padding: 8px 16px;
159
+ border: none;
160
+ border-radius: 6px;
161
+ background: #667eea;
162
+ color: white;
163
+ cursor: pointer;
164
+ font-size: 0.9rem;
165
+ transition: background 0.3s ease;
166
+ }
167
+
168
+ .btn:hover {
169
+ background: #5a67d8;
170
+ }
171
+
172
+ .btn.secondary {
173
+ background: #6c757d;
174
+ }
175
+
176
+ .btn.secondary:hover {
177
+ background: #5a6268;
178
+ }
179
+
180
+ .status {
181
+ margin-top: 30px;
182
+ padding: 20px;
183
+ background: #e7f3ff;
184
+ border-left: 4px solid #0066cc;
185
+ border-radius: 4px;
186
+ }
187
+
188
+ .code-example {
189
+ background: #2d3748;
190
+ color: #e2e8f0;
191
+ padding: 20px;
192
+ border-radius: 8px;
193
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
194
+ font-size: 0.9rem;
195
+ overflow-x: auto;
196
+ margin: 15px 0;
197
+ }
198
+
199
+ .large-banner {
200
+ min-height: 250px;
201
+ }
202
+
203
+ .medium-banner {
204
+ min-height: 200px;
205
+ }
206
+
207
+ .small-banner {
208
+ min-height: 120px;
209
+ }
210
+
211
+ .log {
212
+ background: #f8f9fa;
213
+ border: 1px solid #dee2e6;
214
+ border-radius: 6px;
215
+ padding: 15px;
216
+ max-height: 200px;
217
+ overflow-y: auto;
218
+ font-family: monospace;
219
+ font-size: 0.8rem;
220
+ margin-top: 15px;
221
+ }
222
+
223
+ .log-entry {
224
+ margin-bottom: 5px;
225
+ padding: 2px 5px;
226
+ border-radius: 3px;
227
+ }
228
+
229
+ .log-entry.info {
230
+ background: #d4edda;
231
+ color: #155724;
232
+ }
233
+
234
+ .log-entry.warn {
235
+ background: #fff3cd;
236
+ color: #856404;
237
+ }
238
+
239
+ .log-entry.error {
240
+ background: #f8d7da;
241
+ color: #721c24;
242
+ }
243
+ </style>
244
+ </head>
245
+ <body>
246
+ <div class="container">
247
+ <div class="header">
248
+ <h1>🖼️ 배너 광고 예제</h1>
249
+ <p>다양한 크기와 형태의 배너 광고 표시</p>
250
+ </div>
251
+
252
+ <div class="nav">
253
+ <a href="index.html" class="back-link">← 메인 페이지로 돌아가기</a>
254
+ </div>
255
+
256
+ <div class="content">
257
+ <div class="section">
258
+ <h2>🎯 라이브 배너 광고</h2>
259
+
260
+ <div class="ad-grid">
261
+ <div class="ad-container">
262
+ <h3>대형 배너 (728x90)</h3>
263
+ <div class="ad-slot large-banner loading"
264
+ id="large-banner"
265
+ adstage="large-banner"
266
+ ad-type="BANNER"
267
+ data-size="728x90">
268
+ </div>
269
+ <div class="ad-info">
270
+ 슬롯 ID: large-banner<br>
271
+ 크기: 728x90 (Leaderboard)<br>
272
+ 위치: 페이지 상단
273
+ </div>
274
+ <div class="controls">
275
+ <button class="btn" onclick="refreshAd('large-banner')">🔄 새로고침</button>
276
+ <button class="btn secondary" onclick="hideAd('large-banner')">👁️ 숨기기</button>
277
+ </div>
278
+ </div>
279
+
280
+ <div class="ad-container">
281
+ <h3>중형 배너 (320x100)</h3>
282
+ <div class="ad-slot medium-banner loading"
283
+ id="medium-banner"
284
+ adstage="medium-banner"
285
+ ad-type="BANNER"
286
+ data-size="320x100">
287
+ </div>
288
+ <div class="ad-info">
289
+ 슬롯 ID: medium-banner<br>
290
+ 크기: 320x100 (Mobile Banner)<br>
291
+ 위치: 콘텐츠 중간
292
+ </div>
293
+ <div class="controls">
294
+ <button class="btn" onclick="refreshAd('medium-banner')">🔄 새로고침</button>
295
+ <button class="btn secondary" onclick="hideAd('medium-banner')">👁️ 숨기기</button>
296
+ </div>
297
+ </div>
298
+
299
+ <div class="ad-container">
300
+ <h3>소형 배너 (300x50)</h3>
301
+ <div class="ad-slot small-banner loading"
302
+ id="small-banner"
303
+ adstage="small-banner"
304
+ ad-type="BANNER"
305
+ data-size="300x50">
306
+ </div>
307
+ <div class="ad-info">
308
+ 슬롯 ID: small-banner<br>
309
+ 크기: 300x50 (Mobile Banner)<br>
310
+ 위치: 페이지 하단
311
+ </div>
312
+ <div class="controls">
313
+ <button class="btn" onclick="refreshAd('small-banner')">🔄 새로고침</button>
314
+ <button class="btn secondary" onclick="hideAd('small-banner')">👁️ 숨기기</button>
315
+ </div>
316
+ </div>
317
+
318
+ <div class="ad-container">
319
+ <h3>동적 배너 (유동 크기)</h3>
320
+ <div class="ad-slot medium-banner loading"
321
+ id="dynamic-banner"
322
+ adstage="dynamic-banner"
323
+ ad-type="BANNER"
324
+ data-size="responsive">
325
+ </div>
326
+ <div class="ad-info">
327
+ 슬롯 ID: dynamic-banner<br>
328
+ 크기: 반응형 (Responsive)<br>
329
+ 위치: 동적 콘텐츠 영역
330
+ </div>
331
+ <div class="controls">
332
+ <button class="btn" onclick="refreshAd('dynamic-banner')">🔄 새로고침</button>
333
+ <button class="btn secondary" onclick="hideAd('dynamic-banner')">👁️ 숨기기</button>
334
+ </div>
335
+ </div>
336
+ </div>
337
+ </div>
338
+
339
+ <div class="section">
340
+ <h2>⚡ 전체 제어</h2>
341
+ <div class="controls">
342
+ <button class="btn" onclick="refreshAllAds()">🔄 모든 광고 새로고침</button>
343
+ <button class="btn secondary" onclick="hideAllAds()">👁️ 모든 광고 숨기기</button>
344
+ <button class="btn" onclick="showAllAds()">👀 모든 광고 보이기</button>
345
+ <button class="btn secondary" onclick="clearLog()">🧹 로그 지우기</button>
346
+ </div>
347
+ </div>
348
+
349
+ <div class="section">
350
+ <h2>📝 구현 코드</h2>
351
+ <div class="code-example">
352
+ &lt;!-- HTML: 배너 광고 슬롯 --&gt;
353
+ &lt;div adstage="large-banner" ad-type="BANNER" data-size="728x90"&gt;&lt;/div&gt;
354
+
355
+ &lt;script type="module"&gt;
356
+ import AdStageSDK, { AdType } from '../dist/index.standalone.js';
357
+
358
+ // SDK 초기화
359
+ const sdk = AdStageSDK.init({
360
+ apiKey: 'YOUR_API_KEY',
361
+ debug: true
362
+ });
363
+
364
+ // 수동 광고 로드 (선택사항)
365
+ sdk.loadAd('large-banner', AdType.BANNER);
366
+ &lt;/script&gt;
367
+ </div>
368
+ </div>
369
+
370
+ <div class="status" id="status">
371
+ SDK 초기화 중...
372
+ </div>
373
+
374
+ <div class="log" id="log">
375
+ <div class="log-entry info">페이지 로드 시작...</div>
376
+ </div>
377
+ </div>
378
+ </div>
379
+
380
+ <script type="module">
381
+ import AdStageSDK, { AdType } from './dist/index.standalone.js';
382
+
383
+ const statusEl = document.getElementById('status');
384
+ const logEl = document.getElementById('log');
385
+
386
+ function addLog(message, type = 'info') {
387
+ const timestamp = new Date().toLocaleTimeString();
388
+ const logEntry = document.createElement('div');
389
+ logEntry.className = `log-entry ${type}`;
390
+ logEntry.textContent = `[${timestamp}] ${message}`;
391
+ logEl.appendChild(logEntry);
392
+ logEl.scrollTop = logEl.scrollHeight;
393
+ }
394
+
395
+ function updateAdSlotStatus(slotId, status) {
396
+ const slot = document.getElementById(slotId);
397
+ if (slot) {
398
+ slot.className = slot.className.replace(/\b(loading|loaded|error)\b/g, '');
399
+ slot.classList.add(status);
400
+ }
401
+ }
402
+
403
+ // 전역 함수들
404
+ window.refreshAd = function(slotId) {
405
+ addLog(`광고 새로고침 요청: ${slotId}`, 'info');
406
+ updateAdSlotStatus(slotId, 'loading');
407
+
408
+ try {
409
+ window.adstageSDK.refreshSlot(slotId);
410
+ addLog(`광고 새로고침 성공: ${slotId}`, 'info');
411
+ setTimeout(() => updateAdSlotStatus(slotId, 'loaded'), 1000);
412
+ } catch (error) {
413
+ addLog(`광고 새로고침 실패: ${slotId} - ${error.message}`, 'error');
414
+ updateAdSlotStatus(slotId, 'error');
415
+ }
416
+ };
417
+
418
+ window.hideAd = function(slotId) {
419
+ const slot = document.getElementById(slotId);
420
+ if (slot) {
421
+ slot.style.display = 'none';
422
+ addLog(`광고 숨김: ${slotId}`, 'info');
423
+ }
424
+ };
425
+
426
+ window.showAd = function(slotId) {
427
+ const slot = document.getElementById(slotId);
428
+ if (slot) {
429
+ slot.style.display = 'flex';
430
+ addLog(`광고 표시: ${slotId}`, 'info');
431
+ }
432
+ };
433
+
434
+ window.refreshAllAds = function() {
435
+ const slots = ['large-banner', 'medium-banner', 'small-banner', 'dynamic-banner'];
436
+ slots.forEach(slotId => {
437
+ setTimeout(() => refreshAd(slotId), Math.random() * 2000);
438
+ });
439
+ };
440
+
441
+ window.hideAllAds = function() {
442
+ const slots = ['large-banner', 'medium-banner', 'small-banner', 'dynamic-banner'];
443
+ slots.forEach(hideAd);
444
+ };
445
+
446
+ window.showAllAds = function() {
447
+ const slots = ['large-banner', 'medium-banner', 'small-banner', 'dynamic-banner'];
448
+ slots.forEach(showAd);
449
+ };
450
+
451
+ window.clearLog = function() {
452
+ logEl.innerHTML = '<div class="log-entry info">로그가 지워졌습니다.</div>';
453
+ };
454
+
455
+ try {
456
+ addLog('AdStage SDK 초기화 시작...', 'info');
457
+
458
+ // SDK 초기화
459
+ const sdk = AdStageSDK.init({
460
+ apiKey: '7dfddcfda637fbb73225bac3731688b80b90675942fe9f2057a88e2379aba2a4',
461
+ debug: true
462
+ });
463
+
464
+ // 전역 참조 저장
465
+ window.adstageSDK = sdk;
466
+
467
+ statusEl.innerHTML = '✅ SDK 초기화 완료 - 실제 배너 광고 로딩 중...';
468
+ statusEl.style.background = '#d4edda';
469
+ statusEl.style.borderColor = '#28a745';
470
+
471
+ addLog('SDK 초기화 완료', 'info');
472
+ addLog('실제 API에서 광고 로딩 시작...', 'info');
473
+
474
+ // 실제 API에서 광고 자동 로드
475
+ await sdk.autoLoadSlots();
476
+
477
+ // 광고 슬롯들에 클릭 이벤트 추가
478
+ document.querySelectorAll('.ad-slot').forEach(slot => {
479
+ const slotId = slot.getAttribute('adstage');
480
+ const adSize = slot.getAttribute('data-size');
481
+
482
+ // 클릭 이벤트
483
+ slot.addEventListener('click', function() {
484
+ addLog(`배너 광고 클릭: ${slotId} (${adSize})`, 'info');
485
+
486
+ // 시각적 피드백
487
+ this.style.background = '#e3f2fd';
488
+ setTimeout(() => {
489
+ this.style.background = 'white';
490
+ }, 300);
491
+ });
492
+
493
+ // 로딩 완료 상태로 업데이트
494
+ setTimeout(() => {
495
+ updateAdSlotStatus(slotId, 'loaded');
496
+ addLog(`실제 배너 광고 로드 완료: ${slotId}`, 'info');
497
+ }, Math.random() * 3000 + 1000);
498
+ });
499
+
500
+ addLog('🎯 실제 API 광고 로딩 완료 - beta-api.adstage.app', 'info');
501
+ console.log('배너 광고 예제 페이지 로드 완료');
502
+
503
+ } catch (error) {
504
+ console.error('SDK 초기화 오류:', error);
505
+ addLog(`SDK 초기화 실패: ${error.message}`, 'error');
506
+ statusEl.innerHTML = '❌ SDK 초기화 실패: ' + error.message;
507
+ statusEl.style.background = '#f8d7da';
508
+ statusEl.style.borderColor = '#dc3545';
509
+ }
510
+ </script>
511
+ </body>
512
+ </html>