@adstage/web-sdk 2.6.1 → 2.6.3

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.
@@ -0,0 +1,139 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { track, pageView } from '@adstage/web-sdk';
3
+
4
+ export default function Home() {
5
+ const [result, setResult] = useState('이벤트 결과가 여기에 표시됩니다.');
6
+
7
+ const showResult = (eventName, params, success = true) => {
8
+ const timestamp = new Date().toLocaleString();
9
+ setResult(`[${timestamp}] ${eventName} 이벤트 ${success ? '전송됨' : '실패'}\n\n파라미터:\n${JSON.stringify(params, null, 2)}`);
10
+ };
11
+
12
+ // 장바구니 추가 이벤트
13
+ const handleAddToCart = () => {
14
+ const params = {
15
+ item_id: `prod_${Math.floor(Math.random() * 9999) + 1000}`,
16
+ quantity: Math.floor(Math.random() * 3) + 1,
17
+ cart_value: Math.floor(Math.random() * 200000) + 50000,
18
+ category: 'home',
19
+ brand: 'IKEA',
20
+ source: 'product_page'
21
+ };
22
+
23
+ try {
24
+ track('add_to_cart', params);
25
+ showResult('add_to_cart', params);
26
+ } catch (error) {
27
+ showResult('add_to_cart', params, false);
28
+ console.error('AddToCart event failed:', error);
29
+ }
30
+ };
31
+
32
+ // 앱 설치 이벤트
33
+ const handleAppInstall = () => {
34
+ const params = {
35
+ install_source: 'google_play',
36
+ campaign: 'summer_2024',
37
+ days_since_click: Math.floor(Math.random() * 7),
38
+ organic: false,
39
+ referrer: 'google_ads'
40
+ };
41
+
42
+ try {
43
+ track('first_open', params);
44
+ showResult('first_open', params);
45
+ } catch (error) {
46
+ showResult('first_open', params, false);
47
+ console.error('FirstOpen event failed:', error);
48
+ }
49
+ };
50
+
51
+ // 검색 이벤트
52
+ const handleSearch = () => {
53
+ const searchTerms = ['iPhone', 'Samsung Galaxy', 'MacBook', 'AirPods', 'iPad'];
54
+ const params = {
55
+ search_term: searchTerms[Math.floor(Math.random() * searchTerms.length)],
56
+ results_count: Math.floor(Math.random() * 50) + 10,
57
+ category: 'electronics',
58
+ filters_applied: Math.random() > 0.5,
59
+ sort_by: ['price', 'popularity', 'rating', 'newest'][Math.floor(Math.random() * 4)]
60
+ };
61
+
62
+ try {
63
+ track('search', params);
64
+ showResult('search', params);
65
+ } catch (error) {
66
+ showResult('search', params, false);
67
+ console.error('Search event failed:', error);
68
+ }
69
+ };
70
+
71
+ // 페이지 로드 시 자동 페이지뷰
72
+ useEffect(() => {
73
+ const timer = setTimeout(() => {
74
+ const params = {
75
+ page: '/',
76
+ title: 'AdStage Next.js Example',
77
+ referrer: typeof window !== 'undefined' ? document.referrer || 'direct' : 'ssr',
78
+ utm_source: 'direct'
79
+ };
80
+ pageView(params);
81
+ showResult('page_view', params);
82
+ }, 1000);
83
+
84
+ return () => clearTimeout(timer);
85
+ }, []);
86
+
87
+ return (
88
+ <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
89
+ <h1>AdStage SDK - Next.js 이벤트 추적 예시</h1>
90
+
91
+ <div>
92
+ <button
93
+ onClick={handleAddToCart}
94
+ style={buttonStyle}
95
+ >
96
+ 장바구니 추가 이벤트
97
+ </button>
98
+
99
+ <button
100
+ onClick={handleAppInstall}
101
+ style={buttonStyle}
102
+ >
103
+ 앱 설치 이벤트
104
+ </button>
105
+
106
+ <button
107
+ onClick={handleSearch}
108
+ style={buttonStyle}
109
+ >
110
+ 검색 이벤트
111
+ </button>
112
+ </div>
113
+
114
+ <div style={resultStyle}>
115
+ {result}
116
+ </div>
117
+ </div>
118
+ );
119
+ }
120
+
121
+ const buttonStyle = {
122
+ padding: '12px 24px',
123
+ margin: '10px',
124
+ fontSize: '16px',
125
+ border: '1px solid #ccc',
126
+ borderRadius: '4px',
127
+ backgroundColor: '#f8f9fa',
128
+ cursor: 'pointer'
129
+ };
130
+
131
+ const resultStyle = {
132
+ marginTop: '20px',
133
+ padding: '15px',
134
+ border: '1px solid #ddd',
135
+ borderRadius: '4px',
136
+ backgroundColor: '#f8f9fa',
137
+ whiteSpace: 'pre-wrap',
138
+ fontFamily: 'monospace'
139
+ };
File without changes
@@ -0,0 +1,57 @@
1
+ # AdStage SDK - React 이벤트 추적 예시
2
+
3
+ ## 설치 및 실행
4
+
5
+ ```bash
6
+ cd /Users/gunny/Documents/workspace/adstage/sdk/src/example/events/react
7
+ npm install # @adstage/web-sdk@^2.6.1 설치됨
8
+ npm start
9
+ ```
10
+
11
+ ## 기능
12
+
13
+ - 구매 이벤트 전송
14
+ - 회원가입 이벤트 전송
15
+ - 상품조회 이벤트 전송
16
+ - 자동 페이지뷰 추적
17
+
18
+ ## 이벤트 파라미터 예시
19
+
20
+ ### 구매 이벤트 (purchase)
21
+ ```javascript
22
+ {
23
+ transaction_id: "tx_1725123456789",
24
+ value: 158000,
25
+ currency: "KRW",
26
+ payment_method: "card",
27
+ items: 3,
28
+ category: "fashion",
29
+ brand: "Nike"
30
+ }
31
+ ```
32
+
33
+ ### 회원가입 이벤트 (signup_complete)
34
+ ```javascript
35
+ {
36
+ user_id: "user_1725123456789",
37
+ signup_method: "google",
38
+ email_verified: false,
39
+ time_to_complete: 85,
40
+ form_version: "v3",
41
+ utm_source: "facebook_ads",
42
+ utm_medium: "social"
43
+ }
44
+ ```
45
+
46
+ ### 상품조회 이벤트 (view_item)
47
+ ```javascript
48
+ {
49
+ item_id: "prod_1234",
50
+ category: "electronics",
51
+ brand: "Apple",
52
+ price: 1290000,
53
+ currency: "KRW",
54
+ referrer: "search",
55
+ search_term: "iPhone 15 Pro"
56
+ }
57
+ ```
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "adstage-events-react-example",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "dependencies": {
6
+ "@adstage/web-sdk": "^2.6.1",
7
+ "react": "^18.2.0",
8
+ "react-dom": "^18.2.0",
9
+ "react-scripts": "5.0.1"
10
+ },
11
+ "scripts": {
12
+ "start": "react-scripts start",
13
+ "build": "react-scripts build"
14
+ },
15
+ "browserslist": {
16
+ "production": [
17
+ ">0.2%",
18
+ "not dead",
19
+ "not op_mini all"
20
+ ],
21
+ "development": [
22
+ "last 1 chrome version",
23
+ "last 1 firefox version",
24
+ "last 1 safari version"
25
+ ]
26
+ }
27
+ }
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>AdStage Events React Example</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,162 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { AdStageProvider, useAdStageInstance, track, pageView } from '@adstage/web-sdk';
3
+
4
+ // 이벤트 컴포넌트
5
+ function EventButtons() {
6
+ const adstage = useAdStageInstance();
7
+ const [result, setResult] = useState('이벤트 결과가 여기에 표시됩니다.');
8
+
9
+ const showResult = (eventName, params, success = true) => {
10
+ const timestamp = new Date().toLocaleString();
11
+ setResult(`[${timestamp}] ${eventName} 이벤트 ${success ? '전송됨' : '실패'}\n\n파라미터:\n${JSON.stringify(params, null, 2)}`);
12
+ };
13
+
14
+ // 구매 이벤트
15
+ const handlePurchase = () => {
16
+ const params = {
17
+ transaction_id: `tx_${Date.now()}`,
18
+ value: 158000,
19
+ currency: 'KRW',
20
+ payment_method: 'card',
21
+ items: 3,
22
+ category: 'fashion',
23
+ brand: 'Nike'
24
+ };
25
+
26
+ try {
27
+ track('purchase', params);
28
+ showResult('purchase', params);
29
+ } catch (error) {
30
+ showResult('purchase', params, false);
31
+ console.error('Purchase event failed:', error);
32
+ }
33
+ };
34
+
35
+ // 회원가입 이벤트
36
+ const handleSignup = () => {
37
+ const params = {
38
+ user_id: `user_${Date.now()}`,
39
+ signup_method: 'google',
40
+ email_verified: false,
41
+ time_to_complete: 85,
42
+ form_version: 'v3',
43
+ utm_source: 'facebook_ads',
44
+ utm_medium: 'social'
45
+ };
46
+
47
+ try {
48
+ track('signup_complete', params);
49
+ showResult('signup_complete', params);
50
+ } catch (error) {
51
+ showResult('signup_complete', params, false);
52
+ console.error('Signup event failed:', error);
53
+ }
54
+ };
55
+
56
+ // 아이템 조회 이벤트
57
+ const handleViewItem = () => {
58
+ const params = {
59
+ item_id: `prod_${Math.floor(Math.random() * 9999) + 1000}`,
60
+ category: 'electronics',
61
+ brand: 'Apple',
62
+ price: 1290000,
63
+ currency: 'KRW',
64
+ referrer: 'search',
65
+ search_term: 'iPhone 15 Pro'
66
+ };
67
+
68
+ try {
69
+ track('view_item', params);
70
+ showResult('view_item', params);
71
+ } catch (error) {
72
+ showResult('view_item', params, false);
73
+ console.error('ViewItem event failed:', error);
74
+ }
75
+ };
76
+
77
+ // 페이지 로드 시 자동 페이지뷰
78
+ useEffect(() => {
79
+ const timer = setTimeout(() => {
80
+ const params = {
81
+ page: '/',
82
+ title: 'AdStage React Example',
83
+ referrer: 'direct',
84
+ utm_source: 'direct'
85
+ };
86
+ pageView(params);
87
+ showResult('page_view', params);
88
+ }, 1000);
89
+
90
+ return () => clearTimeout(timer);
91
+ }, []);
92
+
93
+ return (
94
+ <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
95
+ <h1>AdStage SDK - React 이벤트 추적 예시</h1>
96
+
97
+ <div>
98
+ <button
99
+ onClick={handlePurchase}
100
+ style={buttonStyle}
101
+ >
102
+ 구매 이벤트 전송
103
+ </button>
104
+
105
+ <button
106
+ onClick={handleSignup}
107
+ style={buttonStyle}
108
+ >
109
+ 회원가입 이벤트 전송
110
+ </button>
111
+
112
+ <button
113
+ onClick={handleViewItem}
114
+ style={buttonStyle}
115
+ >
116
+ 상품조회 이벤트 전송
117
+ </button>
118
+ </div>
119
+
120
+ <div style={resultStyle}>
121
+ {result}
122
+ </div>
123
+ </div>
124
+ );
125
+ }
126
+
127
+ const buttonStyle = {
128
+ padding: '12px 24px',
129
+ margin: '10px',
130
+ fontSize: '16px',
131
+ border: '1px solid #ccc',
132
+ borderRadius: '4px',
133
+ backgroundColor: '#f8f9fa',
134
+ cursor: 'pointer'
135
+ };
136
+
137
+ const resultStyle = {
138
+ marginTop: '20px',
139
+ padding: '15px',
140
+ border: '1px solid #ddd',
141
+ borderRadius: '4px',
142
+ backgroundColor: '#f8f9fa',
143
+ whiteSpace: 'pre-wrap',
144
+ fontFamily: 'monospace'
145
+ };
146
+
147
+ // 메인 앱 컴포넌트
148
+ function App() {
149
+ return (
150
+ <AdStageProvider
151
+ config={{
152
+ apiKey: '7dfddcfda637fbb73225bac3731688b80b90675942fe9f2057a88e2379aba2a4',
153
+ debug: true,
154
+ appVersion: '2.1.0'
155
+ }}
156
+ >
157
+ <EventButtons />
158
+ </AdStageProvider>
159
+ );
160
+ }
161
+
162
+ export default App;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import App from './App';
4
+
5
+ const root = ReactDOM.createRoot(document.getElementById('root'));
6
+ root.render(<App />);
package/src/index.ts CHANGED
@@ -7,6 +7,9 @@
7
7
  export { default as AdStage } from './core/adstage';
