@adstage/web-sdk 2.6.0 → 2.6.2
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/README.md +1 -1
- package/dist/index.cjs.js +580 -60
- package/dist/index.d.ts +58 -23
- package/dist/index.esm.js +577 -61
- package/dist/index.standalone.js +577 -61
- package/dist/index.umd.js +4776 -0
- package/package.json +4 -2
- package/src/constants/endpoints.ts +0 -2
- package/src/events/global-events.ts +72 -0
- package/src/example/events/README.md +125 -0
- package/src/example/events/javascript/index-esm.html +159 -0
- package/src/example/events/javascript/index.html +138 -0
- package/src/example/events/nextjs/README.md +54 -0
- package/src/example/events/nextjs/next.config.js +8 -0
- package/src/example/events/nextjs/package.json +16 -0
- package/src/example/events/nextjs/pages/_app.js +15 -0
- package/src/example/events/nextjs/pages/index.js +139 -0
- package/src/example/events/react/.env +0 -0
- package/src/example/events/react/README.md +57 -0
- package/src/example/events/react/package.json +27 -0
- package/src/example/events/react/public/index.html +11 -0
- package/src/example/events/react/src/App.js +162 -0
- package/src/example/events/react/src/index.js +6 -0
- package/src/index.ts +3 -0
- package/src/managers/device-info-collector.ts +9 -4
- package/src/managers/events/event-device-collector.ts +106 -0
- package/src/managers/events/event-session-manager.ts +183 -0
- package/src/managers/events/event-user-collector.ts +166 -0
- package/src/modules/events/events-module.ts +95 -42
- package/src/types/config.ts +3 -0
- package/src/utils/config-utils.ts +69 -0
package/dist/index.standalone.js
CHANGED
|
@@ -314,6 +314,80 @@ class DOMUtils {
|
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
/**
|
|
318
|
+
* AdStage SDK - 버전 정보 유틸리티
|
|
319
|
+
*/
|
|
320
|
+
// package.json에서 버전 정보 가져오기 (빌드 시 자동으로 교체됨)
|
|
321
|
+
const SDK_VERSION$1 = '"2.6.2"';
|
|
322
|
+
/**
|
|
323
|
+
* SDK 버전 정보 반환
|
|
324
|
+
*/
|
|
325
|
+
function getSDKVersion() {
|
|
326
|
+
return SDK_VERSION$1;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* AdStage SDK - 설정 유틸리티
|
|
331
|
+
* 전역 설정에 대한 접근 기능 제공
|
|
332
|
+
*/
|
|
333
|
+
class ConfigUtils {
|
|
334
|
+
/**
|
|
335
|
+
* AdStage 전역 설정 반환
|
|
336
|
+
*/
|
|
337
|
+
static getConfig() {
|
|
338
|
+
// AdStage 클래스 동적 임포트로 순환 참조 방지
|
|
339
|
+
try {
|
|
340
|
+
const { AdStage } = require('../core/adstage');
|
|
341
|
+
return AdStage.getConfig();
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* 고객 앱 버전 반환
|
|
349
|
+
*/
|
|
350
|
+
static getAppVersion() {
|
|
351
|
+
const config = ConfigUtils.getConfig();
|
|
352
|
+
return config?.appVersion || '1.0.0';
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* 디버그 모드 여부 확인
|
|
356
|
+
*/
|
|
357
|
+
static isDebugMode() {
|
|
358
|
+
const config = ConfigUtils.getConfig();
|
|
359
|
+
return config?.debug || false;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* API 키 반환
|
|
363
|
+
*/
|
|
364
|
+
static getApiKey() {
|
|
365
|
+
const config = ConfigUtils.getConfig();
|
|
366
|
+
return config?.apiKey;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* 타임아웃 값 반환
|
|
370
|
+
*/
|
|
371
|
+
static getTimeout() {
|
|
372
|
+
const config = ConfigUtils.getConfig();
|
|
373
|
+
return config?.timeout || 30000;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* 플레이스홀더 모드 반환
|
|
377
|
+
*/
|
|
378
|
+
static getPlaceholderMode() {
|
|
379
|
+
const config = ConfigUtils.getConfig();
|
|
380
|
+
return config?.placeholderMode || 'subtle';
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* 활성화된 모듈 목록 반환
|
|
384
|
+
*/
|
|
385
|
+
static getEnabledModules() {
|
|
386
|
+
const config = ConfigUtils.getConfig();
|
|
387
|
+
return config?.modules || ['ads', 'events', 'config'];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
317
391
|
/**
|
|
318
392
|
* 디바이스 정보 수집 클래스
|
|
319
393
|
* - 브라우저 환경 정보 수집
|
|
@@ -348,15 +422,19 @@ class DeviceInfoCollector {
|
|
|
348
422
|
return sessionId;
|
|
349
423
|
}
|
|
350
424
|
/**
|
|
351
|
-
* 모바일 디바이스 여부 확인
|
|
425
|
+
* 모바일 디바이스 여부 확인 (SSR 안전)
|
|
352
426
|
*/
|
|
353
427
|
static isMobile() {
|
|
428
|
+
if (!DOMUtils.isBrowser())
|
|
429
|
+
return false;
|
|
354
430
|
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
355
431
|
}
|
|
356
432
|
/**
|
|
357
|
-
* 플랫폼 타입 반환 (서버 enum에
|
|
433
|
+
* 플랫폼 타입 반환 (서버 enum에 맞춤, SSR 안전)
|
|
358
434
|
*/
|
|
359
435
|
static getPlatform() {
|
|
436
|
+
if (!DOMUtils.isBrowser())
|
|
437
|
+
return 'web';
|
|
360
438
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
361
439
|
if (/iphone|ipad|ipod/.test(userAgent)) {
|
|
362
440
|
return 'ios';
|
|
@@ -379,8 +457,8 @@ class DeviceInfoCollector {
|
|
|
379
457
|
sessionId: DeviceInfoCollector.generateSessionId(),
|
|
380
458
|
osVersion: DOMUtils.isBrowser() ? navigator.platform : 'SSR',
|
|
381
459
|
deviceModel: DOMUtils.isBrowser() ? navigator.platform : 'SSR',
|
|
382
|
-
appVersion: '1.0.0'
|
|
383
|
-
sdkVersion:
|
|
460
|
+
appVersion: ConfigUtils.getAppVersion(), // AdStage.init()에서 설정 또는 기본값 '1.0.0'
|
|
461
|
+
sdkVersion: getSDKVersion(), // package.json에서 동적 로드
|
|
384
462
|
language: DOMUtils.isBrowser() ? (navigator.language || 'ko') : 'ko',
|
|
385
463
|
country: 'KR', // 기본값
|
|
386
464
|
ipAddress: '', // 서버에서 자동으로 설정됨
|
|
@@ -449,18 +527,6 @@ class ApiHeaders {
|
|
|
449
527
|
}
|
|
450
528
|
}
|
|
451
529
|
|
|
452
|
-
/**
|
|
453
|
-
* AdStage SDK - 버전 정보 유틸리티
|
|
454
|
-
*/
|
|
455
|
-
// package.json에서 버전 정보 가져오기 (빌드 시 자동으로 교체됨)
|
|
456
|
-
const SDK_VERSION$1 = '"2.6.0"';
|
|
457
|
-
/**
|
|
458
|
-
* SDK 버전 정보 반환
|
|
459
|
-
*/
|
|
460
|
-
function getSDKVersion() {
|
|
461
|
-
return SDK_VERSION$1;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
530
|
/**
|
|
465
531
|
* 광고 이벤트 추적 관리 클래스
|
|
466
532
|
* - 광고 전용 이벤트 추적 및 전송
|
|
@@ -652,7 +718,6 @@ const API_PATHS = {
|
|
|
652
718
|
/** 이벤트 관련 */
|
|
653
719
|
events: {
|
|
654
720
|
track: '/events/track',
|
|
655
|
-
batch: '/events/batch'
|
|
656
721
|
}
|
|
657
722
|
};
|
|
658
723
|
/**
|
|
@@ -673,7 +738,6 @@ class EndpointBuilder {
|
|
|
673
738
|
*/
|
|
674
739
|
this.events = {
|
|
675
740
|
track: () => `${this.baseUrl}${API_PATHS.events.track}`,
|
|
676
|
-
batch: () => `${this.baseUrl}${API_PATHS.events.batch}`
|
|
677
741
|
};
|
|
678
742
|
// 기본값은 베타 환경 사용
|
|
679
743
|
this.baseUrl = baseUrl || API_ENDPOINTS.production;
|
|
@@ -3863,31 +3927,358 @@ class ConfigModule {
|
|
|
3863
3927
|
}
|
|
3864
3928
|
|
|
3865
3929
|
/**
|
|
3866
|
-
* AdStage SDK -
|
|
3867
|
-
*
|
|
3930
|
+
* AdStage SDK - 이벤트용 디바이스 정보 수집기
|
|
3931
|
+
* Events API 스키마에 최적화된 디바이스 정보 수집
|
|
3932
|
+
* DeviceInfoCollector를 재사용하여 중복 제거
|
|
3933
|
+
*/
|
|
3934
|
+
class EventDeviceCollector {
|
|
3935
|
+
/**
|
|
3936
|
+
* Events API용 디바이스 정보 반환
|
|
3937
|
+
* TrackEventDto.DeviceInfoInput 형태에 맞춤
|
|
3938
|
+
*/
|
|
3939
|
+
static getDeviceInfo() {
|
|
3940
|
+
if (!DOMUtils.isBrowser()) {
|
|
3941
|
+
return {
|
|
3942
|
+
category: 'other',
|
|
3943
|
+
platform: 'SSR',
|
|
3944
|
+
model: 'SSR',
|
|
3945
|
+
appVersion: ConfigUtils.getAppVersion(), // AdStage.init()에서 설정 또는 기본값 '1.0.0'
|
|
3946
|
+
osVersion: 'SSR'
|
|
3947
|
+
};
|
|
3948
|
+
}
|
|
3949
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
3950
|
+
let category = 'desktop';
|
|
3951
|
+
// 디바이스 카테고리 판별 (DeviceInfoCollector 재사용)
|
|
3952
|
+
if (/tablet|ipad/.test(userAgent)) {
|
|
3953
|
+
category = 'tablet';
|
|
3954
|
+
}
|
|
3955
|
+
else if (DeviceInfoCollector.isMobile()) {
|
|
3956
|
+
category = 'mobile';
|
|
3957
|
+
}
|
|
3958
|
+
// 플랫폼 정보 매핑 (Events API용)
|
|
3959
|
+
const platformType = DeviceInfoCollector.getPlatform();
|
|
3960
|
+
const platformString = EventDeviceCollector.mapPlatformForEvents(platformType);
|
|
3961
|
+
return {
|
|
3962
|
+
category,
|
|
3963
|
+
platform: platformString,
|
|
3964
|
+
model: navigator.platform,
|
|
3965
|
+
appVersion: ConfigUtils.getAppVersion(), // AdStage.init()에서 설정 또는 기본값 '1.0.0'
|
|
3966
|
+
osVersion: navigator.platform
|
|
3967
|
+
};
|
|
3968
|
+
}
|
|
3969
|
+
/**
|
|
3970
|
+
* 플랫폼 타입을 Events API용 문자열로 매핑
|
|
3971
|
+
*/
|
|
3972
|
+
static mapPlatformForEvents(platformType) {
|
|
3973
|
+
switch (platformType) {
|
|
3974
|
+
case 'ios': return 'ios';
|
|
3975
|
+
case 'android': return 'android';
|
|
3976
|
+
case 'web': return 'mobile-web';
|
|
3977
|
+
case 'desktop': return 'desktop-web';
|
|
3978
|
+
default: return 'unknown';
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
/**
|
|
3982
|
+
* 디바이스 상세 정보 (디버깅용)
|
|
3983
|
+
*/
|
|
3984
|
+
static getDetailedInfo() {
|
|
3985
|
+
if (!DOMUtils.isBrowser()) {
|
|
3986
|
+
return {
|
|
3987
|
+
userAgent: 'SSR',
|
|
3988
|
+
language: 'ko-KR',
|
|
3989
|
+
platform: 'SSR',
|
|
3990
|
+
cookieEnabled: false,
|
|
3991
|
+
onLine: true,
|
|
3992
|
+
screenResolution: '0x0',
|
|
3993
|
+
viewportSize: '0x0'
|
|
3994
|
+
};
|
|
3995
|
+
}
|
|
3996
|
+
const viewportInfo = DOMUtils.getViewportInfo();
|
|
3997
|
+
return {
|
|
3998
|
+
userAgent: navigator.userAgent,
|
|
3999
|
+
language: navigator.language || 'ko-KR',
|
|
4000
|
+
platform: navigator.platform,
|
|
4001
|
+
cookieEnabled: navigator.cookieEnabled,
|
|
4002
|
+
onLine: navigator.onLine,
|
|
4003
|
+
screenResolution: `${screen.width}x${screen.height}`,
|
|
4004
|
+
viewportSize: `${viewportInfo.width}x${viewportInfo.height}`
|
|
4005
|
+
};
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4009
|
+
/**
|
|
4010
|
+
* AdStage SDK - 이벤트용 사용자 정보 수집기
|
|
4011
|
+
* Events API 스키마에 최적화된 사용자 정보 수집
|
|
4012
|
+
*/
|
|
4013
|
+
class EventUserCollector {
|
|
4014
|
+
/**
|
|
4015
|
+
* Events API용 사용자 정보 반환
|
|
4016
|
+
* TrackEventDto.UserAttributesInput 형태에 맞춤
|
|
4017
|
+
*/
|
|
4018
|
+
static getUserInfo() {
|
|
4019
|
+
const baseInfo = EventUserCollector.getBaseUserInfo();
|
|
4020
|
+
// 설정된 사용자 속성과 병합
|
|
4021
|
+
return {
|
|
4022
|
+
...baseInfo,
|
|
4023
|
+
...EventUserCollector._userProperties
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4026
|
+
/**
|
|
4027
|
+
* 기본 사용자 정보 수집 (브라우저 기반)
|
|
4028
|
+
*/
|
|
4029
|
+
static getBaseUserInfo() {
|
|
4030
|
+
if (!DOMUtils.isBrowser()) {
|
|
4031
|
+
return {
|
|
4032
|
+
language: 'ko-KR',
|
|
4033
|
+
country: 'KR'
|
|
4034
|
+
};
|
|
4035
|
+
}
|
|
4036
|
+
// 브라우저 언어 설정에서 국가 추출
|
|
4037
|
+
const language = navigator.language || 'ko-KR';
|
|
4038
|
+
const country = EventUserCollector.extractCountryFromLanguage(language);
|
|
4039
|
+
return {
|
|
4040
|
+
language,
|
|
4041
|
+
country
|
|
4042
|
+
};
|
|
4043
|
+
}
|
|
4044
|
+
/**
|
|
4045
|
+
* 언어 코드에서 국가 추출
|
|
4046
|
+
*/
|
|
4047
|
+
static extractCountryFromLanguage(language) {
|
|
4048
|
+
const countryMap = {
|
|
4049
|
+
'ko': 'KR',
|
|
4050
|
+
'ko-KR': 'KR',
|
|
4051
|
+
'en': 'US',
|
|
4052
|
+
'en-US': 'US',
|
|
4053
|
+
'en-GB': 'GB',
|
|
4054
|
+
'ja': 'JP',
|
|
4055
|
+
'ja-JP': 'JP',
|
|
4056
|
+
'zh': 'CN',
|
|
4057
|
+
'zh-CN': 'CN',
|
|
4058
|
+
'zh-TW': 'TW',
|
|
4059
|
+
'de': 'DE',
|
|
4060
|
+
'de-DE': 'DE',
|
|
4061
|
+
'fr': 'FR',
|
|
4062
|
+
'fr-FR': 'FR'
|
|
4063
|
+
};
|
|
4064
|
+
// 정확한 매칭 시도
|
|
4065
|
+
if (countryMap[language]) {
|
|
4066
|
+
return countryMap[language];
|
|
4067
|
+
}
|
|
4068
|
+
// 언어 코드만 추출해서 매칭
|
|
4069
|
+
const langCode = language.split('-')[0];
|
|
4070
|
+
return countryMap[langCode] || 'KR';
|
|
4071
|
+
}
|
|
4072
|
+
/**
|
|
4073
|
+
* 사용자 속성 설정
|
|
4074
|
+
*/
|
|
4075
|
+
static setUserProperties(properties) {
|
|
4076
|
+
EventUserCollector._userProperties = {
|
|
4077
|
+
...EventUserCollector._userProperties,
|
|
4078
|
+
...properties
|
|
4079
|
+
};
|
|
4080
|
+
}
|
|
4081
|
+
/**
|
|
4082
|
+
* 특정 사용자 속성 설정
|
|
4083
|
+
*/
|
|
4084
|
+
static setUserProperty(key, value) {
|
|
4085
|
+
EventUserCollector._userProperties[key] = value;
|
|
4086
|
+
}
|
|
4087
|
+
/**
|
|
4088
|
+
* 사용자 속성 초기화
|
|
4089
|
+
*/
|
|
4090
|
+
static clearUserProperties() {
|
|
4091
|
+
EventUserCollector._userProperties = {};
|
|
4092
|
+
}
|
|
4093
|
+
/**
|
|
4094
|
+
* 현재 설정된 사용자 속성 반환
|
|
4095
|
+
*/
|
|
4096
|
+
static getCurrentUserProperties() {
|
|
4097
|
+
return { ...EventUserCollector._userProperties };
|
|
4098
|
+
}
|
|
4099
|
+
/**
|
|
4100
|
+
* 사용자 지역 정보 추정 (타임존 기반)
|
|
4101
|
+
*/
|
|
4102
|
+
static estimateLocation() {
|
|
4103
|
+
if (!DOMUtils.isBrowser()) {
|
|
4104
|
+
return {
|
|
4105
|
+
timezone: 'UTC',
|
|
4106
|
+
estimatedCountry: 'KR'
|
|
4107
|
+
};
|
|
4108
|
+
}
|
|
4109
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
4110
|
+
// 타임존 기반 국가 추정
|
|
4111
|
+
const timezoneCountryMap = {
|
|
4112
|
+
'Asia/Seoul': 'KR',
|
|
4113
|
+
'Asia/Tokyo': 'JP',
|
|
4114
|
+
'Asia/Shanghai': 'CN',
|
|
4115
|
+
'Asia/Hong_Kong': 'HK',
|
|
4116
|
+
'Asia/Taipei': 'TW',
|
|
4117
|
+
'America/New_York': 'US',
|
|
4118
|
+
'America/Los_Angeles': 'US',
|
|
4119
|
+
'Europe/London': 'GB',
|
|
4120
|
+
'Europe/Berlin': 'DE',
|
|
4121
|
+
'Europe/Paris': 'FR'
|
|
4122
|
+
};
|
|
4123
|
+
return {
|
|
4124
|
+
timezone,
|
|
4125
|
+
estimatedCountry: timezoneCountryMap[timezone] || 'KR'
|
|
4126
|
+
};
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
EventUserCollector._userProperties = {};
|
|
4130
|
+
|
|
4131
|
+
/**
|
|
4132
|
+
* AdStage SDK - 이벤트용 세션 관리자
|
|
4133
|
+
* 세션 ID 생성 및 사용자 ID 관리
|
|
4134
|
+
*/
|
|
4135
|
+
class EventSessionManager {
|
|
4136
|
+
/**
|
|
4137
|
+
* 세션 ID 생성 및 반환 (SSR 안전)
|
|
4138
|
+
*/
|
|
4139
|
+
static getSessionId() {
|
|
4140
|
+
if (!DOMUtils.isBrowser()) {
|
|
4141
|
+
return 'ssr_session_' + Date.now();
|
|
4142
|
+
}
|
|
4143
|
+
const stored = sessionStorage.getItem('adstage_session_id');
|
|
4144
|
+
if (stored) {
|
|
4145
|
+
return stored;
|
|
4146
|
+
}
|
|
4147
|
+
const sessionId = 'session_' + Math.random().toString(36).substr(2, 9) + '_' + Date.now();
|
|
4148
|
+
sessionStorage.setItem('adstage_session_id', sessionId);
|
|
4149
|
+
// 세션 시작 시간 기록
|
|
4150
|
+
if (!EventSessionManager._sessionStartTime) {
|
|
4151
|
+
EventSessionManager._sessionStartTime = Date.now();
|
|
4152
|
+
if (DOMUtils.isBrowser()) {
|
|
4153
|
+
sessionStorage.setItem('adstage_session_start', String(EventSessionManager._sessionStartTime));
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
return sessionId;
|
|
4157
|
+
}
|
|
4158
|
+
/**
|
|
4159
|
+
* 사용자 ID 설정
|
|
4160
|
+
*/
|
|
4161
|
+
static setUserId(userId) {
|
|
4162
|
+
EventSessionManager._userId = userId;
|
|
4163
|
+
if (DOMUtils.isBrowser()) {
|
|
4164
|
+
localStorage.setItem('adstage_user_id', userId);
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
/**
|
|
4168
|
+
* 현재 사용자 ID 반환
|
|
4169
|
+
*/
|
|
4170
|
+
static getUserId() {
|
|
4171
|
+
// 메모리에 있는 값 우선 반환
|
|
4172
|
+
if (EventSessionManager._userId) {
|
|
4173
|
+
return EventSessionManager._userId;
|
|
4174
|
+
}
|
|
4175
|
+
// 로컬 스토리지에서 복원
|
|
4176
|
+
if (DOMUtils.isBrowser()) {
|
|
4177
|
+
const stored = localStorage.getItem('adstage_user_id');
|
|
4178
|
+
if (stored) {
|
|
4179
|
+
EventSessionManager._userId = stored;
|
|
4180
|
+
return stored;
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
return undefined;
|
|
4184
|
+
}
|
|
4185
|
+
/**
|
|
4186
|
+
* 사용자 ID 제거
|
|
4187
|
+
*/
|
|
4188
|
+
static clearUserId() {
|
|
4189
|
+
EventSessionManager._userId = undefined;
|
|
4190
|
+
if (DOMUtils.isBrowser()) {
|
|
4191
|
+
localStorage.removeItem('adstage_user_id');
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
/**
|
|
4195
|
+
* 세션 정보 전체 반환
|
|
4196
|
+
*/
|
|
4197
|
+
static getSessionInfo() {
|
|
4198
|
+
const sessionId = EventSessionManager.getSessionId();
|
|
4199
|
+
const userId = EventSessionManager.getUserId();
|
|
4200
|
+
const isNewSession = EventSessionManager.isNewSession();
|
|
4201
|
+
const sessionDuration = EventSessionManager.getSessionDuration();
|
|
4202
|
+
return {
|
|
4203
|
+
sessionId,
|
|
4204
|
+
userId,
|
|
4205
|
+
sessionDuration,
|
|
4206
|
+
isNewSession
|
|
4207
|
+
};
|
|
4208
|
+
}
|
|
4209
|
+
/**
|
|
4210
|
+
* 새 세션인지 확인
|
|
4211
|
+
*/
|
|
4212
|
+
static isNewSession() {
|
|
4213
|
+
if (!DOMUtils.isBrowser())
|
|
4214
|
+
return true;
|
|
4215
|
+
const sessionId = sessionStorage.getItem('adstage_session_id');
|
|
4216
|
+
const sessionStart = sessionStorage.getItem('adstage_session_start');
|
|
4217
|
+
return !sessionId || !sessionStart;
|
|
4218
|
+
}
|
|
4219
|
+
/**
|
|
4220
|
+
* 세션 지속 시간 반환 (ms)
|
|
4221
|
+
*/
|
|
4222
|
+
static getSessionDuration() {
|
|
4223
|
+
if (!DOMUtils.isBrowser())
|
|
4224
|
+
return 0;
|
|
4225
|
+
const sessionStart = sessionStorage.getItem('adstage_session_start');
|
|
4226
|
+
if (!sessionStart)
|
|
4227
|
+
return 0;
|
|
4228
|
+
return Date.now() - parseInt(sessionStart, 10);
|
|
4229
|
+
}
|
|
4230
|
+
/**
|
|
4231
|
+
* 세션 새로고침 (새로운 세션 ID 생성)
|
|
4232
|
+
*/
|
|
4233
|
+
static refreshSession() {
|
|
4234
|
+
if (DOMUtils.isBrowser()) {
|
|
4235
|
+
sessionStorage.removeItem('adstage_session_id');
|
|
4236
|
+
sessionStorage.removeItem('adstage_session_start');
|
|
4237
|
+
}
|
|
4238
|
+
EventSessionManager._sessionStartTime = undefined;
|
|
4239
|
+
return EventSessionManager.getSessionId();
|
|
4240
|
+
}
|
|
4241
|
+
/**
|
|
4242
|
+
* 세션 만료 확인 (24시간 기준)
|
|
4243
|
+
*/
|
|
4244
|
+
static isSessionExpired(maxDurationHours = 24) {
|
|
4245
|
+
const duration = EventSessionManager.getSessionDuration();
|
|
4246
|
+
const maxDuration = maxDurationHours * 60 * 60 * 1000; // ms 변환
|
|
4247
|
+
return duration > maxDuration;
|
|
4248
|
+
}
|
|
4249
|
+
/**
|
|
4250
|
+
* 세션 통계 반환 (디버깅용)
|
|
4251
|
+
*/
|
|
4252
|
+
static getSessionStats() {
|
|
4253
|
+
const sessionId = EventSessionManager.getSessionId();
|
|
4254
|
+
const userId = EventSessionManager.getUserId();
|
|
4255
|
+
const duration = EventSessionManager.getSessionDuration();
|
|
4256
|
+
const isExpired = EventSessionManager.isSessionExpired();
|
|
4257
|
+
const isNewSession = EventSessionManager.isNewSession();
|
|
4258
|
+
let startTime;
|
|
4259
|
+
if (DOMUtils.isBrowser()) {
|
|
4260
|
+
const stored = sessionStorage.getItem('adstage_session_start');
|
|
4261
|
+
startTime = stored ? parseInt(stored, 10) : undefined;
|
|
4262
|
+
}
|
|
4263
|
+
return {
|
|
4264
|
+
sessionId,
|
|
4265
|
+
userId,
|
|
4266
|
+
startTime,
|
|
4267
|
+
duration,
|
|
4268
|
+
isExpired,
|
|
4269
|
+
isNewSession
|
|
4270
|
+
};
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
|
|
4274
|
+
/**
|
|
4275
|
+
* AdStage SDK - Events 모듈
|
|
4276
|
+
* 이벤트 추적 시스템
|
|
3868
4277
|
*/
|
|
3869
4278
|
class EventsModule {
|
|
3870
4279
|
constructor() {
|
|
3871
4280
|
this._isReady = false;
|
|
3872
4281
|
this._config = null;
|
|
3873
|
-
// === 배치 처리 (향후 구현) ===
|
|
3874
|
-
this.batch = {
|
|
3875
|
-
start: () => {
|
|
3876
|
-
console.log('🚧 [TODO] Batch events start');
|
|
3877
|
-
},
|
|
3878
|
-
add: (eventName, properties) => {
|
|
3879
|
-
console.log('🚧 [TODO] Batch events add:', { eventName, properties });
|
|
3880
|
-
},
|
|
3881
|
-
flush: async () => {
|
|
3882
|
-
console.log('🚧 [TODO] Batch events flush');
|
|
3883
|
-
}
|
|
3884
|
-
};
|
|
3885
|
-
// === 실시간 이벤트 (향후 구현) ===
|
|
3886
|
-
this.realtime = {
|
|
3887
|
-
track: async (eventName, properties) => {
|
|
3888
|
-
console.log('🚧 [TODO] Realtime event tracking:', { eventName, properties });
|
|
3889
|
-
}
|
|
3890
|
-
};
|
|
3891
4282
|
}
|
|
3892
4283
|
/**
|
|
3893
4284
|
* Events 모듈 초기화 (동기)
|
|
@@ -3896,7 +4287,7 @@ class EventsModule {
|
|
|
3896
4287
|
this._config = config;
|
|
3897
4288
|
this._isReady = true;
|
|
3898
4289
|
if (config.debug) {
|
|
3899
|
-
console.log('📊 Events module initialized
|
|
4290
|
+
console.log('📊 Events module initialized');
|
|
3900
4291
|
}
|
|
3901
4292
|
}
|
|
3902
4293
|
/**
|
|
@@ -3911,38 +4302,101 @@ class EventsModule {
|
|
|
3911
4302
|
getConfig() {
|
|
3912
4303
|
return this._config;
|
|
3913
4304
|
}
|
|
3914
|
-
// === 향후 구현 예정 메소드들 ===
|
|
3915
4305
|
/**
|
|
3916
|
-
*
|
|
3917
|
-
* @example AdStage.events.track('page_view', { page: '/products' })
|
|
4306
|
+
* 사용자 ID 설정
|
|
3918
4307
|
*/
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
4308
|
+
setUserId(userId) {
|
|
4309
|
+
EventSessionManager.setUserId(userId);
|
|
4310
|
+
if (this._config?.debug) {
|
|
4311
|
+
console.log('👤 User ID set:', userId);
|
|
4312
|
+
}
|
|
3922
4313
|
}
|
|
3923
4314
|
/**
|
|
3924
|
-
*
|
|
3925
|
-
* @example AdStage.events.pageView({ page: '/home', title: 'Homepage' })
|
|
4315
|
+
* 현재 사용자 ID 반환
|
|
3926
4316
|
*/
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
// TODO: Q1 2025 구현 예정
|
|
4317
|
+
getUserId() {
|
|
4318
|
+
return EventSessionManager.getUserId();
|
|
3930
4319
|
}
|
|
3931
4320
|
/**
|
|
3932
|
-
*
|
|
3933
|
-
* @
|
|
4321
|
+
* 이벤트 추적
|
|
4322
|
+
* @param eventName 이벤트 이름
|
|
4323
|
+
* @param properties 이벤트 속성
|
|
3934
4324
|
*/
|
|
3935
|
-
async
|
|
3936
|
-
|
|
3937
|
-
|
|
4325
|
+
async track(eventName, properties) {
|
|
4326
|
+
if (!this._isReady) {
|
|
4327
|
+
console.warn('Events module not initialized. Call AdStage.init() first.');
|
|
4328
|
+
return;
|
|
4329
|
+
}
|
|
4330
|
+
if (!this._config?.apiKey) {
|
|
4331
|
+
console.warn('API key not configured for event tracking.');
|
|
4332
|
+
return;
|
|
4333
|
+
}
|
|
4334
|
+
try {
|
|
4335
|
+
const eventData = {
|
|
4336
|
+
eventName,
|
|
4337
|
+
userId: EventSessionManager.getUserId(),
|
|
4338
|
+
sessionId: EventSessionManager.getSessionId(),
|
|
4339
|
+
device: EventDeviceCollector.getDeviceInfo(),
|
|
4340
|
+
user: EventUserCollector.getUserInfo(),
|
|
4341
|
+
params: properties || {}
|
|
4342
|
+
};
|
|
4343
|
+
await this.sendEventToServer(eventData);
|
|
4344
|
+
if (this._config.debug) {
|
|
4345
|
+
console.log('✅ Event tracked:', eventName, properties);
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
catch (error) {
|
|
4349
|
+
console.error('❌ Failed to track event:', error);
|
|
4350
|
+
if (this._config.debug) {
|
|
4351
|
+
console.error('Event data:', { eventName, properties });
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
3938
4354
|
}
|
|
3939
4355
|
/**
|
|
3940
|
-
*
|
|
3941
|
-
* @example AdStage.events.conversion({ type: 'purchase', value: 99.99 })
|
|
4356
|
+
* 페이지 뷰 이벤트 (track의 편의 메소드)
|
|
3942
4357
|
*/
|
|
3943
|
-
async
|
|
3944
|
-
|
|
3945
|
-
|
|
4358
|
+
async pageView(pageData) {
|
|
4359
|
+
const properties = {};
|
|
4360
|
+
if (pageData?.page)
|
|
4361
|
+
properties.page = pageData.page;
|
|
4362
|
+
if (pageData?.title)
|
|
4363
|
+
properties.title = pageData.title;
|
|
4364
|
+
if (pageData?.category)
|
|
4365
|
+
properties.category = pageData.category;
|
|
4366
|
+
// pageData의 다른 속성들도 포함
|
|
4367
|
+
if (pageData) {
|
|
4368
|
+
Object.keys(pageData).forEach(key => {
|
|
4369
|
+
if (key !== 'page' && key !== 'title' && key !== 'category') {
|
|
4370
|
+
properties[key] = pageData[key];
|
|
4371
|
+
}
|
|
4372
|
+
});
|
|
4373
|
+
}
|
|
4374
|
+
// 현재 페이지 정보 자동 수집
|
|
4375
|
+
if (typeof window !== 'undefined') {
|
|
4376
|
+
if (!properties.page)
|
|
4377
|
+
properties.page = window.location.pathname;
|
|
4378
|
+
if (!properties.title)
|
|
4379
|
+
properties.title = document.title;
|
|
4380
|
+
properties.url = window.location.href;
|
|
4381
|
+
properties.referrer = document.referrer;
|
|
4382
|
+
}
|
|
4383
|
+
await this.track('page_view', properties);
|
|
4384
|
+
}
|
|
4385
|
+
/**
|
|
4386
|
+
* 서버에 이벤트 전송
|
|
4387
|
+
*/
|
|
4388
|
+
async sendEventToServer(eventData) {
|
|
4389
|
+
const response = await fetch(endpoints.events.track(), {
|
|
4390
|
+
method: 'POST',
|
|
4391
|
+
headers: {
|
|
4392
|
+
...ApiHeaders.create(this._config.apiKey),
|
|
4393
|
+
'Content-Type': 'application/json'
|
|
4394
|
+
},
|
|
4395
|
+
body: JSON.stringify(eventData)
|
|
4396
|
+
});
|
|
4397
|
+
if (!response.ok) {
|
|
4398
|
+
throw new Error(`Event tracking failed: ${response.status} ${response.statusText}`);
|
|
4399
|
+
}
|
|
3946
4400
|
}
|
|
3947
4401
|
}
|
|
3948
4402
|
|
|
@@ -4075,6 +4529,68 @@ AdStage.debug = {
|
|
|
4075
4529
|
}
|
|
4076
4530
|
};
|
|
4077
4531
|
|
|
4532
|
+
/**
|
|
4533
|
+
* AdStage SDK - 전역 이벤트 함수들
|
|
4534
|
+
* Firebase Analytics와 유사한 간단한 API 제공
|
|
4535
|
+
*/
|
|
4536
|
+
/**
|
|
4537
|
+
* 이벤트 추적 (메인 함수)
|
|
4538
|
+
* @param eventName 이벤트 이름
|
|
4539
|
+
* @param properties 이벤트 속성
|
|
4540
|
+
*
|
|
4541
|
+
* @example
|
|
4542
|
+
* track('purchase', {
|
|
4543
|
+
* transaction_id: 'T123',
|
|
4544
|
+
* value: 99.99,
|
|
4545
|
+
* currency: 'USD'
|
|
4546
|
+
* });
|
|
4547
|
+
*/
|
|
4548
|
+
function track(eventName, properties) {
|
|
4549
|
+
if (!AdStage.isReady()) {
|
|
4550
|
+
console.warn('AdStage not initialized. Call AdStage.init() first.');
|
|
4551
|
+
return Promise.resolve();
|
|
4552
|
+
}
|
|
4553
|
+
return AdStage.events.track(eventName, properties);
|
|
4554
|
+
}
|
|
4555
|
+
/**
|
|
4556
|
+
* 페이지 뷰 추적
|
|
4557
|
+
* @param pageData 페이지 정보
|
|
4558
|
+
*
|
|
4559
|
+
* @example
|
|
4560
|
+
* pageView({ page: '/products', title: 'Products Page' });
|
|
4561
|
+
*/
|
|
4562
|
+
function pageView(pageData) {
|
|
4563
|
+
if (!AdStage.isReady()) {
|
|
4564
|
+
console.warn('AdStage not initialized. Call AdStage.init() first.');
|
|
4565
|
+
return Promise.resolve();
|
|
4566
|
+
}
|
|
4567
|
+
return AdStage.events.pageView(pageData);
|
|
4568
|
+
}
|
|
4569
|
+
/**
|
|
4570
|
+
* 사용자 ID 설정
|
|
4571
|
+
* @param userId 사용자 ID
|
|
4572
|
+
*
|
|
4573
|
+
* @example
|
|
4574
|
+
* setUserId('user123');
|
|
4575
|
+
*/
|
|
4576
|
+
function setUserId(userId) {
|
|
4577
|
+
if (!AdStage.isReady()) {
|
|
4578
|
+
console.warn('AdStage not initialized. Call AdStage.init() first.');
|
|
4579
|
+
return;
|
|
4580
|
+
}
|
|
4581
|
+
AdStage.events.setUserId(userId);
|
|
4582
|
+
}
|
|
4583
|
+
/**
|
|
4584
|
+
* 현재 사용자 ID 반환
|
|
4585
|
+
*/
|
|
4586
|
+
function getUserId() {
|
|
4587
|
+
if (!AdStage.isReady()) {
|
|
4588
|
+
console.warn('AdStage not initialized. Call AdStage.init() first.');
|
|
4589
|
+
return undefined;
|
|
4590
|
+
}
|
|
4591
|
+
return AdStage.events.getUserId();
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4078
4594
|
var jsxRuntime = {exports: {}};
|
|
4079
4595
|
|
|
4080
4596
|
var reactJsxRuntime_production_min = {};
|
|
@@ -4240,4 +4756,4 @@ if (typeof window !== 'undefined') {
|
|
|
4240
4756
|
window.AdStage = AdStage;
|
|
4241
4757
|
}
|
|
4242
4758
|
|
|
4243
|
-
export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStageContext, useAdStageInstance };
|
|
4759
|
+
export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, getUserId, pageView, setUserId, track, useAdStageContext, useAdStageInstance };
|