@adstage/web-sdk 3.0.14 → 3.0.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adstage/web-sdk",
3
- "version": "3.0.14",
3
+ "version": "3.0.15",
4
4
  "description": "AdStage Web SDK - Production-ready marketing platform SDK with React Provider support for seamless integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
@@ -33,6 +33,14 @@ export interface TrackingParams {
33
33
  creative_id?: string; // 광고소재 ID
34
34
  term?: string; // 키워드
35
35
 
36
+ // ========== 카카오 키워드광고 파라미터 ==========
37
+ k_campaign?: string; // 카카오 키워드광고 캠페인 ID
38
+ k_adgroup?: string; // 카카오 키워드광고 광고그룹 ID
39
+ k_keyword?: string; // 카카오 키워드광고 등록 키워드
40
+ k_keyword_id?: string; // 카카오 키워드광고 키워드 ID
41
+ k_creative?: string; // 카카오 키워드광고 소재 ID
42
+ k_rank?: string; // 카카오 키워드광고 노출 순위
43
+
36
44
  // ========== UTM 파라미터 ==========
37
45
  utm_source?: string; // 트래픽 소스 (예: google, facebook, newsletter)
38
46
  utm_medium?: string; // 마케팅 매체 (예: cpc, banner, email)
@@ -51,7 +59,7 @@ export class TrackingParamsModule {
51
59
  // 추출할 파라미터 키 정의
52
60
  private static readonly CLICK_ID_KEYS = ['gclid', 'fbclid', 'ttclid', 'nclid', 'liclid', 'msclkid', 'twclid'];
53
61
 
54
- private static readonly CAMPAIGN_KEYS = ['channel', 'campaign', 'campaign_id', 'ad_group', 'ad_group_id', 'ad_creative', 'creative_id', 'term'];
62
+ private static readonly CAMPAIGN_KEYS = ['channel', 'campaign', 'campaign_id', 'ad_group', 'ad_group_id', 'ad_creative', 'creative_id', 'term', 'k_campaign', 'k_adgroup', 'k_keyword', 'k_keyword_id', 'k_creative', 'k_rank'];
55
63
 
56
64
  private static readonly UTM_KEYS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
57
65
 
@@ -130,6 +138,9 @@ export class TrackingParamsModule {
130
138
  else if (params.liclid) params.channel = 'linkedin.ads';
131
139
  else if (params.msclkid) params.channel = 'microsoft.ads';
132
140
  else if (params.twclid) params.channel = 'twitter.ads';
141
+ // 카카오 키워드광고: utm_source=kakao & utm_medium=keyword 또는 k_ 파라미터
142
+ else if (params.utm_source === 'kakao' && params.utm_medium === 'keyword') params.channel = 'kakao.keyword';
143
+ else if (params.k_campaign || params.k_keyword) params.channel = 'kakao.keyword';
133
144
  }
134
145
 
135
146
  TrackingParamsModule.store(params);
@@ -315,6 +326,14 @@ export class TrackingParamsModule {
315
326
  if (params.creative_id) attribution.creativeId = params.creative_id;
316
327
  if (params.term) attribution.term = params.term;
317
328
 
329
+ // ========== 카카오 키워드광고 파라미터 ==========
330
+ if (params.k_campaign) attribution.campaignId = attribution.campaignId || params.k_campaign;
331
+ if (params.k_adgroup) attribution.adGroupId = attribution.adGroupId || params.k_adgroup;
332
+ if (params.k_keyword) attribution.keyword = params.k_keyword;
333
+ if (params.k_keyword_id) attribution.keywordId = params.k_keyword_id;
334
+ if (params.k_creative) attribution.creativeId = attribution.creativeId || params.k_creative;
335
+ if (params.k_rank) attribution.adRank = params.k_rank;
336
+
318
337
  // ========== UTM 파라미터 (앱/웹 공통 - snake_case 유지) ==========
319
338
  if (params.utm_source) attribution.utm_source = params.utm_source;
320
339
  if (params.utm_medium) attribution.utm_medium = params.utm_medium;
@@ -6,11 +6,14 @@
6
6
  export class ApiHeaders {
7
7
  /**
8
8
  * 표준 API 헤더 생성
9
+ *
10
+ * 주의: `User-Agent`는 브라우저 forbidden header라 fetch에서 수동 설정하면 안 된다.
11
+ * (Chromium은 무시하지만 Safari/WebKit은 설정값을 CORS preflight에 포함시켜
12
+ * 서버가 허용하지 않으면 요청 자체가 차단됨.) 브라우저가 모든 요청에 진짜
13
+ * `User-Agent`를 자동 첨부하므로, 서버는 `req.headers['user-agent']`로 동일한 값을 받는다.
9
14
  */
10
15
  static create(apiKey: string, options?: {
11
16
  contentType?: string;
12
- userAgent?: string;
13
- currentUrl?: string;
14
17
  }): Record<string, string> {
15
18
  if (!apiKey) {
16
19
  throw new Error('API key is required');
@@ -21,32 +24,20 @@ export class ApiHeaders {
21
24
  'Content-Type': options?.contentType || 'application/json'
22
25
  };
23
26
 
24
- // User-Agent는 이벤트 추적에서 실제로 사용됨
25
- if (typeof navigator !== 'undefined') {
26
- headers['User-Agent'] = options?.userAgent || navigator.userAgent;
27
- }
28
-
29
- // X-Current-URL은 현재 서버에서 사용하지 않으므로 제거
30
- // 필요시 이벤트 데이터 body에 포함
27
+ // X-Current-URL 등 부가 정보는 HTTP 헤더가 아닌 이벤트 데이터 body에 포함
31
28
 
32
29
  return headers;
33
30
  }
34
31
 
35
32
  /**
36
33
  * 이벤트 추적용 헤더 생성
37
- * User-Agent는 서버에서 실제로 사용됨
34
+ *
35
+ * 디바이스 `userAgent`는 HTTP 헤더가 아니라 요청 body(eventData) 또는 브라우저가
36
+ * 자동 첨부하는 `User-Agent`로 전달된다. (서버 advertisement 컨트롤러는
37
+ * `req.headers['user-agent']`를 직접 사용.) 헤더 수동 override는 Safari CORS
38
+ * preflight를 깨뜨리므로 하지 않는다. 시그니처는 호출처 호환을 위해 유지.
38
39
  */
39
- static createForEvents(apiKey: string, eventData?: Record<string, any>): Record<string, string> {
40
- const baseHeaders = ApiHeaders.create(apiKey);
41
-
42
- // User-Agent 오버라이드 (서버에서 실제 사용)
43
- if (eventData?.userAgent) {
44
- baseHeaders['User-Agent'] = eventData.userAgent;
45
- }
46
-
47
- // 다른 정보들은 HTTP 헤더가 아닌 이벤트 데이터 body에 포함하는 것이 적절
48
- // (currentUrl, referrer 등은 POST body로 전송)
49
-
50
- return baseHeaders;
40
+ static createForEvents(apiKey: string, _eventData?: Record<string, any>): Record<string, string> {
41
+ return ApiHeaders.create(apiKey);
51
42
  }
52
43
  }