8
8
  import AdStageCore from './core/adstage';
9
9
 
10
+ // 전역 이벤트 함수들 (Firebase 스타일)
11
+ export { track, pageView, setUserId, getUserId } from './events/global-events';
12
+
10
13
  // React 통합
11
14
  export { AdStageProvider, useAdStageContext, useAdStageInstance } from './react';
12
15
 
@@ -1,4 +1,6 @@
1
1
  import { DOMUtils } from '../utils/dom-utils';
2
+ import { getSDKVersion } from '../utils/version';
3
+ import { ConfigUtils } from '../utils/config-utils';
2
4
 
3
5
  /**
4
6
  * 디바이스 정보 수집 클래스
@@ -36,16 +38,19 @@ export class DeviceInfoCollector {
36
38
  }
37
39
 
38
40
  /**
39
- * 모바일 디바이스 여부 확인
41
+ * 모바일 디바이스 여부 확인 (SSR 안전)
40
42
  */
41
43
  static isMobile(): boolean {
44
+ if (!DOMUtils.isBrowser()) return false;
42
45
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
43
46
  }
44
47
 
45
48
  /**
46
- * 플랫폼 타입 반환 (서버 enum에 맞춤)
49
+ * 플랫폼 타입 반환 (서버 enum에 맞춤, SSR 안전)
47
50
  */
