@vircle/sdk-web 0.2.0
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 +598 -0
- package/dist/index.d.ts +701 -0
- package/dist/index.esm.js +1472 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +1488 -0
- package/dist/index.js.map +1 -0
- package/dist/vircle-web-sdk.min.js +2 -0
- package/dist/vircle-web-sdk.min.js.map +1 -0
- package/dist/vircle-web-sdk.standalone.esm.js +2 -0
- package/dist/vircle-web-sdk.standalone.esm.js.map +1 -0
- package/package.json +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
# @vircle/sdk-web
|
|
2
|
+
|
|
3
|
+
VircleCore를 확장하여 웹 환경에 특화된 기능을 제공하는 웹 애플리케이션용 Vircle Analytics SDK입니다. 자동 추적, 브라우저 컨텍스트 수집, 웹 스토리지 지원 등 웹 환경에 최적화된 기능을 제공합니다.
|
|
4
|
+
|
|
5
|
+
## 🚀 주요 기능
|
|
6
|
+
|
|
7
|
+
### 핵심 기능
|
|
8
|
+
- **VircleCore 상속**: 코어 SDK의 모든 기능 + 웹 특화 기능
|
|
9
|
+
- **자동 추적**: 페이지뷰, 클릭, 폼 제출, JavaScript 에러 자동 감지
|
|
10
|
+
- **SPA 지원**: History API 래핑으로 라우트 변경 감지
|
|
11
|
+
- **웹 컨텍스트 수집**: 브라우저, OS, 디바이스, UTM 파라미터 등
|
|
12
|
+
|
|
13
|
+
### 성능 최적화
|
|
14
|
+
- **requestIdleCallback**: UI 블로킹 없는 백그라운드 처리
|
|
15
|
+
- **컨텍스트 캐싱**: 반복적인 DOM 접근 방지
|
|
16
|
+
- **LocalStorage**: 영속적 데이터 저장 및 LRU 캐시 정리
|
|
17
|
+
- **이벤트 배칭**: 네트워크 요청 최소화
|
|
18
|
+
|
|
19
|
+
### 보안 및 프라이버시
|
|
20
|
+
- **페이로드 암호화**: 하이브리드 암호화 (AES-256-GCM + RSA-OAEP)
|
|
21
|
+
- **추적 제외**: data-vircle-ignore 속성으로 민감 정보 보호
|
|
22
|
+
- **안전한 에러 처리**: SDK 안정성을 위한 모든 외부 API 호출 보호
|
|
23
|
+
|
|
24
|
+
## 설치
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @vircle/sdk-web
|
|
28
|
+
# 또는
|
|
29
|
+
yarn add @vircle/sdk-web
|
|
30
|
+
# 또는
|
|
31
|
+
pnpm add @vircle/sdk-web
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 예제 실행
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# 저장소 클론 후
|
|
38
|
+
cd packages/sdk-web
|
|
39
|
+
|
|
40
|
+
# 의존성 설치 및 빌드
|
|
41
|
+
pnpm install
|
|
42
|
+
pnpm build
|
|
43
|
+
|
|
44
|
+
# 예제 서버 실행
|
|
45
|
+
pnpm example
|
|
46
|
+
|
|
47
|
+
# 브라우저에서 http://localhost:3000 접속
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 📖 빠른 시작
|
|
51
|
+
|
|
52
|
+
### 기본 사용법
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { VircleWeb } from '@vircle/sdk-web';
|
|
56
|
+
|
|
57
|
+
// SDK 초기화 (VircleCore 설정 + 웹 전용 설정)
|
|
58
|
+
const vircle = new VircleWeb({
|
|
59
|
+
apiKey: 'your-api-key',
|
|
60
|
+
environment: 'production', // 'development' | 'production'
|
|
61
|
+
|
|
62
|
+
// 배치 처리 설정 (VircleCore 기능)
|
|
63
|
+
batch: {
|
|
64
|
+
size: 20, // 배치당 이벤트 수
|
|
65
|
+
timeout: 5000, // 대기 시간 (ms)
|
|
66
|
+
flushInterval: 10000, // 자동 플러시 간격
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// 웹 전용 자동 추적 설정
|
|
70
|
+
autoTrack: {
|
|
71
|
+
pageView: true,
|
|
72
|
+
clicks: true,
|
|
73
|
+
forms: true,
|
|
74
|
+
errors: true,
|
|
75
|
+
},
|
|
76
|
+
}, {
|
|
77
|
+
// 웹 전용 옵션
|
|
78
|
+
enableEncryption: true, // 페이로드 암호화
|
|
79
|
+
flushOnUnload: true, // 페이지 이탈 시 전송
|
|
80
|
+
contextCacheTime: 300000, // 컨텍스트 캐싱 시간
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// SDK 초기화 필수!
|
|
84
|
+
await vircle.initialize();
|
|
85
|
+
|
|
86
|
+
// 커스텀 이벤트 추적
|
|
87
|
+
await vircle.track('button_clicked', {
|
|
88
|
+
button_id: 'purchase',
|
|
89
|
+
value: 99.99,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// 사용자 식별 (identify 이벤트로 서버 전송)
|
|
93
|
+
await vircle.identify('user-123', {
|
|
94
|
+
email: 'user@example.com',
|
|
95
|
+
name: '홍길동',
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### ES Modules (브라우저)
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<!-- 방법1: CDN 사용 (standalone 빌드 - 모든 의존성 포함) -->
|
|
103
|
+
<script type="module">
|
|
104
|
+
import { VircleWeb } from 'https://cdn.jsdelivr.net/npm/@vircle/sdk-web/dist/vircle-web-sdk.standalone.esm.js';
|
|
105
|
+
|
|
106
|
+
const vircle = new VircleWeb({
|
|
107
|
+
apiKey: 'your-api-key',
|
|
108
|
+
environment: 'production', // 'development' | 'production'
|
|
109
|
+
autoTrack: {
|
|
110
|
+
pageView: true,
|
|
111
|
+
clicks: true,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await vircle.initialize();
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<!-- 방법2: 로컬 파일 사용 -->
|
|
119
|
+
<script type="module" src="/dist/vircle-web-sdk.standalone.esm.js"></script>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### UMD (레거시 지원)
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<script src="https://cdn.jsdelivr.net/npm/@vircle/sdk-web/dist/vircle-web-sdk.min.js"></script>
|
|
126
|
+
<script>
|
|
127
|
+
const vircle = new VircleWebSDK.VircleWeb({
|
|
128
|
+
apiKey: 'your-api-key',
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
vircle.initialize().then(() => {
|
|
132
|
+
console.log('SDK 초기화 완료');
|
|
133
|
+
});
|
|
134
|
+
</script>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 🏗️ 아키텍처
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
┌────────────────────────────────────────────┐
|
|
141
|
+
│ VircleWeb │
|
|
142
|
+
│ (VircleCore 상속 + 웹 전용 기능) │
|
|
143
|
+
├────────────────────────────────────────────┤
|
|
144
|
+
│ • 자동 추적 시스템 (setupAutoTracking) │
|
|
145
|
+
│ • 웹 컨텍스트 수집 (WebContextCollector) │
|
|
146
|
+
│ • LocalStorage 어댑터 │
|
|
147
|
+
│ • 브라우저 이벤트 핸들러 │
|
|
148
|
+
└────────────────────┬───────────────────────┘
|
|
149
|
+
│ extends
|
|
150
|
+
┌────────────────────┴───────────────────────┐
|
|
151
|
+
│ VircleCore │
|
|
152
|
+
│ (이벤트 처리, 전송, 플러그인, 암호화 등) │
|
|
153
|
+
└────────────────────────────────────────────┘
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 🔍 웹 전용 기능 상세
|
|
157
|
+
|
|
158
|
+
### 자동 추적 시스템
|
|
159
|
+
|
|
160
|
+
SDK는 `setupAutoTracking` 메서드를 통해 다양한 브라우저 이벤트를 자동으로 추적합니다:
|
|
161
|
+
|
|
162
|
+
#### 페이지뷰 추적
|
|
163
|
+
- **초기 로드**: 페이지 첫 로드 시 자동 추적
|
|
164
|
+
- **SPA 라우트 변경**: History API (`pushState`, `replaceState`) 래핑
|
|
165
|
+
- **브라우저 네비게이션**: `popstate` 이벤트 감지
|
|
166
|
+
- **추적 데이터**: URL, 제목, 리퍼러, UTM 파라미터
|
|
167
|
+
|
|
168
|
+
#### 클릭 추적
|
|
169
|
+
- **전역 이벤트 위임**: `document`에 단일 리스너로 모든 클릭 감지
|
|
170
|
+
- **요소 정보 수집**: 태그명, 텍스트, 클래스, ID
|
|
171
|
+
- **추적 제외**: `data-vircle-ignore` 속성으로 민감 요소 제외
|
|
172
|
+
|
|
173
|
+
#### 폼 제출 추적
|
|
174
|
+
- **자동 감지**: 모든 `form` 태그의 `submit` 이벤트
|
|
175
|
+
- **수집 정보**: 폼 ID, action, method
|
|
176
|
+
- **보안**: 폼 데이터는 수집하지 않음
|
|
177
|
+
|
|
178
|
+
#### 에러 추적
|
|
179
|
+
- **JavaScript 에러**: `window.onerror` 이벤트
|
|
180
|
+
- **Promise 거부**: `unhandledrejection` 이벤트
|
|
181
|
+
- **에러 정보**: 메시지, 스택, 파일명, 라인 번호
|
|
182
|
+
|
|
183
|
+
### 웹 컨텍스트 수집 (WebContextCollector)
|
|
184
|
+
|
|
185
|
+
자동으로 수집되는 브라우저 환경 정보:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
{
|
|
189
|
+
device: {
|
|
190
|
+
type: 'desktop', // mobile | tablet | desktop
|
|
191
|
+
browser: 'Chrome', // 브라우저 이름
|
|
192
|
+
browserVersion: '120.0.0', // 브라우저 버전
|
|
193
|
+
os: 'macOS', // 운영체제
|
|
194
|
+
osVersion: '14.0', // OS 버전
|
|
195
|
+
screenWidth: 1920, // 화면 너비
|
|
196
|
+
screenHeight: 1080, // 화면 높이
|
|
197
|
+
viewportWidth: 1200, // 뷰포트 너비
|
|
198
|
+
viewportHeight: 800, // 뷰포트 높이
|
|
199
|
+
language: 'ko-KR', // 언어
|
|
200
|
+
timezone: 'Asia/Seoul' // 타임존
|
|
201
|
+
},
|
|
202
|
+
page: {
|
|
203
|
+
url: 'https://example.com/products?category=electronics',
|
|
204
|
+
title: '제품 목록',
|
|
205
|
+
path: '/products',
|
|
206
|
+
search: '?category=electronics',
|
|
207
|
+
hash: '',
|
|
208
|
+
referrer: 'https://google.com'
|
|
209
|
+
},
|
|
210
|
+
custom: {
|
|
211
|
+
utm_source: 'google',
|
|
212
|
+
utm_medium: 'cpc',
|
|
213
|
+
utm_campaign: 'summer_sale',
|
|
214
|
+
cookiesEnabled: true,
|
|
215
|
+
network: {
|
|
216
|
+
effectiveType: '4g',
|
|
217
|
+
downlink: 10.0
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### LocalStorage 어댑터
|
|
224
|
+
|
|
225
|
+
웹 브라우저의 LocalStorage를 활용한 데이터 영속성:
|
|
226
|
+
|
|
227
|
+
- **자동 용량 관리**: QuotaExceededError 시 LRU 캐시 정리
|
|
228
|
+
- **접두사 격리**: `vircle_` 접두사로 다른 데이터와 분리
|
|
229
|
+
- **타임스탬프 추적**: 데이터 저장 시간 기록
|
|
230
|
+
- **안전한 폴백**: LocalStorage 불가 시 경고 후 계속 작동
|
|
231
|
+
|
|
232
|
+
### 성능 최적화 기술
|
|
233
|
+
|
|
234
|
+
- **requestIdleCallback 활용**: UI 블로킹 없는 백그라운드 이벤트 처리
|
|
235
|
+
- **컨텍스트 캐싱**: 브라우저 정보 캐싱으로 반복적 DOM 접근 방지
|
|
236
|
+
- **이벤트 배칭**: TransportService의 큐를 통한 네트워크 요청 최소화
|
|
237
|
+
- **메모리 관리**: cleanup 시 모든 이벤트 리스너 제거로 누수 방지
|
|
238
|
+
|
|
239
|
+
### 추적 제외
|
|
240
|
+
|
|
241
|
+
특정 요소나 영역을 추적에서 제외하려면 `data-vircle-ignore` 속성을 사용하세요:
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<!-- 이 버튼은 클릭 추적에서 제외됩니다 -->
|
|
245
|
+
<button data-vircle-ignore>추적 제외 버튼</button>
|
|
246
|
+
|
|
247
|
+
<!-- 이 영역 내의 모든 요소가 추적에서 제외됩니다 -->
|
|
248
|
+
<div data-vircle-ignore>
|
|
249
|
+
<button>추적 안됨</button>
|
|
250
|
+
<a href="#">이 링크도 추적 안됨</a>
|
|
251
|
+
</div>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## 설정 옵션
|
|
255
|
+
|
|
256
|
+
### VircleWebConfig
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
interface VircleWebConfig {
|
|
260
|
+
apiKey: string; // 필수: API 키
|
|
261
|
+
endpoint?: string; // API 엔드포인트 (선택)
|
|
262
|
+
trackPageViews?: boolean; // 페이지뷰 자동 추적 (기본: false)
|
|
263
|
+
trackClicks?: boolean; // 클릭 자동 추적 (기본: false)
|
|
264
|
+
trackForms?: boolean; // 폼 제출 자동 추적 (기본: false)
|
|
265
|
+
trackErrors?: boolean; // 에러 자동 추적 (기본: false)
|
|
266
|
+
singlePageApp?: boolean; // SPA 모드 (기본: false)
|
|
267
|
+
storagePrefix?: string; // 스토리지 키 접두사 (기본: 'vircle_')
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### VircleWebOptions
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
interface VircleWebOptions {
|
|
275
|
+
flushOnUnload?: boolean; // 페이지 언로드 시 이벤트 전송 (기본: true)
|
|
276
|
+
contextCacheTime?: number; // 컨텍스트 캐시 시간(ms) (기본: 300000)
|
|
277
|
+
enablePerformance?: boolean; // 성능 측정 활성화 (기본: true)
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## API 레퍼런스
|
|
282
|
+
|
|
283
|
+
### 초기화
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
await vircle.initialize(): Promise<void>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
SDK를 초기화하고 자동 추적을 시작합니다.
|
|
290
|
+
|
|
291
|
+
### 이벤트 추적
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
await vircle.track(
|
|
295
|
+
eventName: string,
|
|
296
|
+
properties?: Record<string, unknown>,
|
|
297
|
+
context?: EventContext
|
|
298
|
+
): Promise<void>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
커스텀 이벤트를 추적합니다.
|
|
302
|
+
|
|
303
|
+
### 페이지뷰 추적
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
await vircle.trackPageView(
|
|
307
|
+
properties?: Record<string, unknown>,
|
|
308
|
+
context?: EventContext
|
|
309
|
+
): Promise<void>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
페이지뷰를 수동으로 추적합니다.
|
|
313
|
+
|
|
314
|
+
### 사용자 식별
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
await vircle.identify(
|
|
318
|
+
userId: string,
|
|
319
|
+
traits?: UserProperties
|
|
320
|
+
): Promise<void>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
사용자를 식별하고 속성을 설정합니다.
|
|
324
|
+
|
|
325
|
+
### 사용자 리셋
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
await vircle.reset(): Promise<void>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
사용자 정보를 초기화합니다.
|
|
332
|
+
|
|
333
|
+
### 정리
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
await vircle.cleanup(): Promise<void>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
모든 이벤트 리스너를 제거하고 리소스를 정리합니다.
|
|
340
|
+
|
|
341
|
+
## 빌드 파일 설명
|
|
342
|
+
|
|
343
|
+
| 파일 | 용도 | 의존성 | 사용 방법 |
|
|
344
|
+
|------|------|--------|-----------|
|
|
345
|
+
| `dist/index.js` | CommonJS (Node.js/번들러) | 별도 설치 필요 | `require('@vircle/sdk-web')` |
|
|
346
|
+
| `dist/index.esm.js` | ES Modules (번들러) | 별도 설치 필요 | `import { VircleWeb } from '@vircle/sdk-web'` |
|
|
347
|
+
| `dist/vircle-web-sdk.standalone.esm.js` | ES Modules (브라우저) | 모두 포함 ✅ | `<script type="module">` |
|
|
348
|
+
| `dist/vircle-web-sdk.min.js` | UMD (레거시 브라우저) | 모두 포함 ✅ | `<script>` |
|
|
349
|
+
|
|
350
|
+
⚠️ **중요**: 브라우저에서 직접 사용할 때는 반드시 `standalone` 또는 `min` 빌드를 사용하세요.
|
|
351
|
+
|
|
352
|
+
## 브라우저 지원
|
|
353
|
+
|
|
354
|
+
- Chrome/Edge: 최신 2개 버전
|
|
355
|
+
- Firefox: 최신 2개 버전
|
|
356
|
+
- Safari: 최신 2개 버전
|
|
357
|
+
- iOS Safari: iOS 12+
|
|
358
|
+
- Chrome for Android: Android 6+
|
|
359
|
+
|
|
360
|
+
## 보안 고려사항
|
|
361
|
+
|
|
362
|
+
1. **클라이언트 측 API 키**: 브라우저에서 사용되는 API 키는 공개됩니다. 데이터 수집(Write) 권한만 가진 키를 사용하세요.
|
|
363
|
+
|
|
364
|
+
2. **민감한 데이터**: 민감한 데이터가 수집되지 않도록 `data-vircle-ignore` 속성을 적절히 사용하세요.
|
|
365
|
+
|
|
366
|
+
3. **LocalStorage**: SDK는 LocalStorage를 사용하여 데이터를 임시 저장합니다. 민감한 정보는 저장하지 않습니다.
|
|
367
|
+
|
|
368
|
+
## 성능 영향
|
|
369
|
+
|
|
370
|
+
- **번들 크기**: ~13.5KB (minified), ~4.5KB (gzipped)
|
|
371
|
+
- **초기화 시간**: < 10ms
|
|
372
|
+
- **이벤트 추적**: < 1ms (requestIdleCallback 사용)
|
|
373
|
+
|
|
374
|
+
## 고급 사용 예제
|
|
375
|
+
|
|
376
|
+
### SPA (Single Page Application) 설정
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
const vircle = new VircleWeb({
|
|
380
|
+
apiKey: 'your-api-key',
|
|
381
|
+
singlePageApp: true, // SPA 모드 활성화
|
|
382
|
+
trackPageViews: true
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
await vircle.initialize()
|
|
386
|
+
|
|
387
|
+
// React Router 예제
|
|
388
|
+
import { useEffect } from 'react'
|
|
389
|
+
import { useLocation } from 'react-router-dom'
|
|
390
|
+
|
|
391
|
+
function App() {
|
|
392
|
+
const location = useLocation()
|
|
393
|
+
|
|
394
|
+
useEffect(() => {
|
|
395
|
+
// 라우트 변경 시 자동으로 페이지뷰가 추적됩니다
|
|
396
|
+
}, [location])
|
|
397
|
+
|
|
398
|
+
return <Routes>...</Routes>
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 에러 처리와 함께 사용
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
try {
|
|
406
|
+
const vircle = new VircleWeb({
|
|
407
|
+
apiKey: 'your-api-key',
|
|
408
|
+
trackErrors: false, // 자동 에러 추적 비활성화
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
await vircle.initialize();
|
|
412
|
+
|
|
413
|
+
// 수동으로 에러 추적
|
|
414
|
+
try {
|
|
415
|
+
await riskyOperation();
|
|
416
|
+
} catch (error) {
|
|
417
|
+
await vircle.track('error', {
|
|
418
|
+
message: error.message,
|
|
419
|
+
stack: error.stack,
|
|
420
|
+
component: 'PaymentForm',
|
|
421
|
+
});
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
424
|
+
} catch (error) {
|
|
425
|
+
console.error('SDK 초기화 실패:', error);
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### 성능 최적화 설정
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
const vircle = new VircleWeb(
|
|
433
|
+
{
|
|
434
|
+
apiKey: 'your-api-key',
|
|
435
|
+
trackPageViews: true,
|
|
436
|
+
trackClicks: true,
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
contextCacheTime: 600000, // 10분 캐싱
|
|
440
|
+
flushOnUnload: false, // 수동으로 flush 관리
|
|
441
|
+
enablePerformance: false, // 성능 추적 비활성화
|
|
442
|
+
},
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// 중요한 시점에 수동으로 flush
|
|
446
|
+
window.addEventListener('beforeunload', async () => {
|
|
447
|
+
await vircle.flush();
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### 민감한 정보가 있는 페이지
|
|
452
|
+
|
|
453
|
+
```html
|
|
454
|
+
<!-- 개인정보가 포함된 대시보드 -->
|
|
455
|
+
<div class="user-dashboard">
|
|
456
|
+
<!-- 사용자 정보는 추적에서 제외 -->
|
|
457
|
+
<h1 data-vircle-ignore>안녕하세요, 김철수님!</h1>
|
|
458
|
+
|
|
459
|
+
<!-- 계좌 정보는 추적에서 완전 제외 -->
|
|
460
|
+
<div data-vircle-ignore>
|
|
461
|
+
<p>계좌번호: 123-456-789</p>
|
|
462
|
+
<p>잔액: ₩1,234,567</p>
|
|
463
|
+
<button>송금하기</button>
|
|
464
|
+
</div>
|
|
465
|
+
|
|
466
|
+
<!-- 일반 액션은 추적 -->
|
|
467
|
+
<button>프로필 수정</button>
|
|
468
|
+
</div>
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### 동적 컨텍스트 추가
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
// 글로벌 컨텍스트 설정
|
|
475
|
+
vircle.setContext({
|
|
476
|
+
app: {
|
|
477
|
+
version: '2.1.0',
|
|
478
|
+
environment: 'production',
|
|
479
|
+
},
|
|
480
|
+
custom: {
|
|
481
|
+
experiment_id: 'exp_123',
|
|
482
|
+
feature_flags: {
|
|
483
|
+
new_ui: true,
|
|
484
|
+
beta_features: false,
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
// 특정 이벤트에만 컨텍스트 추가
|
|
490
|
+
await vircle.track(
|
|
491
|
+
'purchase_completed',
|
|
492
|
+
{
|
|
493
|
+
product_id: 'sku_123',
|
|
494
|
+
price: 49.99,
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
custom: {
|
|
498
|
+
payment_method: 'card',
|
|
499
|
+
coupon_used: true,
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
);
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## 문제 해결
|
|
506
|
+
|
|
507
|
+
### SDK가 초기화되지 않음
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
// 상태 확인
|
|
511
|
+
const status = vircle.getStatus();
|
|
512
|
+
console.log('SDK 초기화 상태:', status.isInitialized);
|
|
513
|
+
|
|
514
|
+
// 네트워크 상태 확인
|
|
515
|
+
console.log('온라인 상태:', vircle.isOnline());
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### 이벤트가 전송되지 않음
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
// 디버그 모드로 재초기화
|
|
522
|
+
const vircle = new VircleWeb({
|
|
523
|
+
apiKey: 'your-api-key',
|
|
524
|
+
debug: true, // 콘솔에 디버그 로그 출력
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// 수동으로 이벤트 플러시
|
|
528
|
+
await vircle.flush();
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### 메모리 누수 방지
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
// 컴포넌트 언마운트 시 반드시 cleanup 호출
|
|
535
|
+
// React 예제
|
|
536
|
+
useEffect(() => {
|
|
537
|
+
const vircle = new VircleWeb({ apiKey: 'your-api-key' });
|
|
538
|
+
vircle.initialize();
|
|
539
|
+
|
|
540
|
+
return () => {
|
|
541
|
+
vircle.cleanup();
|
|
542
|
+
};
|
|
543
|
+
}, []);
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## TypeScript 지원
|
|
547
|
+
|
|
548
|
+
SDK는 완전한 TypeScript 타입 정의를 제공합니다:
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
import { VircleWeb, VircleWebConfig, VircleWebOptions, EventContext, UserProperties } from '@vircle/sdk-web';
|
|
552
|
+
|
|
553
|
+
// 타입 안전한 설정
|
|
554
|
+
const config: VircleWebConfig = {
|
|
555
|
+
apiKey: process.env.VIRCLE_API_KEY!,
|
|
556
|
+
trackPageViews: true,
|
|
557
|
+
trackErrors: true,
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
const options: VircleWebOptions = {
|
|
561
|
+
contextCacheTime: 300000,
|
|
562
|
+
flushOnUnload: true,
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const vircle = new VircleWeb(config, options);
|
|
566
|
+
|
|
567
|
+
// 타입 안전한 이벤트 추적
|
|
568
|
+
interface PurchaseEvent {
|
|
569
|
+
product_id: string;
|
|
570
|
+
price: number;
|
|
571
|
+
currency: string;
|
|
572
|
+
quantity: number;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
await vircle.track<PurchaseEvent>('purchase', {
|
|
576
|
+
product_id: 'sku_123',
|
|
577
|
+
price: 49.99,
|
|
578
|
+
currency: 'USD',
|
|
579
|
+
quantity: 1,
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// 타입 안전한 사용자 속성
|
|
583
|
+
interface UserTraits extends UserProperties {
|
|
584
|
+
email: string;
|
|
585
|
+
plan: 'free' | 'pro' | 'enterprise';
|
|
586
|
+
created_at: string;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
await vircle.identify<UserTraits>('user-123', {
|
|
590
|
+
email: 'user@example.com',
|
|
591
|
+
plan: 'pro',
|
|
592
|
+
created_at: new Date().toISOString(),
|
|
593
|
+
});
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
## 라이선스
|
|
597
|
+
|
|
598
|
+
MIT
|