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