48
51
  static getPlatform(): 'ios' | 'android' | 'web' | 'desktop' {
52
+ if (!DOMUtils.isBrowser()) return 'web';
53
+
49
54
  const userAgent = navigator.userAgent.toLowerCase();
50
55
 
51
56
  if (/iphone|ipad|ipod/.test(userAgent)) {
@@ -94,8 +99,8 @@ export class DeviceInfoCollector {
94
99
  sessionId: DeviceInfoCollector.generateSessionId(),
95
100
  osVersion: DOMUtils.isBrowser() ? navigator.platform : 'SSR',
96
101
  deviceModel: DOMUtils.isBrowser() ? navigator.platform : 'SSR',
97
- appVersion: '1.0.0',
98
- sdkVersion: '1.0.0',
102
+ appVersion: ConfigUtils.getAppVersion(), // AdStage.init()에서 설정 또는 기본값 '1.0.0'
103
+ sdkVersion: getSDKVersion(), // package.json에서 동적 로드
99
104
  language: DOMUtils.isBrowser() ? (navigator.language || 'ko') : 'ko',
100
105
  country: 'KR', // 기본값
101
106
  ipAddress: '', // 서버에서 자동으로 설정됨
@@ -0,0 +1,106 @@
1
+ /**
2
+ * AdStage SDK - 이벤트용 디바이스 정보 수집기
3
+ * Events API 스키마에 최적화된 디바이스 정보 수집
4
+ * DeviceInfoCollector를 재사용하여 중복 제거
5
+ */
6
+
7
+ import { DOMUtils } from '../../utils/dom-utils';
8
+ import { DeviceInfoCollector } from '../device-info-collector';
9
+ import { getSDKVersion } from '../../utils/version';
10
+ import { ConfigUtils } from '../../utils/config-utils';
11
+
12
+ export class EventDeviceCollector {
13
+ /**
14
+ * Events API용 디바이스 정보 반환
15
+ * TrackEventDto.DeviceInfoInput 형태에 맞춤
16
+ */
17
+ static getDeviceInfo(): {
18
+ category: 'mobile' | 'desktop' | 'tablet' | 'other';
19
+ platform?: string;
20
+ model?: string;
21
+ appVersion?: string;
22
+ osVersion?: string;
23
+ } {
24
+ if (!DOMUtils.isBrowser()) {
25
+ return {
26
+ category: 'other',
27
+ platform: 'SSR',
28
+ model: 'SSR',
29
+ appVersion: ConfigUtils.getAppVersion(), // AdStage.init()에서 설정 또는 기본값 '1.0.0'
30
+ osVersion: 'SSR'
31
+ };
32
+ }
33
+
34
+ const userAgent = navigator.userAgent.toLowerCase();
35
+ let category: 'mobile' | 'desktop' | 'tablet' | 'other' = 'desktop';
36
+
37
+ // 디바이스 카테고리 판별 (DeviceInfoCollector 재사용)
38
+ if (/tablet|ipad/.test(userAgent)) {
39
+ category = 'tablet';
40
+ } else if (DeviceInfoCollector.isMobile()) {
41
+ category = 'mobile';
42
+ }
43
+
44
+ // 플랫폼 정보 매핑 (Events API용)
45
+ const platformType = DeviceInfoCollector.getPlatform();
46
+ const platformString = EventDeviceCollector.mapPlatformForEvents(platformType);
47
+
48
+ return {
49
+ category,
50
+ platform: platformString,
51
+ model: navigator.platform,
52
+ appVersion: ConfigUtils.getAppVersion(), // AdStage.init()에서 설정 또는 기본값 '1.0.0'
53
+ osVersion: navigator.platform
54
+ };
55
+ }
56
+
57
+ /**
58
+ * 플랫폼 타입을 Events API용 문자열로 매핑
59
+ */
60
+ private static mapPlatformForEvents(platformType: 'ios' | 'android' | 'web' | 'desktop'): string {
61
+ switch (platformType) {
62
+ case 'ios': return 'ios';
63
+ case 'android': return 'android';
64
+ case 'web': return 'mobile-web';
65
+ case 'desktop': return 'desktop-web';
66
+ default: return 'unknown';
67
+ }
68
+ }
69
+
70
+ /**
71
+ * 디바이스 상세 정보 (디버깅용)
72
+ */
73
+ static getDetailedInfo(): {
74
+ userAgent: string;
75
+ language: string;
76
+ platform: string;
77
+ cookieEnabled: boolean;
78
+ onLine: boolean;
79
+ screenResolution: string;
80
+ viewportSize: string;
81
+ } {
82
+ if (!DOMUtils.isBrowser()) {
83
+ return {
84
+ userAgent: 'SSR',
85
+ language: 'ko-KR',
86
+ platform: 'SSR',
87
+ cookieEnabled: false,
88
+ onLine: true,
89
+ screenResolution: '0x0',
90
+ viewportSize: '0x0'
91
+ };
92
+ }
93
+
94
+ const viewportInfo = DOMUtils.getViewportInfo();
95
+
96
+ return {
97
+ userAgent: navigator.userAgent,
98
+ language: navigator.language || 'ko-KR',
99
+ platform: navigator.platform,
100
+ cookieEnabled: navigator.cookieEnabled,
101
+ onLine: navigator.onLine,
102
+ screenResolution: `${screen.width}x${screen.height}`,
103
+ viewportSize: `${viewportInfo.width}x${viewportInfo.height}`
104
+ };
105
+ }
106
+ }