@adstage/web-sdk 2.6.1 → 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.
@@ -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: '1.0.0',
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.1"';
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 - Events 모듈 (기본 구조)
3867
- * 이벤트 추적 시스템 - Q1 2025 구현 예정
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 (sync mode)');
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
- async track(eventName, properties) {
3920
- console.log('🚧 [TODO] Event tracking:', { eventName, properties });
3921
- // TODO: Q1 2025 구현 예정
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
- async pageView(pageData) {
3928
- console.log('🚧 [TODO] Page view tracking:', pageData);
3929
- // TODO: Q1 2025 구현 예정
4317
+ getUserId() {
4318
+ return EventSessionManager.getUserId();
3930
4319
  }
3931
4320
  /**
3932
- * 사용자 액션 이벤트
3933
- * @example AdStage.events.userAction('button_click', { button_id: 'cta' })
4321
+ * 이벤트 추적
4322
+ * @param eventName 이벤트 이름
4323
+ * @param properties 이벤트 속성
3934
4324
  */
3935
- async userAction(actionType, metadata) {
3936
- console.log('🚧 [TODO] User action tracking:', { actionType, metadata });
3937
- // TODO: Q1 2025 구현 예정
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 conversion(conversionData) {
3944
- console.log('🚧 [TODO] Conversion tracking:', conversionData);
3945
- // TODO: Q1 2025 구현 예정
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 };