@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,226 @@
1
+ import { useEffect, useState } from 'react';
2
+ // @ts-ignore - SDK 로컬 경로 사용
3
+ import { AdStageSDK, AdType } from '../../../dist/index.esm.js';
4
+
5
+ function App() {
6
+ const [isSDKInitialized, setIsSDKInitialized] = useState(false);
7
+ const [isLoading, setIsLoading] = useState(false);
8
+
9
+ useEffect(() => {
10
+ // SDK 초기화
11
+ const initSDK = async () => {
12
+ try {
13
+ setIsLoading(true);
14
+ const sdk = AdStageSDK.init({
15
+ apiKey: process.env.REACT_APP_ADSTAGE_API_KEY || '',
16
+ debug: true
17
+ });
18
+ setIsSDKInitialized(true);
19
+ console.log('AdStage SDK 초기화 완료');
20
+ } catch (error) {
21
+ console.error('SDK 초기화 실패:', error);
22
+ } finally {
23
+ setIsLoading(false);
24
+ }
25
+ };
26
+
27
+ initSDK();
28
+ }, []);
29
+
30
+ const createBannerSlot = async () => {
31
+ if (!isSDKInitialized) return;
32
+
33
+ try {
34
+ const sdk = AdStageSDK.getInstance();
35
+ await sdk.createSlot(
36
+ 'banner-slot-1',
37
+ 'banner-container',
38
+ AdType.BANNER,
39
+ {
40
+ width: '100%',
41
+ height: 200,
42
+ autoSlideInterval: 5,
43
+ sliderEffect: 'fade'
44
+ }
45
+ );
46
+ console.log('배너 슬롯 생성 완료');
47
+ } catch (error) {
48
+ console.error('배너 슬롯 생성 실패:', error);
49
+ }
50
+ };
51
+
52
+ const createSliderSlot = async () => {
53
+ if (!isSDKInitialized) return;
54
+
55
+ try {
56
+ const sdk = AdStageSDK.getInstance();
57
+ await sdk.createSlot(
58
+ 'slider-slot-1',
59
+ 'slider-container',
60
+ AdType.SLIDER,
61
+ {
62
+ width: 400,
63
+ height: 300,
64
+ autoSlideInterval: 3,
65
+ sliderEffect: 'slide'
66
+ }
67
+ );
68
+ console.log('슬라이더 슬롯 생성 완료');
69
+ } catch (error) {
70
+ console.error('슬라이더 슬롯 생성 실패:', error);
71
+ }
72
+ };
73
+
74
+ const trackCustomEvent = () => {
75
+ if (!isSDKInitialized) return;
76
+
77
+ try {
78
+ const sdk = AdStageSDK.getInstance();
79
+ sdk.trackEvent('button_click', {
80
+ button_name: 'custom_event_button',
81
+ timestamp: new Date().toISOString()
82
+ });
83
+ console.log('커스텀 이벤트 추적 완료');
84
+ } catch (error) {
85
+ console.error('이벤트 추적 실패:', error);
86
+ }
87
+ };
88
+
89
+ return (
90
+ <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
91
+ <h1>AdStage SDK React 예제</h1>
92
+
93
+ {isLoading && (
94
+ <div style={{ color: '#666', marginBottom: '20px' }}>
95
+ SDK 초기화 중...
96
+ </div>
97
+ )}
98
+
99
+ <div style={{ marginBottom: '20px' }}>
100
+ <strong>SDK 상태:</strong>
101
+ <span style={{
102
+ color: isSDKInitialized ? '#28a745' : '#dc3545',
103
+ marginLeft: '8px'
104
+ }}>
105
+ {isSDKInitialized ? '초기화 완료' : '초기화되지 않음'}
106
+ </span>
107
+ </div>
108
+
109
+ <div style={{ marginBottom: '30px' }}>
110
+ <h2>광고 슬롯 관리</h2>
111
+ <div style={{ marginBottom: '10px' }}>
112
+ <button
113
+ onClick={createBannerSlot}
114
+ disabled={!isSDKInitialized}
115
+ style={{
116
+ padding: '8px 16px',
117
+ marginRight: '10px',
118
+ backgroundColor: isSDKInitialized ? '#007bff' : '#ccc',
119
+ color: 'white',
120
+ border: 'none',
121
+ borderRadius: '4px',
122
+ cursor: isSDKInitialized ? 'pointer' : 'not-allowed'
123
+ }}
124
+ >
125
+ 배너 광고 생성
126
+ </button>
127
+ <button
128
+ onClick={createSliderSlot}
129
+ disabled={!isSDKInitialized}
130
+ style={{
131
+ padding: '8px 16px',
132
+ backgroundColor: isSDKInitialized ? '#28a745' : '#ccc',
133
+ color: 'white',
134
+ border: 'none',
135
+ borderRadius: '4px',
136
+ cursor: isSDKInitialized ? 'pointer' : 'not-allowed'
137
+ }}
138
+ >
139
+ 슬라이더 광고 생성
140
+ </button>
141
+ </div>
142
+ </div>
143
+
144
+ <div style={{ marginBottom: '30px' }}>
145
+ <h2>이벤트 추적</h2>
146
+ <button
147
+ onClick={trackCustomEvent}
148
+ disabled={!isSDKInitialized}
149
+ style={{
150
+ padding: '8px 16px',
151
+ backgroundColor: isSDKInitialized ? '#ffc107' : '#ccc',
152
+ color: isSDKInitialized ? '#212529' : 'white',
153
+ border: 'none',
154
+ borderRadius: '4px',
155
+ cursor: isSDKInitialized ? 'pointer' : 'not-allowed'
156
+ }}
157
+ >
158
+ 커스텀 이벤트 추적
159
+ </button>
160
+ </div>
161
+
162
+ <div style={{ marginBottom: '20px' }}>
163
+ <h2>광고 컨테이너</h2>
164
+
165
+ <div style={{ marginBottom: '20px' }}>
166
+ <h3>배너 광고</h3>
167
+ <div
168
+ id="banner-container"
169
+ style={{
170
+ border: '2px dashed #ccc',
171
+ borderRadius: '4px',
172
+ padding: '20px',
173
+ textAlign: 'center',
174
+ backgroundColor: '#f8f9fa',
175
+ minHeight: '200px',
176
+ display: 'flex',
177
+ alignItems: 'center',
178
+ justifyContent: 'center'
179
+ }}
180
+ >
181
+ 배너 광고가 여기에 로드됩니다
182
+ </div>
183
+ </div>
184
+
185
+ <div style={{ marginBottom: '20px' }}>
186
+ <h3>슬라이더 광고</h3>
187
+ <div
188
+ id="slider-container"
189
+ style={{
190
+ border: '2px dashed #ccc',
191
+ borderRadius: '4px',
192
+ padding: '20px',
193
+ textAlign: 'center',
194
+ backgroundColor: '#f8f9fa',
195
+ minHeight: '300px',
196
+ width: '400px',
197
+ display: 'flex',
198
+ alignItems: 'center',
199
+ justifyContent: 'center'
200
+ }}
201
+ >
202
+ 슬라이더 광고가 여기에 로드됩니다
203
+ </div>
204
+ </div>
205
+ </div>
206
+
207
+ <div style={{
208
+ marginTop: '40px',
209
+ padding: '16px',
210
+ backgroundColor: '#e9ecef',
211
+ borderRadius: '4px',
212
+ fontSize: '14px'
213
+ }}>
214
+ <h3 style={{ marginTop: '0' }}>사용 방법:</h3>
215
+ <ol>
216
+ <li>SDK가 초기화되면 "초기화 완료" 상태가 표시됩니다</li>
217
+ <li>"배너 광고 생성" 또는 "슬라이더 광고 생성" 버튼을 클릭하여 광고 슬롯을 생성합니다</li>
218
+ <li>"커스텀 이벤트 추적" 버튼을 클릭하여 사용자 이벤트를 추적합니다</li>
219
+ <li>브라우저 개발자 도구의 콘솔에서 SDK 동작을 확인할 수 있습니다</li>
220
+ </ol>
221
+ </div>
222
+ </div>
223
+ );
224
+ }
225
+
226
+ export default App;
@@ -0,0 +1,37 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ margin: 0;
4
+ padding: 0;
5
+ }
6
+
7
+ body {
8
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
9
+ line-height: 1.6;
10
+ color: #333;
11
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
12
+ min-height: 100vh;
13
+ }
14
+
15
+ #root {
16
+ min-height: 100vh;
17
+ }
18
+
19
+ button:hover {
20
+ opacity: 0.9;
21
+ transform: translateY(-1px);
22
+ transition: all 0.3s ease;
23
+ }
24
+
25
+ button:active {
26
+ transform: translateY(0);
27
+ }
28
+
29
+ @media (max-width: 768px) {
30
+ .metrics-grid {
31
+ grid-template-columns: 1fr !important;
32
+ }
33
+
34
+ .button-group {
35
+ flex-direction: column;
36
+ }
37
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import App from './App'
4
+ import './index.css'
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')!).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ )
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+ "jsx": "react-jsx",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["src"],
24
+ "references": [{ "path": "./tsconfig.node.json" }]
25
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true
8
+ },
9
+ "include": ["vite.config.ts"]
10
+ }
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ port: 3000,
8
+ open: true
9
+ },
10
+ resolve: {
11
+ alias: {
12
+ '@adstage/web-sdk': '../../dist/index.esm.js'
13
+ }
14
+ }
15
+ })
@@ -0,0 +1,200 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ margin: 0;
4
+ padding: 0;
5
+ }
6
+
7
+ body {
8
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
9
+ line-height: 1.6;
10
+ color: #333;
11
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
12
+ min-height: 100vh;
13
+ }
14
+
15
+ .container {
16
+ max-width: 1200px;
17
+ margin: 0 auto;
18
+ padding: 20px;
19
+ }
20
+
21
+ .header {
22
+ text-align: center;
23
+ background: white;
24
+ border-radius: 12px;
25
+ padding: 40px 30px;
26
+ margin-bottom: 30px;
27
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
28
+ }
29
+
30
+ .header h1 {
31
+ color: #667eea;
32
+ font-size: 2.5rem;
33
+ margin-bottom: 10px;
34
+ }
35
+
36
+ .header p {
37
+ color: #666;
38
+ font-size: 1.1rem;
39
+ }
40
+
41
+ .main {
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: 30px;
45
+ }
46
+
47
+ .metrics {
48
+ background: white;
49
+ border-radius: 12px;
50
+ padding: 30px;
51
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
52
+ }
53
+
54
+ .metrics h2 {
55
+ color: #555;
56
+ margin-bottom: 20px;
57
+ font-size: 1.5rem;
58
+ }
59
+
60
+ .metrics-grid {
61
+ display: grid;
62
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
63
+ gap: 20px;
64
+ }
65
+
66
+ .metric-card {
67
+ text-align: center;
68
+ padding: 20px;
69
+ background: #f8f9fa;
70
+ border-radius: 8px;
71
+ border: 2px solid #e9ecef;
72
+ }
73
+
74
+ .metric-value {
75
+ font-size: 2rem;
76
+ font-weight: bold;
77
+ color: #667eea;
78
+ }
79
+
80
+ .metric-label {
81
+ color: #666;
82
+ margin-top: 5px;
83
+ font-size: 0.9rem;
84
+ }
85
+
86
+ .ads-section {
87
+ background: white;
88
+ border-radius: 12px;
89
+ padding: 30px;
90
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
91
+ }
92
+
93
+ .ads-section h2 {
94
+ color: #555;
95
+ margin-bottom: 25px;
96
+ font-size: 1.5rem;
97
+ }
98
+
99
+ .ad-demo {
100
+ margin-bottom: 30px;
101
+ padding: 20px;
102
+ border: 2px solid #f0f0f0;
103
+ border-radius: 8px;
104
+ background: #fafafa;
105
+ }
106
+
107
+ .ad-demo h3 {
108
+ color: #555;
109
+ margin-bottom: 15px;
110
+ font-size: 1.2rem;
111
+ }
112
+
113
+ .banner-ad, .text-ad, .native-ad, .video-ad {
114
+ margin: 15px 0;
115
+ padding: 20px;
116
+ border: 1px dashed #ccc;
117
+ border-radius: 6px;
118
+ background: white;
119
+ min-height: 80px;
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ }
124
+
125
+ .interaction-section {
126
+ background: white;
127
+ border-radius: 12px;
128
+ padding: 30px;
129
+ text-align: center;
130
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
131
+ }
132
+
133
+ .interaction-section h2 {
134
+ color: #555;
135
+ margin-bottom: 25px;
136
+ font-size: 1.5rem;
137
+ }
138
+
139
+ .conversion-btn, .reset-btn {
140
+ padding: 12px 24px;
141
+ margin: 0 10px;
142
+ border: none;
143
+ border-radius: 6px;
144
+ font-size: 16px;
145
+ cursor: pointer;
146
+ transition: all 0.3s ease;
147
+ }
148
+
149
+ .conversion-btn {
150
+ background: #28a745;
151
+ color: white;
152
+ }
153
+
154
+ .conversion-btn:hover {
155
+ background: #218838;
156
+ transform: translateY(-2px);
157
+ }
158
+
159
+ .reset-btn {
160
+ background: #6c757d;
161
+ color: white;
162
+ }
163
+
164
+ .reset-btn:hover {
165
+ background: #5a6268;
166
+ transform: translateY(-2px);
167
+ }
168
+
169
+ .footer {
170
+ text-align: center;
171
+ background: white;
172
+ border-radius: 12px;
173
+ padding: 20px;
174
+ margin-top: 30px;
175
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
176
+ color: #666;
177
+ }
178
+
179
+ @media (max-width: 768px) {
180
+ .container {
181
+ padding: 10px;
182
+ }
183
+
184
+ .header h1 {
185
+ font-size: 2rem;
186
+ }
187
+
188
+ .metrics-grid {
189
+ grid-template-columns: repeat(3, 1fr);
190
+ gap: 10px;
191
+ }
192
+
193
+ .metric-card {
194
+ padding: 15px 10px;
195
+ }
196
+
197
+ .metric-value {
198
+ font-size: 1.5rem;
199
+ }
200
+ }
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+
3
+ // @ts-ignore - SDK 로컬 경로 사용
4
+ import { AdStageProvider } from '../../../dist/index.esm.js';
5
+ import './globals.css';
6
+
7
+ export default function RootLayout({
8
+ children,
9
+ }: {
10
+ children: React.ReactNode;
11
+ }) {
12
+ return (
13
+ <html lang="ko">
14
+ <body>
15
+ <AdStageProvider
16
+ config={{
17
+ apiKey: process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || '',
18
+ debug: process.env.NODE_ENV === 'development'
19
+ }}
20
+ autoInit={true}
21
+ >
22
+ {children}
23
+ </AdStageProvider>
24
+ </body>
25
+ </html>
26
+ );
27
+ }