@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,597 @@
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, #28a745, #20c997);
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
+ .nav {
44
+ background: #f8f9fa;
45
+ padding: 15px 30px;
46
+ border-bottom: 1px solid #dee2e6;
47
+ }
48
+
49
+ .back-link {
50
+ color: #28a745;
51
+ text-decoration: none;
52
+ font-weight: 500;
53
+ }
54
+
55
+ .content {
56
+ padding: 30px;
57
+ }
58
+
59
+ .section {
60
+ margin-bottom: 40px;
61
+ }
62
+
63
+ .section h2 {
64
+ color: #495057;
65
+ margin-bottom: 20px;
66
+ padding-bottom: 10px;
67
+ border-bottom: 2px solid #28a745;
68
+ }
69
+
70
+ .ad-grid {
71
+ display: grid;
72
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
73
+ gap: 25px;
74
+ margin-bottom: 30px;
75
+ }
76
+
77
+ .ad-container {
78
+ border: 2px solid #e9ecef;
79
+ border-radius: 12px;
80
+ padding: 25px;
81
+ background: #f8f9fa;
82
+ transition: all 0.3s ease;
83
+ }
84
+
85
+ .ad-container:hover {
86
+ border-color: #28a745;
87
+ box-shadow: 0 4px 12px rgba(40, 167, 69, 0.15);
88
+ }
89
+
90
+ .ad-container h3 {
91
+ margin-bottom: 15px;
92
+ color: #28a745;
93
+ font-size: 1.1rem;
94
+ }
95
+
96
+ .text-ad-slot {
97
+ background: white;
98
+ border: 2px dashed #dee2e6;
99
+ border-radius: 8px;
100
+ padding: 20px;
101
+ min-height: 120px;
102
+ transition: all 0.3s ease;
103
+ position: relative;
104
+ }
105
+
106
+ .text-ad-slot:hover {
107
+ border-color: #28a745;
108
+ background: #f8fff8;
109
+ }
110
+
111
+ .text-ad-slot.loading::before {
112
+ content: "📄 텍스트 광고 로딩 중...";
113
+ color: #6c757d;
114
+ display: block;
115
+ text-align: center;
116
+ margin-top: 20px;
117
+ }
118
+
119
+ .text-ad-content {
120
+ display: none;
121
+ }
122
+
123
+ .text-ad-content.loaded {
124
+ display: block;
125
+ }
126
+
127
+ .text-ad-title {
128
+ font-size: 1.2rem;
129
+ font-weight: bold;
130
+ color: #495057;
131
+ margin-bottom: 10px;
132
+ cursor: pointer;
133
+ transition: color 0.3s ease;
134
+ }
135
+
136
+ .text-ad-title:hover {
137
+ color: #28a745;
138
+ }
139
+
140
+ .text-ad-description {
141
+ color: #6c757d;
142
+ font-size: 0.95rem;
143
+ line-height: 1.5;
144
+ margin-bottom: 15px;
145
+ }
146
+
147
+ .text-ad-cta {
148
+ display: inline-block;
149
+ background: #28a745;
150
+ color: white;
151
+ padding: 8px 16px;
152
+ border-radius: 6px;
153
+ text-decoration: none;
154
+ font-size: 0.9rem;
155
+ font-weight: 500;
156
+ transition: background 0.3s ease;
157
+ }
158
+
159
+ .text-ad-cta:hover {
160
+ background: #218838;
161
+ color: white;
162
+ }
163
+
164
+ .text-ad-footer {
165
+ margin-top: 15px;
166
+ padding-top: 10px;
167
+ border-top: 1px solid #e9ecef;
168
+ font-size: 0.8rem;
169
+ color: #adb5bd;
170
+ }
171
+
172
+ .ad-info {
173
+ margin-top: 15px;
174
+ padding: 10px;
175
+ background: #e9ecef;
176
+ border-radius: 6px;
177
+ font-size: 0.9rem;
178
+ color: #6c757d;
179
+ }
180
+
181
+ .controls {
182
+ display: flex;
183
+ gap: 10px;
184
+ margin-top: 15px;
185
+ flex-wrap: wrap;
186
+ }
187
+
188
+ .btn {
189
+ padding: 8px 16px;
190
+ border: none;
191
+ border-radius: 6px;
192
+ background: #28a745;
193
+ color: white;
194
+ cursor: pointer;
195
+ font-size: 0.9rem;
196
+ transition: background 0.3s ease;
197
+ }
198
+
199
+ .btn:hover {
200
+ background: #218838;
201
+ }
202
+
203
+ .btn.secondary {
204
+ background: #6c757d;
205
+ }
206
+
207
+ .btn.secondary:hover {
208
+ background: #5a6268;
209
+ }
210
+
211
+ .status {
212
+ margin-top: 30px;
213
+ padding: 20px;
214
+ background: #d4edda;
215
+ border-left: 4px solid #28a745;
216
+ border-radius: 4px;
217
+ }
218
+
219
+ .log {
220
+ background: #f8f9fa;
221
+ border: 1px solid #dee2e6;
222
+ border-radius: 6px;
223
+ padding: 15px;
224
+ max-height: 200px;
225
+ overflow-y: auto;
226
+ font-family: monospace;
227
+ font-size: 0.8rem;
228
+ margin-top: 15px;
229
+ }
230
+
231
+ .log-entry {
232
+ margin-bottom: 5px;
233
+ padding: 2px 5px;
234
+ border-radius: 3px;
235
+ }
236
+
237
+ .log-entry.info {
238
+ background: #d4edda;
239
+ color: #155724;
240
+ }
241
+
242
+ .log-entry.warn {
243
+ background: #fff3cd;
244
+ color: #856404;
245
+ }
246
+
247
+ .log-entry.error {
248
+ background: #f8d7da;
249
+ color: #721c24;
250
+ }
251
+
252
+ .article-style {
253
+ padding: 20px;
254
+ background: white;
255
+ border-radius: 8px;
256
+ margin-bottom: 20px;
257
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
258
+ }
259
+
260
+ .article-content {
261
+ font-size: 1rem;
262
+ line-height: 1.7;
263
+ color: #495057;
264
+ margin-bottom: 20px;
265
+ }
266
+
267
+ .inline-text-ad {
268
+ background: #f8fff8;
269
+ border: 1px solid #28a745;
270
+ border-radius: 6px;
271
+ padding: 15px;
272
+ margin: 20px 0;
273
+ font-size: 0.9rem;
274
+ }
275
+
276
+ .sponsored-label {
277
+ background: #28a745;
278
+ color: white;
279
+ font-size: 0.7rem;
280
+ padding: 2px 6px;
281
+ border-radius: 3px;
282
+ margin-bottom: 8px;
283
+ display: inline-block;
284
+ }
285
+ </style>
286
+ </head>
287
+ <body>
288
+ <div class="container">
289
+ <div class="header">
290
+ <h1>📝 텍스트 광고 예제</h1>
291
+ <p>콘텐츠와 자연스럽게 어울리는 텍스트 기반 광고</p>
292
+ </div>
293
+
294
+ <div class="nav">
295
+ <a href="index.html" class="back-link">← 메인 페이지로 돌아가기</a>
296
+ </div>
297
+
298
+ <div class="content">
299
+ <div class="section">
300
+ <h2>📄 기본 텍스트 광고</h2>
301
+
302
+ <div class="ad-grid">
303
+ <div class="ad-container">
304
+ <h3>헤드라인 광고</h3>
305
+ <div class="text-ad-slot loading"
306
+ id="headline-text"
307
+ adstage="headline-text"
308
+ ad-type="TEXT">
309
+ <div class="text-ad-content">
310
+ <div class="text-ad-title">🚀 새로운 모바일 앱 출시!</div>
311
+ <div class="text-ad-description">혁신적인 기능과 직관적인 디자인으로 더 나은 사용자 경험을 제공합니다. 지금 바로 다운로드하고 특별 혜택을 받아보세요.</div>
312
+ <a href="#" class="text-ad-cta">지금 다운로드</a>
313
+ <div class="text-ad-footer">
314
+ <span class="sponsored-label">스폰서</span>
315
+ AdStage • 텍스트 광고
316
+ </div>
317
+ </div>
318
+ </div>
319
+ <div class="ad-info">
320
+ 슬롯 ID: headline-text<br>
321
+ 형태: 헤드라인 + 설명 + CTA<br>
322
+ 위치: 메인 콘텐츠 상단
323
+ </div>
324
+ <div class="controls">
325
+ <button class="btn" onclick="refreshTextAd('headline-text')">🔄 새로고침</button>
326
+ <button class="btn secondary" onclick="hideAd('headline-text')">👁️ 숨기기</button>
327
+ </div>
328
+ </div>
329
+
330
+ <div class="ad-container">
331
+ <h3>제품 광고</h3>
332
+ <div class="text-ad-slot loading"
333
+ id="product-text"
334
+ adstage="product-text"
335
+ ad-type="TEXT">
336
+ <div class="text-ad-content">
337
+ <div class="text-ad-title">💡 스마트 홈 솔루션</div>
338
+ <div class="text-ad-description">IoT 기술로 집안의 모든 기기를 연결하고 제어하세요. 에너지 절약과 편의성을 동시에 얻을 수 있습니다.</div>
339
+ <a href="#" class="text-ad-cta">자세히 보기</a>
340
+ <div class="text-ad-footer">
341
+ <span class="sponsored-label">스폰서</span>
342
+ SmartHome Corp • 제품 광고
343
+ </div>
344
+ </div>
345
+ </div>
346
+ <div class="ad-info">
347
+ 슬롯 ID: product-text<br>
348
+ 형태: 제품 소개 텍스트<br>
349
+ 위치: 사이드바
350
+ </div>
351
+ <div class="controls">
352
+ <button class="btn" onclick="refreshTextAd('product-text')">🔄 새로고침</button>
353
+ <button class="btn secondary" onclick="hideAd('product-text')">👁️ 숨기기</button>
354
+ </div>
355
+ </div>
356
+
357
+ <div class="ad-container">
358
+ <h3>서비스 광고</h3>
359
+ <div class="text-ad-slot loading"
360
+ id="service-text"
361
+ adstage="service-text"
362
+ ad-type="TEXT">
363
+ <div class="text-ad-content">
364
+ <div class="text-ad-title">🏆 프리미엄 컨설팅 서비스</div>
365
+ <div class="text-ad-description">전문가의 1:1 맞춤 컨설팅으로 비즈니스 성장을 가속화하세요. 무료 상담 신청하고 성공 전략을 수립해보세요.</div>
366
+ <a href="#" class="text-ad-cta">무료 상담 신청</a>
367
+ <div class="text-ad-footer">
368
+ <span class="sponsored-label">스폰서</span>
369
+ Business Pro • 서비스 광고
370
+ </div>
371
+ </div>
372
+ </div>
373
+ <div class="ad-info">
374
+ 슬롯 ID: service-text<br>
375
+ 형태: 서비스 소개 텍스트<br>
376
+ 위치: 콘텐츠 하단
377
+ </div>
378
+ <div class="controls">
379
+ <button class="btn" onclick="refreshTextAd('service-text')">🔄 새로고침</button>
380
+ <button class="btn secondary" onclick="hideAd('service-text')">👁️ 숨기기</button>
381
+ </div>
382
+ </div>
383
+
384
+ <div class="ad-container">
385
+ <h3>이벤트 광고</h3>
386
+ <div class="text-ad-slot loading"
387
+ id="event-text"
388
+ adstage="event-text"
389
+ ad-type="TEXT">
390
+ <div class="text-ad-content">
391
+ <div class="text-ad-title">🎉 한정 특가 이벤트!</div>
392
+ <div class="text-ad-description">오늘 하루만! 최대 70% 할인 혜택을 놓치지 마세요. 인기 상품들이 특별 가격으로 만나볼 수 있는 기회입니다.</div>
393
+ <a href="#" class="text-ad-cta">특가 상품 보기</a>
394
+ <div class="text-ad-footer">
395
+ <span class="sponsored-label">스폰서</span>
396
+ Shopping Mall • 프로모션 광고
397
+ </div>
398
+ </div>
399
+ </div>
400
+ <div class="ad-info">
401
+ 슬롯 ID: event-text<br>
402
+ 형태: 이벤트/프로모션 텍스트<br>
403
+ 위치: 플로팅 영역
404
+ </div>
405
+ <div class="controls">
406
+ <button class="btn" onclick="refreshTextAd('event-text')">🔄 새로고침</button>
407
+ <button class="btn secondary" onclick="hideAd('event-text')">👁️ 숨기기</button>
408
+ </div>
409
+ </div>
410
+ </div>
411
+ </div>
412
+
413
+ <div class="section">
414
+ <h2>📰 인라인 텍스트 광고 (기사 내 삽입)</h2>
415
+
416
+ <div class="article-style">
417
+ <h3>모바일 마케팅의 최신 트렌드</h3>
418
+ <div class="article-content">
419
+ 모바일 마케팅은 끊임없이 진화하고 있습니다. 사용자들의 행동 패턴이 변화하면서 기업들도 새로운 전략을 모색해야 합니다.
420
+ 개인화된 콘텐츠와 실시간 상호작용이 핵심 키워드로 떠오르고 있습니다.
421
+ </div>
422
+
423
+ <!-- 인라인 텍스트 광고 -->
424
+ <div class="inline-text-ad"
425
+ id="inline-article"
426
+ adstage="inline-article"
427
+ ad-type="TEXT">
428
+ <span class="sponsored-label">스폰서</span>
429
+ <div class="text-ad-title">📊 마케팅 성과 분석 도구</div>
430
+ <div class="text-ad-description">실시간 데이터 분석으로 마케팅 ROI를 극대화하세요.</div>
431
+ <a href="#" class="text-ad-cta">무료 체험</a>
432
+ </div>
433
+
434
+ <div class="article-content">
435
+ 특히 딥링크를 활용한 사용자 여정 추적과 세분화된 타겟팅이 중요해지고 있습니다.
436
+ AdStage와 같은 플랫폼을 통해 정교한 분석과 최적화가 가능합니다.
437
+ </div>
438
+ </div>
439
+ </div>
440
+
441
+ <div class="section">
442
+ <h2>⚡ 전체 제어</h2>
443
+ <div class="controls">
444
+ <button class="btn" onclick="refreshAllTextAds()">🔄 모든 텍스트 광고 새로고침</button>
445
+ <button class="btn secondary" onclick="hideAllAds()">👁️ 모든 광고 숨기기</button>
446
+ <button class="btn" onclick="showAllAds()">👀 모든 광고 보이기</button>
447
+ <button class="btn secondary" onclick="clearLog()">🧹 로그 지우기</button>
448
+ </div>
449
+ </div>
450
+
451
+ <div class="status" id="status">
452
+ SDK 초기화 중...
453
+ </div>
454
+
455
+ <div class="log" id="log">
456
+ <div class="log-entry info">텍스트 광고 페이지 로드 시작...</div>
457
+ </div>
458
+ </div>
459
+ </div>
460
+
461
+ <script type="module">
462
+ import AdStageSDK, { AdType } from './dist/index.standalone.js';
463
+
464
+ const statusEl = document.getElementById('status');
465
+ const logEl = document.getElementById('log');
466
+
467
+ function addLog(message, type = 'info') {
468
+ const timestamp = new Date().toLocaleTimeString();
469
+ const logEntry = document.createElement('div');
470
+ logEntry.className = `log-entry ${type}`;
471
+ logEntry.textContent = `[${timestamp}] ${message}`;
472
+ logEl.appendChild(logEntry);
473
+ logEl.scrollTop = logEl.scrollHeight;
474
+ }
475
+
476
+ // 전역 함수들
477
+ window.refreshTextAd = function(slotId) {
478
+ addLog(`텍스트 광고 새로고침: ${slotId}`, 'info');
479
+ const slot = document.getElementById(slotId);
480
+ const content = slot.querySelector('.text-ad-content');
481
+
482
+ if (slot && content) {
483
+ slot.classList.add('loading');
484
+ content.classList.remove('loaded');
485
+
486
+ setTimeout(() => {
487
+ slot.classList.remove('loading');
488
+ content.classList.add('loaded');
489
+ addLog(`텍스트 광고 로드 완료: ${slotId}`, 'info');
490
+ }, Math.random() * 2000 + 500);
491
+ }
492
+ };
493
+
494
+ window.hideAd = function(slotId) {
495
+ const slot = document.getElementById(slotId);
496
+ if (slot) {
497
+ slot.style.display = 'none';
498
+ addLog(`광고 숨김: ${slotId}`, 'info');
499
+ }
500
+ };
501
+
502
+ window.showAd = function(slotId) {
503
+ const slot = document.getElementById(slotId);
504
+ if (slot) {
505
+ slot.style.display = 'block';
506
+ addLog(`광고 표시: ${slotId}`, 'info');
507
+ }
508
+ };
509
+
510
+ window.refreshAllTextAds = function() {
511
+ const slots = ['headline-text', 'product-text', 'service-text', 'event-text', 'inline-article'];
512
+ slots.forEach(slotId => {
513
+ setTimeout(() => refreshTextAd(slotId), Math.random() * 2000);
514
+ });
515
+ };
516
+
517
+ window.hideAllAds = function() {
518
+ const slots = ['headline-text', 'product-text', 'service-text', 'event-text', 'inline-article'];
519
+ slots.forEach(hideAd);
520
+ };
521
+
522
+ window.showAllAds = function() {
523
+ const slots = ['headline-text', 'product-text', 'service-text', 'event-text', 'inline-article'];
524
+ slots.forEach(showAd);
525
+ };
526
+
527
+ window.clearLog = function() {
528
+ logEl.innerHTML = '<div class="log-entry info">로그가 지워졌습니다.</div>';
529
+ };
530
+
531
+ try {
532
+ addLog('AdStage SDK 초기화 시작...', 'info');
533
+
534
+ // SDK 초기화
535
+ const sdk = AdStageSDK.init({
536
+ apiKey: '7dfddcfda637fbb73225bac3731688b80b90675942fe9f2057a88e2379aba2a4',
537
+ debug: true
538
+ });
539
+
540
+ window.adstageSDK = sdk;
541
+
542
+ statusEl.innerHTML = '✅ SDK 초기화 완료 - 실제 텍스트 광고 로딩 중...';
543
+ statusEl.style.background = '#d4edda';
544
+ statusEl.style.borderColor = '#28a745';
545
+
546
+ addLog('SDK 초기화 완료', 'info');
547
+ addLog('실제 API에서 텍스트 광고 로딩 시작...', 'info');
548
+
549
+ // 실제 API에서 광고 자동 로드
550
+ await sdk.autoLoadSlots();
551
+
552
+ // 텍스트 광고 슬롯들 초기화
553
+ document.querySelectorAll('.text-ad-slot, .inline-text-ad').forEach(slot => {
554
+ const slotId = slot.getAttribute('adstage');
555
+ const content = slot.querySelector('.text-ad-content');
556
+
557
+ // 클릭 이벤트 추가
558
+ if (content) {
559
+ content.addEventListener('click', function(e) {
560
+ if (e.target.classList.contains('text-ad-cta')) {
561
+ e.preventDefault();
562
+ addLog(`텍스트 광고 CTA 클릭: ${slotId}`, 'info');
563
+
564
+ // 시각적 피드백
565
+ e.target.style.background = '#198754';
566
+ setTimeout(() => {
567
+ e.target.style.background = '#28a745';
568
+ }, 200);
569
+ } else {
570
+ addLog(`텍스트 광고 클릭: ${slotId}`, 'info');
571
+ }
572
+ });
573
+ }
574
+
575
+ // 로딩 시뮬레이션
576
+ setTimeout(() => {
577
+ slot.classList.remove('loading');
578
+ if (content) {
579
+ content.classList.add('loaded');
580
+ }
581
+ addLog(`텍스트 광고 로드 완료: ${slotId}`, 'info');
582
+ }, Math.random() * 3000 + 1000);
583
+ });
584
+
585
+ addLog('텍스트 광고 페이지 초기화 완료', 'info');
586
+ console.log('텍스트 광고 예제 페이지 로드 완료');
587
+
588
+ } catch (error) {
589
+ console.error('SDK 초기화 오류:', error);
590
+ addLog(`SDK 초기화 실패: ${error.message}`, 'error');
591
+ statusEl.innerHTML = '❌ SDK 초기화 실패: ' + error.message;
592
+ statusEl.style.background = '#f8d7da';
593
+ statusEl.style.borderColor = '#dc3545';
594
+ }
595
+ </script>
596
+ </body>
597
+ </html>