@solapi/crm-browser
v0.1.2
Published
SOLAPI CRM browser SDK for event tracking and record creation
Downloads
347
Maintainers
Readme
SOLAPI CRM Browser SDK
English | 한국어
A lightweight, production-ready JavaScript SDK for tracking user events and creating records in SOLAPI CRM from web browsers.
- Homepage: https://solapi.com
- Tracking Key: Issue at SOLAPI Console > CRM > Settings > SDK
- API Documentation: https://developers.solapi.com/references/crm-core
Features
- Event Tracking: Track custom user events with flexible properties
- Automatic Page View Tracking: Monitor user navigation in single-page applications (SPAs)
- Record Creation: Create CRM records directly from your frontend
- Session Identification: Associate anonymous sessions with known records
- Super Properties: Register global properties sent with every event
- Automatic Batching: Queue and batch events for efficient network usage
- Offline Resilience: Queue events locally when network is unavailable
- GDPR Compliant: Full opt-in/opt-out tracking control
- TypeScript Support: Full type definitions included
- Zero Dependencies: Minimal bundle size (~2KB minified+gzipped)
- Security: HTTPS-only, prototype pollution prevention, cryptographic ID generation
Installation
npm
npm install @solapi/crm-browserYarn
yarn add @solapi/crm-browserpnpm
pnpm add @solapi/crm-browserCDN (Script Tag)
<script src="https://cdn.solapi.com/mfe/sdk/crm-sdk/solapi-crm.min.js"></script>
<script>
SolapiCRM.init({ trackingKey: 'tk_live_...' });
</script>Quick Start
1. Initialize the SDK
Always call init() first with your tracking key:
import SolapiCRM from '@solapi/crm-browser';
SolapiCRM.init({
trackingKey: 'tk_live_your_tracking_key_here',
entityId: 'entity_abc123', // Optional: default entity for events
autoPageView: true, // Optional: auto-track page views
debug: true, // Optional: enable debug logging
});Required: trackingKey — SOLAPI Console > CRM > Settings > SDK에서 발급
2. Track Events
// Simple event
SolapiCRM.track('signup');
// Event with properties
SolapiCRM.track('purchase', {
product: 'Premium Plan',
amount: 79000,
currency: 'KRW',
});
// Event with analytics options
SolapiCRM.track('button_click', {}, {
category: 'engagement',
label: 'subscribe_button',
value: 1,
});3. Identify Users
Once a user logs in, associate the session with their CRM record:
// After user login
const record = await SolapiCRM.createRecord({
name: 'John Doe',
data: { email: '[email protected]' },
tags: ['premium'],
});
// Associate session with the record
await SolapiCRM.identify(record.recordId, {
email: '[email protected]',
plan: 'premium',
});4. Register Super Properties
Send the same properties with every event:
SolapiCRM.register({
plan: 'premium',
region: 'KR',
appVersion: '1.2.0',
});Configuration
The init() method accepts a configuration object:
interface SolapiCRMConfig {
// Required
trackingKey: string;
// Optional
entityId?: string; // Default entity ID for events/records
endpoint?: string; // API endpoint (default: https://api.solapi.com/crm-core/v1/sdk)
autoPageView?: boolean; // Auto track page views on init (default: false)
batchSize?: number; // Events to queue before auto-flush (default: 10)
flushInterval?: number; // Auto-flush interval in milliseconds (default: 5000)
maxQueueSize?: number; // Max queued events before dropping oldest (default: 1000)
debug?: boolean; // Enable debug logging (default: false)
}Configuration Examples
Production Setup:
SolapiCRM.init({
trackingKey: 'tk_live_xxxxxxxx',
entityId: 'entity_main_app',
batchSize: 20, // Larger batches for efficiency
flushInterval: 10000, // Less frequent flushes
});Development with Auto-Tracking:
SolapiCRM.init({
trackingKey: 'tk_live_xxxxxxxx',
autoPageView: true,
debug: true, // See all events logged
});API Reference
init(config)
Initialize the SDK with a configuration object.
SolapiCRM.init({
trackingKey: 'tk_live_xxxxxxxx',
entityId: 'entity_abc123',
autoPageView: true,
});Throws: Error if trackingKey is missing or invalid.
Note: Calling init() again will clean up and restart the SDK instance.
track(eventType, properties?, options?)
Track a custom event.
SolapiCRM.track('button_click', {
buttonId: 'submit_btn',
pageSection: 'checkout',
}, {
category: 'engagement',
label: 'checkout_submit',
value: 1,
});Parameters:
eventType(string, required): Event name (e.g.,'purchase','login')properties(object, optional): Custom event dataoptions(object, optional):category(string): Event category for analyticslabel(string): Event labelvalue(number): Numeric value for the eventrecordId(string): Override the current record ID for this event only
Throws: Error if SDK not initialized or eventType is empty.
Behavior: If opt-out is active, event is silently discarded.
trackPageView()
Manually track a page view.
SolapiCRM.trackPageView();Behavior:
- Captures current URL, path, page title, and referrer
- Sent as an event with
eventType: 'PAGE_VIEW' - Useful for manual tracking in non-SPA apps
Throws: Error if SDK not initialized.
enableAutoPageView()
Install automatic SPA route change tracking.
SolapiCRM.init({ trackingKey: 'tk_live_...' });
SolapiCRM.enableAutoPageView();Behavior:
- Patches
history.pushState()andhistory.replaceState() - Listens for
popstateevents (browser back/forward) - Tracks a new page view when routes change
- Safe to call multiple times (installs only once)
createRecord(data)
Create a new CRM record.
const record = await SolapiCRM.createRecord({
entityId: 'entity_customers',
name: 'John Doe',
data: {
email: '[email protected]',
phone: '010-1234-5678',
source: 'web_signup',
},
tags: ['vip', 'early_adopter'],
});
console.log(record.recordId); // 'record_xyz789'
console.log(record.entityId); // 'entity_customers'
console.log(record.name); // 'John Doe'Parameters:
entityId(string, optional): Entity type for the record (defaults to config entityId)name(string, required): Record name (e.g., user name, company name)data(object, optional): Custom record fieldstags(string[], optional): Labels for organizing records
Returns: Promise<CreateRecordResponse>
interface CreateRecordResponse {
recordId: string; // Unique record ID
entityId: string; // Entity type
name: string; // Record name
[key: string]: any; // Additional response fields
}Throws: Error if SDK not initialized or name is missing.
identify(recordId, traits?)
Associate the current session with a known CRM record.
// Basic identification
await SolapiCRM.identify('record_abc123');
// With trait updates
await SolapiCRM.identify('record_abc123', {
email: '[email protected]',
plan: 'premium',
lastLogin: new Date().toISOString(),
});Parameters:
recordId(string, required): The CRM record ID to identify astraits(object, optional): User properties to update on the record
Behavior:
- Merges all previous anonymous events to this record (server-side)
- Persists the record ID to localStorage for session continuity
- Network failures are logged but never throw (safe for host app)
- If switching from one known ID to another, sends identify request with traits
Throws: Error if SDK not initialized or recordId is empty.
Best Practice: Call this after user login or when record is known.
register(properties)
Register super properties sent with every subsequent event.
SolapiCRM.register({
plan: 'premium',
region: 'KR',
userAgent: navigator.userAgent,
});
SolapiCRM.track('purchase', { amount: 79000 });
// Sent event includes: plan, region, userAgent, amountParameters:
properties(object): Key-value pairs to attach to all future events
Behavior:
- Properties persist in memory only (cleared on page reload or
reset()) - Later calls overwrite earlier values for duplicate keys
- Prototype pollution safe (blocks
__proto__,constructor,prototype)
registerOnce(properties)
Register super properties, skipping keys that are already set.
SolapiCRM.registerOnce({
firstVisit: new Date().toISOString(),
source: 'google_ads',
});
// Later calls don't overwrite these
SolapiCRM.registerOnce({
firstVisit: '2099-01-01', // Skipped (already set)
source: 'facebook_ads', // Skipped (already set)
});Parameters:
properties(object): Key-value pairs to set if not already present
unregister(propertyName)
Remove a super property.
SolapiCRM.unregister('tempFlag');Parameters:
propertyName(string): The key to remove
optOutTracking()
Opt out of all event tracking.
SolapiCRM.optOutTracking();
// No events are sent until optInTracking() is calledBehavior:
- All
track()calls silently return - Event queue timer stops
- Persists in memory (not stored; resets on page reload)
Use Case: Respect user privacy preferences or GDPR consent.
optInTracking()
Resume event tracking after opting out.
SolapiCRM.optInTracking();Behavior:
- Resumes accepting and sending events
- Restarts the event queue timer
hasOptedOut()
Check the current opt-out status.
if (SolapiCRM.hasOptedOut()) {
console.log('Tracking is disabled');
}Returns: boolean — true if opted out, false if tracking is active.
reset()
Reset the user session identity.
// On user logout
SolapiCRM.reset();Behavior:
- Generates a new anonymous ID
- Clears all super properties
- Clears persisted record ID from localStorage
- Future events sent with the new anonymous ID
Use Case: Handle user logout or session reset.
flush()
Immediately send all queued events to the server.
await SolapiCRM.flush();Behavior:
- Bypasses the automatic flush interval
- Retries up to 3 times with exponential backoff (1s, 2s, 4s)
- Batches events in groups of up to 100
- Dropped on persistent network failure after all retries
Use Case: Ensure events are sent before page unload (handled automatically).
destroy()
Clean up and destroy the SDK instance.
await SolapiCRM.destroy();Behavior:
- Flushes all remaining queued events
- Restores original
history.pushState/replaceStateif patched - Removes all event listeners
- Stops timers
- Clears internal state
Use Case: Cleanup in single-page app route changes or app unmount.
Common Patterns
React Integration
// App.tsx
import { useEffect } from 'react';
import SolapiCRM from '@solapi/crm-browser';
export function App() {
useEffect(() => {
// Initialize on mount
SolapiCRM.init({
trackingKey: import.meta.env.VITE_TRACKING_KEY,
entityId: 'entity_web_app',
autoPageView: true,
});
// Cleanup on unmount
return () => {
SolapiCRM.destroy();
};
}, []);
return <div>Your app</div>;
}User Authentication Flow
// After successful login
const user = await loginUser(credentials);
const record = await SolapiCRM.createRecord({
name: user.fullName,
data: {
email: user.email,
role: user.role,
},
});
await SolapiCRM.identify(record.recordId, {
email: user.email,
role: user.role,
});
// Now all events are tied to this user
SolapiCRM.track('login_success');SPA Route Navigation
// In your router (e.g., React Router)
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import SolapiCRM from '@solapi/crm-browser';
export function RouteTracker() {
const location = useLocation();
useEffect(() => {
// Manual page view on each route change
SolapiCRM.trackPageView();
}, [location]);
return null;
}
// Or use enableAutoPageView() for automatic tracking
SolapiCRM.enableAutoPageView();GDPR Opt-Out Consent Banner
function ConsentBanner() {
const handleOptOut = () => {
localStorage.setItem('tracking_consent', 'denied');
SolapiCRM.optOutTracking();
};
const handleOptIn = () => {
localStorage.setItem('tracking_consent', 'accepted');
SolapiCRM.optInTracking();
};
return (
<div className="consent-banner">
<p>We use analytics to improve your experience.</p>
<button onClick={handleOptOut}>Opt Out</button>
<button onClick={handleOptIn}>Accept</button>
</div>
);
}
// On app load, respect stored consent
const consent = localStorage.getItem('tracking_consent');
if (consent === 'denied') {
SolapiCRM.optOutTracking();
}Vue.js Integration
// main.ts
import { createApp } from 'vue';
import SolapiCRM from '@solapi/crm-browser';
import App from './App.vue';
const app = createApp(App);
SolapiCRM.init({
trackingKey: import.meta.env.VITE_TRACKING_KEY,
entityId: 'entity_vue_app',
});
app.config.globalProperties.$crm = SolapiCRM;
app.mount('#app');Event Properties Best Practices
// Good: Clear, lowercase keys, minimal nesting
SolapiCRM.track('add_to_cart', {
product_id: '123',
product_name: 'Laptop',
price: 1500000,
currency: 'KRW',
quantity: 1,
});
// Avoid: Nested objects, inconsistent naming, excessive data
SolapiCRM.track('add_to_cart', {
product: {
id: '123',
name: 'Laptop',
pricing: {
amount: 1500000,
},
},
ProductQuantity: 1, // Inconsistent casing
});Browser Compatibility
The SDK requires modern browser APIs. Minimum support:
| Browser | Minimum Version | |---------|-----------------| | Chrome | 57+ | | Firefox | 52+ | | Safari | 11+ | | Edge | 79+ | | iOS Safari | 11+ | | Android Chrome | 57+ |
Required APIs:
fetch()crypto.getRandomValues()localStorage(optional; falls back gracefully)history.pushState/replaceState(for SPA tracking)
Graceful Fallbacks:
- If
localStorageis unavailable (private mode, iframe sandbox), IDs are generated per-session - If historical API patching fails, manual
trackPageView()calls still work - Network failures are caught and logged; never crash the host app
TypeScript Support
Full TypeScript support with exported types:
import type { SolapiCRMConfig, TrackEvent } from '@solapi/crm-browser';
import SolapiCRM from '@solapi/crm-browser';
const config: SolapiCRMConfig = {
trackingKey: 'tk_live_...',
entityId: 'entity_main',
debug: true,
};
SolapiCRM.init(config);
interface CustomEvent extends TrackEvent {
customProperty: string;
}Debug Mode
Enable detailed logging to troubleshoot issues:
SolapiCRM.init({
trackingKey: 'tk_live_...',
debug: true,
});
// Console output:
// [SolapiCRM] Event queued: purchase
// [SolapiCRM] Flushing 5 events...
// [SolapiCRM] Events sent successfullySecurity Considerations
- HTTPS Only: SDK enforces HTTPS transport (localhost exempted for development)
- Tracking Key Security: Never expose
trackingKeyon public repositories. Use environment variables. - Prototype Pollution: Property keys like
__proto__,constructor,prototypeare automatically blocked - ID Generation: Uses
crypto.getRandomValues()with rejection sampling for cryptographically secure IDs - No Sensitive Data: Avoid sending passwords, credit card numbers, or PII in event properties
Best Practice:
// Store tracking key in environment variables
const config = {
trackingKey: process.env.REACT_APP_TRACKING_KEY,
};
// Don't do this:
const config = {
trackingKey: 'tk_live_hardcoded_key', // Visible in source code
};Performance Tips
- Batch Events: Queue events and flush periodically instead of individual requests
- Limit Super Properties: Register only essential global properties
- Use Appropriate Batch Size: Balance between network overhead and queue memory
SolapiCRM.init({
trackingKey: 'tk_live_...',
batchSize: 20, // Flush after 20 events (default: 10)
flushInterval: 10000, // Flush every 10 seconds (default: 5s)
});- Clean Up: Call
destroy()when SDK is no longer needed
Storage
The SDK uses browser localStorage to persist:
| Key | Value | Purpose |
|-----|-------|---------|
| _solapi_anon_id | Randomly generated ID | Persistent anonymous user ID |
| _solapi_record_id | Record ID from identify() | Persistent identified user |
Note: Storage is not used if localStorage is unavailable (e.g., private mode, iframe with sandbox).
Error Handling
The SDK is designed to never break the host application:
try {
// Always safe — errors are caught and logged
SolapiCRM.track('event_name', { customData: true });
} catch (error) {
// This rarely happens (only on init)
console.error('SolapiCRM error:', error);
}Network Failures: Events are retried up to 3 times with exponential backoff. On persistent failure, events are dropped and logged (if debug is enabled).
Initialization: Only init() throws errors if required config is missing.
Changelog
0.1.0 (Initial Release)
- Event tracking with custom properties
- Page view tracking for SPAs
- Record creation and identification
- Super properties support
- Opt-in/opt-out GDPR compliance
- Automatic batching and retry logic
- TypeScript support
- Browser SDK distribution (IIFE + ESM)
License
MIT
한국어
SOLAPI CRM 브라우저 SDK
웹 브라우저에서 SOLAPI CRM의 사용자 이벤트를 추적하고 레코드를 생성하기 위한 가볍고 본격적인 JavaScript SDK입니다.
- 홈페이지: https://solapi.com
- 트래킹 키 발급: SOLAPI 콘솔 > CRM > 설정 > SDK
- API 문서: https://developers.solapi.com/references/crm-core
주요 기능
- 이벤트 추적: 유연한 속성을 가진 사용자 정의 이벤트 추적
- 자동 페이지 뷰 추적: 단일 페이지 애플리케이션(SPA)의 사용자 네비게이션 모니터링
- 레코드 생성: 프론트엔드에서 직접 CRM 레코드 생성
- 세션 식별: 익명 세션을 알려진 레코드와 연결
- 슈퍼 속성: 모든 이벤트와 함께 전송되는 전역 속성 등록
- 자동 배칭: 효율적인 네트워크 사용을 위한 이벤트 큐잉 및 배칭
- 오프라인 복원력: 네트워크를 사용할 수 없을 때 로컬에서 이벤트 대기
- GDPR 준수: 추적의 전체 옵트인/옵트아웃 제어
- TypeScript 지원: 완전한 타입 정의 포함
- 의존성 없음: 최소 번들 크기 (~2KB minified+gzipped)
- 보안: HTTPS만 지원, 프로토타입 오염 방지, 암호학적 ID 생성
설치
npm
npm install @solapi/crm-browserYarn
yarn add @solapi/crm-browserpnpm
pnpm add @solapi/crm-browserCDN (Script 태그)
<script src="https://cdn.solapi.com/mfe/sdk/crm-sdk/solapi-crm.min.js"></script>
<script>
SolapiCRM.init({ trackingKey: 'tk_live_...' });
</script>빠른 시작
1. SDK 초기화
항상 추적 키를 사용하여 먼저 init()을 호출합니다:
import SolapiCRM from '@solapi/crm-browser';
SolapiCRM.init({
trackingKey: 'tk_live_your_tracking_key_here',
entityId: 'entity_abc123', // 선택사항: 이벤트 기본 개체
autoPageView: true, // 선택사항: 페이지 뷰 자동 추적
debug: true, // 선택사항: 디버그 로깅 활성화
});필수: trackingKey — SOLAPI 콘솔 > CRM > 설정 > SDK에서 발급
2. 이벤트 추적
// 간단한 이벤트
SolapiCRM.track('signup');
// 속성이 있는 이벤트
SolapiCRM.track('purchase', {
product: 'Premium Plan',
amount: 79000,
currency: 'KRW',
});
// 분석 옵션이 있는 이벤트
SolapiCRM.track('button_click', {}, {
category: 'engagement',
label: 'subscribe_button',
value: 1,
});3. 사용자 식별
사용자 로그인 후 세션을 CRM 레코드와 연결합니다:
// 사용자 로그인 후
const record = await SolapiCRM.createRecord({
name: 'John Doe',
data: { email: '[email protected]' },
tags: ['premium'],
});
// 세션을 레코드와 연결
await SolapiCRM.identify(record.recordId, {
email: '[email protected]',
plan: 'premium',
});4. 슈퍼 속성 등록
모든 이벤트와 함께 동일한 속성을 전송합니다:
SolapiCRM.register({
plan: 'premium',
region: 'KR',
appVersion: '1.2.0',
});설정
init() 메서드는 설정 객체를 허용합니다:
interface SolapiCRMConfig {
// 필수
trackingKey: string;
// 선택사항
entityId?: string; // 이벤트/레코드의 기본 개체 ID
endpoint?: string; // API 엔드포인트 (기본값: https://api.solapi.com/crm-core/v1/sdk)
autoPageView?: boolean; // init 시 자동으로 페이지 뷰 추적 (기본값: false)
batchSize?: number; // 자동 플러시 전 큐잉할 이벤트 수 (기본값: 10)
flushInterval?: number; // 자동 플러시 간격(밀리초) (기본값: 5000)
maxQueueSize?: number; // 최대 큐 크기, 초과 시 가장 오래된 항목 삭제 (기본값: 1000)
debug?: boolean; // 디버그 로깅 활성화 (기본값: false)
}설정 예제
프로덕션 설정:
SolapiCRM.init({
trackingKey: 'tk_live_xxxxxxxx',
entityId: 'entity_main_app',
batchSize: 20, // 효율성을 위한 더 큰 배치
flushInterval: 10000, // 덜 빈번한 플러시
});자동 추적을 사용한 개발:
SolapiCRM.init({
trackingKey: 'tk_live_xxxxxxxx',
autoPageView: true,
debug: true, // 모든 이벤트 로깅 확인
});API 참조
init(config)
설정 객체로 SDK를 초기화합니다.
SolapiCRM.init({
trackingKey: 'tk_live_xxxxxxxx',
entityId: 'entity_abc123',
autoPageView: true,
});예외: trackingKey가 누락되거나 유효하지 않으면 Error 발생.
참고: init()을 다시 호출하면 이전 인스턴스를 정리하고 다시 시작합니다.
track(eventType, properties?, options?)
사용자 정의 이벤트를 추적합니다.
SolapiCRM.track('button_click', {
buttonId: 'submit_btn',
pageSection: 'checkout',
}, {
category: 'engagement',
label: 'checkout_submit',
value: 1,
});매개변수:
eventType(문자열, 필수): 이벤트 이름 (예:'purchase','login')properties(객체, 선택): 사용자 정의 이벤트 데이터options(객체, 선택):category(문자열): 분석을 위한 이벤트 카테고리label(문자열): 이벤트 레이블value(숫자): 이벤트의 수치 값recordId(문자열): 이 이벤트만 현재 레코드 ID 무시
예외: SDK가 초기화되지 않았거나 eventType이 비어있으면 Error 발생.
동작: 옵트아웃이 활성화되면 이벤트가 자동으로 삭제됩니다.
trackPageView()
페이지 뷰를 수동으로 추적합니다.
SolapiCRM.trackPageView();동작:
- 현재 URL, 경로, 페이지 제목 및 리퍼러를 캡처합니다
eventType: 'PAGE_VIEW'인 이벤트로 전송됩니다- 비 SPA 앱의 수동 추적에 유용합니다
예외: SDK가 초기화되지 않으면 Error 발생.
enableAutoPageView()
자동 SPA 라우트 변경 추적을 설치합니다.
SolapiCRM.init({ trackingKey: 'tk_live_...' });
SolapiCRM.enableAutoPageView();동작:
history.pushState()와history.replaceState()패치popstate이벤트 리스너 (브라우저 뒤로/앞으로)- 라우트 변경 시 새로운 페이지 뷰 추적
- 여러 번 호출해도 안전 (한 번만 설치)
createRecord(data)
새로운 CRM 레코드를 생성합니다.
const record = await SolapiCRM.createRecord({
entityId: 'entity_customers',
name: 'John Doe',
data: {
email: '[email protected]',
phone: '010-1234-5678',
source: 'web_signup',
},
tags: ['vip', 'early_adopter'],
});
console.log(record.recordId); // 'record_xyz789'
console.log(record.entityId); // 'entity_customers'
console.log(record.name); // 'John Doe'매개변수:
entityId(문자열, 선택): 레코드의 개체 유형 (설정 entityId 기본값)name(문자열, 필수): 레코드 이름 (예: 사용자 이름, 회사명)data(객체, 선택): 사용자 정의 레코드 필드tags(문자열[], 선택): 레코드 구성을 위한 레이블
반환: Promise<CreateRecordResponse>
interface CreateRecordResponse {
recordId: string; // 고유 레코드 ID
entityId: string; // 개체 유형
name: string; // 레코드 이름
[key: string]: any; // 추가 응답 필드
}예외: SDK가 초기화되지 않았거나 name이 누락되면 Error 발생.
identify(recordId, traits?)
현재 세션을 알려진 CRM 레코드와 연결합니다.
// 기본 식별
await SolapiCRM.identify('record_abc123');
// 특성 업데이트 포함
await SolapiCRM.identify('record_abc123', {
email: '[email protected]',
plan: 'premium',
lastLogin: new Date().toISOString(),
});매개변수:
recordId(문자열, 필수): 식별할 CRM 레코드 IDtraits(객체, 선택): 레코드에 업데이트할 사용자 속성
동작:
- 모든 이전 익명 이벤트를 이 레코드로 병합 (서버 쪽)
- localStorage에 레코드 ID를 저장하여 세션 연속성 유지
- 네트워크 실패는 로깅되지만 절대 발생하지 않음 (호스트 앱 안전)
- 알려진 ID에서 다른 ID로 전환 시 특성과 함께 식별 요청 전송
예외: SDK가 초기화되지 않았거나 recordId가 비어있으면 Error 발생.
모범 사례: 사용자 로그인 후 또는 레코드가 알려져 있을 때 호출합니다.
register(properties)
모든 후속 이벤트와 함께 전송되는 슈퍼 속성을 등록합니다.
SolapiCRM.register({
plan: 'premium',
region: 'KR',
userAgent: navigator.userAgent,
});
SolapiCRM.track('purchase', { amount: 79000 });
// 전송된 이벤트는 다음을 포함: plan, region, userAgent, amount매개변수:
properties(객체): 모든 향후 이벤트에 첨부할 키-값 쌍
동작:
- 속성은 메모리에만 저장됨 (페이지 새로고침 또는
reset()시 삭제) - 이후 호출이 중복 키에 대한 이전 값을 덮어씁니다
- 프로토타입 오염 안전 (
__proto__,constructor,prototype차단)
registerOnce(properties)
슈퍼 속성을 등록하되, 이미 설정된 키를 건너뜁니다.
SolapiCRM.registerOnce({
firstVisit: new Date().toISOString(),
source: 'google_ads',
});
// 이후 호출은 이를 덮어쓰지 않음
SolapiCRM.registerOnce({
firstVisit: '2099-01-01', // 건너뜀 (이미 설정됨)
source: 'facebook_ads', // 건너뜀 (이미 설정됨)
});매개변수:
properties(객체): 아직 존재하지 않으면 설정할 키-값 쌍
unregister(propertyName)
슈퍼 속성을 제거합니다.
SolapiCRM.unregister('tempFlag');매개변수:
propertyName(문자열): 제거할 키
optOutTracking()
모든 이벤트 추적을 옵트아웃합니다.
SolapiCRM.optOutTracking();
// optInTracking()을 호출할 때까지 이벤트가 전송되지 않음동작:
- 모든
track()호출이 자동으로 반환됨 - 이벤트 큐 타이머 중지
- 메모리에 저장됨 (저장 안 함; 페이지 새로고침 시 재설정)
사용 사례: 사용자 개인정보 보호 설정 또는 GDPR 동의 존중.
optInTracking()
옵트아웃 후 이벤트 추적을 재개합니다.
SolapiCRM.optInTracking();동작:
- 이벤트 수락 및 전송 재개
- 이벤트 큐 타이머 재시작
hasOptedOut()
현재 옵트아웃 상태를 확인합니다.
if (SolapiCRM.hasOptedOut()) {
console.log('추적이 비활성화됨');
}반환: boolean — 옵트아웃된 경우 true, 추적이 활성인 경우 false.
reset()
사용자 세션 식별을 재설정합니다.
// 사용자 로그아웃 시
SolapiCRM.reset();동작:
- 새로운 익명 ID 생성
- 모든 슈퍼 속성 삭제
- localStorage에서 지속된 레코드 ID 삭제
- 향후 이벤트는 새 익명 ID로 전송됨
사용 사례: 사용자 로그아웃 또는 세션 재설정 처리.
flush()
모든 큐 이벤트를 즉시 서버로 전송합니다.
await SolapiCRM.flush();동작:
- 자동 플러시 간격 무시
- 모든 재시도 후 지수 백오프 최대 3회 재시도 (1초, 2초, 4초)
- 이벤트를 최대 100개 그룹으로 배치
- 모든 재시도 후 지속적 네트워크 오류 시 삭제
사용 사례: 페이지 언로드 전에 이벤트가 전송되는지 확인 (자동 처리됨).
destroy()
SDK 인스턴스를 정리하고 삭제합니다.
await SolapiCRM.destroy();동작:
- 모든 남은 큐 이벤트 플러시
- 패치된 경우 원본
history.pushState/replaceState복원 - 모든 이벤트 리스너 제거
- 타이머 중지
- 내부 상태 삭제
사용 사례: 단일 페이지 앱 라우트 변경 또는 앱 언마운트 시 정리.
일반적인 패턴
React 통합
// App.tsx
import { useEffect } from 'react';
import SolapiCRM from '@solapi/crm-browser';
export function App() {
useEffect(() => {
// 마운트 시 초기화
SolapiCRM.init({
trackingKey: import.meta.env.VITE_TRACKING_KEY,
entityId: 'entity_web_app',
autoPageView: true,
});
// 언마운트 시 정리
return () => {
SolapiCRM.destroy();
};
}, []);
return <div>Your app</div>;
}사용자 인증 흐름
// 성공적인 로그인 후
const user = await loginUser(credentials);
const record = await SolapiCRM.createRecord({
name: user.fullName,
data: {
email: user.email,
role: user.role,
},
});
await SolapiCRM.identify(record.recordId, {
email: user.email,
role: user.role,
});
// 이제 모든 이벤트가 이 사용자와 연결됨
SolapiCRM.track('login_success');SPA 라우트 네비게이션
// 라우터에서 (예: React Router)
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import SolapiCRM from '@solapi/crm-browser';
export function RouteTracker() {
const location = useLocation();
useEffect(() => {
// 각 라우트 변경 시 수동 페이지 뷰
SolapiCRM.trackPageView();
}, [location]);
return null;
}
// 또는 자동 추적을 위해 enableAutoPageView() 사용
SolapiCRM.enableAutoPageView();GDPR 옵트아웃 동의 배너
function ConsentBanner() {
const handleOptOut = () => {
localStorage.setItem('tracking_consent', 'denied');
SolapiCRM.optOutTracking();
};
const handleOptIn = () => {
localStorage.setItem('tracking_consent', 'accepted');
SolapiCRM.optInTracking();
};
return (
<div className="consent-banner">
<p>분석을 통해 환경을 개선하기 위해 사용합니다.</p>
<button onClick={handleOptOut}>거부</button>
<button onClick={handleOptIn}>승인</button>
</div>
);
}
// 앱 로드 시 저장된 동의 존중
const consent = localStorage.getItem('tracking_consent');
if (consent === 'denied') {
SolapiCRM.optOutTracking();
}Vue.js 통합
// main.ts
import { createApp } from 'vue';
import SolapiCRM from '@solapi/crm-browser';
import App from './App.vue';
const app = createApp(App);
SolapiCRM.init({
trackingKey: import.meta.env.VITE_TRACKING_KEY,
entityId: 'entity_vue_app',
});
app.config.globalProperties.$crm = SolapiCRM;
app.mount('#app');이벤트 속성 모범 사례
// 좋은 예: 명확한 소문자 키, 최소 중첩
SolapiCRM.track('add_to_cart', {
product_id: '123',
product_name: 'Laptop',
price: 1500000,
currency: 'KRW',
quantity: 1,
});
// 피해야 할 것: 중첩 객체, 일관성 없는 이름, 과도한 데이터
SolapiCRM.track('add_to_cart', {
product: {
id: '123',
name: 'Laptop',
pricing: {
amount: 1500000,
},
},
ProductQuantity: 1, // 일관성 없는 대소문자
});브라우저 호환성
SDK는 최신 브라우저 API가 필요합니다. 최소 지원:
| 브라우저 | 최소 버전 | |---------|----------| | Chrome | 57+ | | Firefox | 52+ | | Safari | 11+ | | Edge | 79+ | | iOS Safari | 11+ | | Android Chrome | 57+ |
필수 API:
fetch()crypto.getRandomValues()localStorage(선택; 우아하게 폴백)history.pushState/replaceState(SPA 추적용)
우아한 폴백:
localStorage를 사용할 수 없으면 (개인 모드, iframe 샌드박스) ID는 세션별로 생성됨- 기록 API 패칭이 실패하면 수동
trackPageView()호출은 여전히 작동 - 네트워크 오류는 포착되고 로깅됨; 호스트 앱이 절대 충돌하지 않음
TypeScript 지원
내보낸 타입의 완전한 TypeScript 지원:
import type { SolapiCRMConfig, TrackEvent } from '@solapi/crm-browser';
import SolapiCRM from '@solapi/crm-browser';
const config: SolapiCRMConfig = {
trackingKey: 'tk_live_...',
entityId: 'entity_main',
debug: true,
};
SolapiCRM.init(config);
interface CustomEvent extends TrackEvent {
customProperty: string;
}디버그 모드
문제를 해결하기 위해 자세한 로깅을 활성화합니다:
SolapiCRM.init({
trackingKey: 'tk_live_...',
debug: true,
});
// 콘솔 출력:
// [SolapiCRM] Event queued: purchase
// [SolapiCRM] Flushing 5 events...
// [SolapiCRM] Events sent successfully보안 고려사항
- HTTPS만: SDK는 HTTPS 전송을 강제함 (개발용 localhost 제외)
- 추적 키 보안: 공개 저장소에
trackingKey를 노출하지 마세요. 환경 변수를 사용하세요. - 프로토타입 오염:
__proto__,constructor,prototype같은 속성 키는 자동으로 차단됨 - ID 생성: 암호학적으로 안전한 ID를 위해
crypto.getRandomValues()를 거부 샘플링과 함께 사용 - 민감한 데이터 없음: 이벤트 속성에 비밀번호, 신용카드 번호 또는 PII를 보내지 마세요
모범 사례:
// 환경 변수에 추적 키 저장
const config = {
trackingKey: process.env.REACT_APP_TRACKING_KEY,
};
// 이렇게 하지 마세요:
const config = {
trackingKey: 'tk_live_hardcoded_key', // 소스 코드에 표시됨
};성능 팁
- 배치 이벤트: 개별 요청 대신 이벤트를 큐잉하고 주기적으로 플러시합니다
- 슈퍼 속성 제한: 필수 전역 속성만 등록합니다
- 적절한 배치 크기 사용: 네트워크 오버헤드와 큐 메모리의 균형
SolapiCRM.init({
trackingKey: 'tk_live_...',
batchSize: 20, // 20개 이벤트 후 플러시 (기본값: 10)
flushInterval: 10000, // 10초마다 플러시 (기본값: 5초)
});- 정리: SDK가 더 이상 필요하지 않을 때
destroy()호출
저장소
SDK는 브라우저 localStorage를 사용하여 다음을 저장합니다:
| 키 | 값 | 목적 |
|----|------|------|
| _solapi_anon_id | 무작위로 생성된 ID | 지속적 익명 사용자 ID |
| _solapi_record_id | identify()의 레코드 ID | 지속적 식별 사용자 |
참고: localStorage를 사용할 수 없으면 저장소가 사용되지 않음 (예: 개인 모드, 샌드박스가 있는 iframe).
오류 처리
SDK는 호스트 애플리케이션을 절대 손상시키지 않도록 설계되었습니다:
try {
// 항상 안전 — 오류는 포착되고 로깅됨
SolapiCRM.track('event_name', { customData: true });
} catch (error) {
// 이것은 드물게 발생 (init에서만)
console.error('SolapiCRM error:', error);
}네트워크 실패: 이벤트는 지수 백오프로 최대 3회 재시도됩니다. 지속적 실패 시 이벤트가 삭제되고 로깅됨 (디버그가 활성화된 경우).
초기화: trackingKey가 누락된 경우에만 init()이 오류를 발생시킵니다.
변경 로그
0.1.0 (초기 릴리스)
- 사용자 정의 속성을 사용한 이벤트 추적
- SPA를 위한 페이지 뷰 추적
- 레코드 생성 및 식별
- 슈퍼 속성 지원
- GDPR 준수 옵트인/옵트아웃
- 자동 배칭 및 재시도 로직
- TypeScript 지원
- 브라우저 SDK 배포 (IIFE + ESM)
라이선스
MIT
