npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vircle/sdk-web

v0.4.1

Published

Vircle SDK for Web applications

Readme

@vircle/sdk-web

VircleCore를 확장하여 웹 환경에 특화된 기능을 제공하는 웹 애플리케이션용 Vircle Analytics SDK입니다. 자동 추적, 브라우저 컨텍스트 수집, 웹 스토리지 지원, 하이브리드 암호화 등 웹 환경에 최적화된 기능을 제공합니다.

주요 기능

핵심 기능

  • VircleCore 상속: 코어 SDK의 모든 기능 + 웹 특화 기능
  • 자동 추적: 페이지뷰, 클릭, 폼 제출, JavaScript 에러 자동 감지
  • SPA 지원: History API 래핑으로 라우트 변경 감지
  • 웹 컨텍스트 수집: 브라우저, OS, 디바이스, UTM 파라미터 등

성능 최적화

  • requestIdleCallback: UI 블로킹 없는 백그라운드 처리
  • 컨텍스트 캐싱: 반복적인 DOM 접근 방지
  • 스마트 스토리지: IndexedDB/LocalStorage 자동 선택으로 대용량 데이터 지원
  • 이벤트 배칭: 네트워크 요청 최소화
  • 큐 크기 제한: TaskScheduler 큐 오버플로우 방지 (기본 1000)

크로스 도메인 어트리뷰션

  • URL _vuid 자동 적용: 아웃링크에 ?_vuid=xxx가 있으면 SDK의 anonymousId를 해당 값으로 교체
  • 라운지→Cafe24 시나리오: 동일 VUID로 크로스 도메인 이벤트 수집

세션 관리

  • 세션 타임아웃: 비활성 상태 감지 후 자동 세션 로테이션 (기본 30분)
  • fetch+keepalive: 페이지 종료 시 Authorization 헤더 포함 이벤트 전송

보안 및 프라이버시

  • 페이로드 암호화: Web Crypto API 기반 하이브리드 암호화 (AES-256-GCM + RSA-OAEP)
  • 추적 제외: data-vircle-ignore 속성으로 민감 정보 보호
  • 안전한 에러 처리: SDK 안정성을 위한 모든 외부 API 호출 보호
  • 전용 에러 클래스: 상세한 에러 코드 및 디버깅 정보 제공

설치

npm install @vircle/sdk-web
# 또는
yarn add @vircle/sdk-web
# 또는
pnpm add @vircle/sdk-web

예제 실행

예제는 packages/demo-web 디렉토리에서 확인할 수 있습니다:

# 저장소 클론 후
cd packages/demo-web

# ESM 예제
open esm/index.html

# React 예제
cd react && pnpm install && pnpm start

# Next.js 예제
cd nextjs && pnpm install && pnpm dev

자세한 내용은 demo-web README를 참조하세요.

빠른 시작

기본 사용법

import { VircleWeb } from '@vircle/sdk-web';

// SDK 초기화 (VircleCore 설정 + 웹 전용 설정)
const vircle = new VircleWeb({
    apiKey: 'your-api-key',
    environment: 'production',

    // 배치 처리 설정 (VircleCore 기능)
    batch: {
        size: 50,           // 기본값: 50
        timeout: 5000,      // 기본값: 5000ms
        flushInterval: 10000, // 기본값: 10000ms
    },

    // 웹 전용 자동 추적 설정
    trackPageViews: true,
    trackClicks: true,
    trackForms: true,
    trackErrors: true,
    singlePageApp: true,

    // 암호화 활성화
    enableEncryption: true,

    // 스토리지 설정
    storageType: 'auto',
    storagePrefix: 'vircle_',
}, {
    // 웹 전용 옵션
    flushOnUnload: true,
    contextCacheTime: 300000,
});

// SDK 초기화 필수!
await vircle.initialize();

// 커스텀 이벤트 추적
await vircle.track('button_clicked', {
    button_id: 'purchase',
    value: 99.99,
});

// 사용자 식별 (identify 이벤트로 서버 전송)
await vircle.identify('user-123', {
    email: '[email protected]',
    name: '홍길동',
});

ES Modules (브라우저)

<!-- 방법1: CDN 사용 (standalone 빌드 - 모든 의존성 포함) -->
<script type="module">
    import { VircleWeb } from 'https://static.vircle.co.kr/sdk/v1/js/vircle-web-sdk.standalone.esm.js';

    const vircle = new VircleWeb({
        apiKey: 'your-api-key',
        environment: 'production',
        trackPageViews: true,
        trackClicks: true,
    });

    await vircle.initialize();
</script>

<!-- 방법2: 로컬 파일 사용 -->
<script type="module" src="/dist/vircle-web-sdk.standalone.esm.js"></script>

UMD (레거시 지원)

<script src="https://static.vircle.co.kr/sdk/v1/js/vircle-web-sdk.min.js"></script>
<script>
    // UMD 빌드는 VircleWebSDK 전역 변수로 VircleWeb 클래스를 직접 노출합니다
    const vircle = new VircleWebSDK({
        apiKey: 'your-api-key',
    });

    vircle.initialize().then(() => {
        console.log('SDK 초기화 완료');
    });
</script>

아키텍처

┌────────────────────────────────────────────┐
│                  VircleWeb                 │
│         (VircleCore 상속 + 웹 전용 기능)       │
├────────────────────────────────────────────┤
│  • 자동 추적 (페이지뷰/클릭/폼/에러/SPA)       │
│  • 웹 컨텍스트 수집 (WebContextCollector)      │
│  • 세션 타임아웃 / 로테이션                      │
│  • fetch+keepalive (페이지 이탈 보호)            │
│  • StorageFactory (IndexedDB/LocalStorage) │
│  • 하이브리드 암호화 (WebExtendedCryptoAdapter)│
│  • 에러 클래스 (VircleWebError 계열)           │
└────────────────────┬───────────────────────┘
                     │ extends
┌────────────────────┴───────────────────────┐
│                 VircleCore                 │
│  (이벤트/전송/플러그인/암호화/원격 설정/스토리지) │
└────────────────────────────────────────────┘

웹 전용 기능 상세

자동 추적 시스템

SDK는 setupAutoTracking 메서드를 통해 다양한 브라우저 이벤트를 자동으로 추적합니다:

페이지뷰 추적

  • 초기 로드: 페이지 첫 로드 시 자동 추적
  • SPA 라우트 변경: History API (pushState, replaceState) 래핑
  • 브라우저 네비게이션: popstate 이벤트 감지
  • 추적 데이터: URL, 제목, 리퍼러, UTM 파라미터

클릭 추적

  • 전역 이벤트 위임: document에 단일 리스너로 모든 클릭 감지
  • 요소 정보 수집: 태그명, 텍스트, 클래스, ID
  • 추적 대상: A, BUTTON, INPUT, SELECT, TEXTAREA 요소만 추적
  • 추적 제외: data-vircle-ignore 속성으로 민감 요소 제외
  • data- 속성 수집*: idle 시간에 비동기로 추가 data 속성 수집

폼 제출 추적

  • 자동 감지: 모든 form 태그의 submit 이벤트
  • 수집 정보: 폼 ID, name, action, method
  • 보안: 폼 데이터는 수집하지 않음

에러 추적

  • JavaScript 에러: ErrorEvent 리스너 (메시지, 파일명, 라인, 컬럼, 스택)
  • Promise 거부: PromiseRejectionEvent 리스너 (reason)

웹 컨텍스트 수집 (WebContextCollector)

collect() 호출 시 device, page, app, custom 4가지 컨텍스트를 병렬로 수집합니다.

device (DeviceContext)

User-Agent 기반 디바이스/브라우저/OS 감지 및 화면 정보 수집

| 필드 | 타입 | 소스 | 설명 | |------|------|------|------| | type | 'mobile' \| 'tablet' \| 'desktop' | navigator.userAgent | UA 패턴 매칭으로 판별 | | os | string | navigator.userAgent | Windows, macOS, Linux, Android, iOS, Chrome OS | | osVersion | string \| undefined | navigator.userAgent | OS 버전 (예: 14.0) | | browser | string | navigator.userAgent | Edge, Chrome, Firefox, Safari, Opera, IE | | browserVersion | string \| undefined | navigator.userAgent | 브라우저 버전 (예: 120.0) | | screenResolution | string | screen.width/height | "1920x1080" 형태 | | viewport | { width, height } | window.innerWidth/Height | 현재 뷰포트 크기 | | language | string | navigator.language | 브라우저 언어 (예: ko-KR) | | timezone | string | Intl.DateTimeFormat | IANA 타임존 (예: Asia/Seoul) |

page (PageContext)

현재 페이지 URL 파싱 결과

| 필드 | 타입 | 소스 | 설명 | |------|------|------|------| | url | string | window.location.href | 전체 URL | | title | string | document.title | 페이지 제목 | | path | string | URL.pathname | 경로 (예: /products) | | search | string | URL.search | 쿼리스트링 (예: ?category=electronics) | | hash | string | URL.hash | 해시 프래그먼트 |

app (AppContext)

애플리케이션 메타 정보 (HTML meta 태그 기반)

| 필드 | 타입 | 소스 | 설명 | |------|------|------|------| | name | string | <meta name="application-name"> 또는 <meta property="og:site_name"> 또는 document.title | 앱 이름 | | version | string \| undefined | <meta name="version"> | 앱 버전 | | environment | 'development' \| 'production' | window.location.hostname | localhost/staging → development, 그 외 → production |

campaign (CampaignContext)

URL의 UTM 파라미터 및 광고 클릭 ID를 수집합니다. sessionStorage에 저장하여 MPA 페이지 전환 간에도 유지됩니다.

| 필드 | 타입 | 조건 | 설명 | |------|------|------|------| | source | string | utm_source 존재 시 | 트래픽 소스 (예: google) | | medium | string | utm_medium 존재 시 | 트래픽 매체 (예: cpc) | | campaign | string | utm_campaign 존재 시 | 캠페인 이름 | | term | string | utm_term 존재 시 | 검색 키워드 | | content | string | utm_content 존재 시 | 광고 콘텐츠 구분 | | gclid | string | gclid 존재 시 | Google Ads 클릭 ID | | fbclid | string | fbclid 존재 시 | Facebook 클릭 ID | | msclkid | string | msclkid 존재 시 | Microsoft Ads 클릭 ID | | ttclid | string | ttclid 존재 시 | TikTok 클릭 ID |

custom (Record<string, unknown>)

추가 브라우저 환경 정보 (가용한 경우에만 수집)

| 필드 | 타입 | 조건 | 설명 | |------|------|------|------| | referrer | string | document.referrer 존재 시 | 이전 페이지 URL | | cookieEnabled | boolean | 항상 | 쿠키 활성화 여부 | | doNotTrack | boolean | navigator.doNotTrack 존재 시 | DNT 설정 여부 | | connection | { effectiveType, downlink, rtt, saveData } | Network Information API 지원 시 | 네트워크 연결 정보 | | memory | { usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit } | performance.memory 지원 시 (Chrome 전용) | JS 힙 메모리 사용량 |

수집 결과 예시

{
  device: {
    type: 'desktop',
    os: 'macOS',
    osVersion: '14.0',
    browser: 'Chrome',
    browserVersion: '120.0',
    screenResolution: '1920x1080',
    viewport: { width: 1200, height: 800 },
    language: 'ko-KR',
    timezone: 'Asia/Seoul'
  },
  page: {
    url: 'https://example.com/products?category=electronics',
    title: '제품 목록',
    path: '/products',
    search: '?category=electronics',
    hash: ''
  },
  app: {
    name: 'My Store',
    version: '2.1.0',
    environment: 'production'
  },
  campaign: {
    source: 'google',
    medium: 'cpc',
    campaign: 'summer_sale'
  },
  custom: {
    referrer: 'https://google.com',
    cookieEnabled: true,
    doNotTrack: false,
    connection: {
      effectiveType: '4g',
      downlink: 10.0,
      rtt: 50,
      saveData: false
    },
    memory: {
      usedJSHeapSize: 12345678,
      totalJSHeapSize: 33554432,
      jsHeapSizeLimit: 2147483648
    }
  }
}

스토리지 어댑터

SDK는 StorageFactory를 통해 환경에 최적화된 스토리지를 자동 선택합니다:

IndexedDB 어댑터 (권장)

  • 대용량 지원: LocalStorage 5MB 제한 없이 대량 이벤트 저장
  • 비동기 처리: UI 블로킹 없는 백그라운드 저장
  • 트랜잭션 지원: 데이터 무결성 보장
  • LRU 정리: 타임스탬프 인덱스 기반 오래된 데이터 자동 정리

LocalStorage 어댑터

  • 자동 용량 관리: QuotaExceededError 시 LRU 캐시 정리
  • 접두사 격리: vircle_ 접두사로 다른 데이터와 분리
  • 타임스탬프 추적: 데이터 저장 시간 기록
  • 안전한 폴백: LocalStorage 불가 시 경고 후 계속 작동

스토리지 타입 선택

const vircle = new VircleWeb({
    apiKey: 'your-api-key',
    storageType: 'auto', // 'auto' | 'localStorage' | 'indexedDB'
});

| 타입 | 설명 | |------|------| | auto (기본값) | IndexedDB 우선, 미지원 시 LocalStorage 폴백 | | localStorage | 항상 LocalStorage 사용 | | indexedDB | 항상 IndexedDB 사용 |

StorageFactory는 동기(create) 및 비동기(createAsync) 생성을 모두 지원합니다. 비동기 방식은 Safari Private 모드 등에서 IndexedDB 실제 사용 가능 여부를 테스트한 후 안전하게 폴백합니다.

암호화 (WebExtendedCryptoAdapter)

Web Crypto API 기반의 하이브리드 암호화를 제공합니다:

데이터 (JSON) → AES-256-GCM 암호화 → 암호문 + Auth Tag
AES 키 → RSA-OAEP (서버 공개키) → 암호화된 키
결과: { data, key, iv, authTag, metadata }

암호화 활성화

const vircle = new VircleWeb({
    apiKey: 'your-api-key',
    enableEncryption: true, // Web Crypto API 기반 암호화 활성화
});

제공 기능

  • 하이브리드 암호화: AES-256-GCM (데이터) + RSA-OAEP (키 래핑)
  • UUID v4 생성: crypto.randomUUID 또는 getRandomValues 폴백
  • HMAC-SHA256 서명: 페이로드 무결성 검증
  • 자동 가용성 감지: Web Crypto API 미지원 시 암호화 자동 비활성화

암호화된 페이로드 구조

interface EncryptedPayload {
    data: string;     // Base64 인코딩된 AES-GCM 암호문
    key: string;      // Base64 인코딩된 RSA-OAEP 암호화 AES 키
    iv: string;       // Base64 인코딩된 초기화 벡터 (96-bit)
    authTag: string;  // Base64 인코딩된 GCM 인증 태그 (128-bit)
    metadata: {
        algorithm: 'AES-256-GCM';
        keyAlgorithm: 'RSA-OAEP';
        timestamp: string;
    };
}

세션 타임아웃

SDK는 사용자 비활성 상태를 감지하여 자동으로 새 세션을 시작합니다:

  • 기본 타임아웃: 30분 (설정 가능)
  • 동작 방식: visibilitychange → visible 이벤트 시 마지막 활동 시간과 비교
  • 세션 로테이션: 타임아웃 경과 시 새 세션 ID 생성 + session_start 이벤트 자동 추적
  • 활동 갱신: track() 호출 시마다 마지막 활동 시간 갱신
const vircle = new VircleWeb({
    apiKey: 'your-api-key',
    sessionTimeout: 15 * 60 * 1000, // 15분
});

fetch+keepalive (페이지 이탈 보호)

페이지 종료 시 fetch() + keepalive: true 옵션을 사용하여 대기 중인 이벤트를 안전하게 전송합니다:

  • beforeunload: 페이지 닫기/새로고침 시 큐의 이벤트를 keepalive fetch로 전송
  • visibilitychange → hidden: 탭 전환/최소화 시 keepalive fetch로 전송
  • 장점: Authorization 헤더를 포함할 수 있어 인증된 요청으로 전송 가능
  • 폴백: keepalive 미지원 환경에서는 자동으로 무시됩니다
const vircle = new VircleWeb(
    { apiKey: 'your-api-key' },
    { flushOnUnload: true }, // 기본값: true
);

에러 클래스

SDK는 디버깅을 위한 구조화된 에러 클래스를 제공합니다:

| 에러 클래스 | 코드 | 설명 | |------------|------|------| | VircleWebError | - | 기본 에러 클래스 (코드, 상세정보 포함) | | VircleConfigError | CONFIG_ERROR | 잘못된 설정 (API 키 누락 등) | | VircleInitializationError | INITIALIZATION_ERROR | 초기화 실패 | | VircleStorageError | STORAGE_ERROR | 스토리지 작업 실패 | | VircleBrowserCompatibilityError | BROWSER_COMPATIBILITY_ERROR | 브라우저 미지원 기능 |

import { VircleWebError, VircleConfigError } from '@vircle/sdk-web';

try {
    const vircle = new VircleWeb({ apiKey: '' });
    await vircle.initialize();
} catch (error) {
    if (error instanceof VircleConfigError) {
        console.error('설정 오류:', error.code, error.details);
    }
}

성능 최적화 기술

  • requestIdleCallback 활용: TaskScheduler를 통한 UI 블로킹 없는 백그라운드 이벤트 처리
  • 컨텍스트 캐싱: ContextCache를 통한 브라우저 정보 캐싱 (기본 5분), 중복 수집 방지를 위한 Promise 재사용
  • 이벤트 배칭: TransportService의 큐를 통한 네트워크 요청 최소화
  • 메모리 관리: cleanup 시 모든 이벤트 리스너 제거 및 History API 원복으로 누수 방지
  • 페이지 이탈 대응: fetch()+keepalive를 사용한 페이지 종료 시 안정적 이벤트 전송
  • 큐 크기 제한: TaskScheduler의 maxQueueSize(기본 1000)로 메모리 오버플로우 방지

추적 제외

특정 요소나 영역을 추적에서 제외하려면 data-vircle-ignore 속성을 사용하세요:

<!-- 이 버튼은 클릭 추적에서 제외됩니다 -->
<button data-vircle-ignore>추적 제외 버튼</button>

<!-- 이 영역 내의 모든 요소가 추적에서 제외됩니다 -->
<div data-vircle-ignore>
    <button>추적 안됨</button>
    <a href="#">이 링크도 추적 안됨</a>
</div>

설정 옵션

VircleWebConfig

VircleConfig (코어)를 상속하며 웹 전용 옵션을 추가합니다.

interface VircleWebConfig extends VircleConfig {
    trackPageViews?: boolean;    // 자동 페이지뷰 추적 (기본: false)
    trackClicks?: boolean;       // 자동 클릭 추적 (기본: false)
    trackForms?: boolean;        // 자동 폼 제출 추적 (기본: false)
    trackErrors?: boolean;       // 자동 에러 추적 (기본: false)
    singlePageApp?: boolean;     // SPA 모드 (기본: false)
    storagePrefix?: string;      // 스토리지 키 접두사 (기본: 'vircle_')
    storageType?: StorageType;   // 스토리지 타입: 'auto' | 'localStorage' | 'indexedDB' (기본: 'auto')
    enableEncryption?: boolean;  // Web Crypto API 암호화 활성화 (기본: false)
    sessionTimeout?: number;     // 세션 타임아웃(ms) (기본: 1800000 = 30분)
}

VircleConfig (코어) 주요 옵션

| 옵션 | 타입 | 기본값 | 설명 | |------|------|--------|------| | apiKey | string | (필수) | API 인증 키 | | environment | 'development' \| 'production' | 'production' | 환경 설정 | | debug | boolean | false | 디버그 모드 | | batch.size | number | 50 | 배치당 최대 이벤트 수 | | batch.timeout | number | 5000 | 배치 대기 시간(ms) | | batch.flushInterval | number | 10000 | 자동 플러시 간격(ms) | | retry.maxAttempts | number | 3 | 최대 재시도 횟수 | | retry.initialDelay | number | 500 | 초기 백오프 지연(ms) | | privacy.respectDoNotTrack | boolean | true | DNT 헤더 존중 | | headers | Record<string, string> | - | 커스텀 HTTP 헤더 |

VircleWebOptions

interface VircleWebOptions extends Partial<VircleCoreOptions> {
    flushOnUnload?: boolean;   // 페이지 언로드 시 fetch+keepalive 전송 (기본: true)
    contextCacheTime?: number; // 컨텍스트 캐시 시간(ms) (기본: 300000 = 5분)
}

API 레퍼런스

초기화

await vircle.initialize(): Promise<void>

SDK를 초기화하고 자동 추적을 시작합니다.

이벤트 추적

await vircle.track<TProperties>(
  name: string,
  properties?: TProperties,
  context?: EventContext
): Promise<void>

커스텀 이벤트를 추적합니다. requestIdleCallback을 활용하여 UI를 블로킹하지 않으며, 호출 시마다 세션의 마지막 활동 시간이 갱신됩니다.

페이지뷰 추적

await vircle.trackPageView(
  properties?: Record<string, unknown>,
  context?: EventContext
): Promise<void>

페이지뷰를 수동으로 추적합니다. URL, 제목, 리퍼러 등의 페이지 컨텍스트를 자동 수집합니다.

사용자 식별

await vircle.identify<TTraits>(
  userId: string,
  traits?: TTraits
): Promise<void>

사용자를 식별하고 속성을 설정합니다. VircleWeb에서는 호출 시 컨텍스트 캐시를 자동으로 무효화합니다.

사용자 리셋

await vircle.reset(): Promise<void>

사용자 정보와 세션을 초기화합니다.

유틸리티

// 현재 페이지 URL
vircle.getCurrentUrl(): string

// 네트워크 연결 상태
vircle.isOnline(): boolean

// 크로스 도메인 어트리뷰션용 anonymousId
vircle.getAnonymousId(): string | undefined

// SDK 상태 확인
vircle.getStatus(): {
    isInitialized: boolean;
    sessionId?: string;
    userId?: string;
    pluginCount: number;
    queueSize: number;
}

// 런타임 메트릭 조회
vircle.getMetrics(): {
    queueSize: number;
    isProcessing: boolean;
}

// 이벤트 수동 플러시
await vircle.flush(): Promise<void>

// 이벤트 수동 플러시 (타임아웃 지정)
await vircle.flush(timeoutMs?: number): Promise<void>

정리

await vircle.cleanup(): Promise<void>

SDK가 사용하는 모든 리소스를 정리합니다:

  1. 이벤트 리스너 제거 (beforeunload, error, click, submit, popstate, visibilitychange)
  2. History API 원복 (pushState, replaceState)
  3. 대기 중인 이벤트 전송 (flush())
  4. 코어 서비스 정리 (super.cleanup())

Exports

// 메인 SDK 클래스
import { VircleWeb } from '@vircle/sdk-web';
import VircleWeb from '@vircle/sdk-web'; // default export

// 설정 타입
import type { VircleWebConfig, VircleWebOptions } from '@vircle/sdk-web';

// 컨텍스트 수집기
import { WebContextCollector } from '@vircle/sdk-web';

// 스토리지
import { LocalStorageAdapter, IndexedDBAdapter, StorageFactory } from '@vircle/sdk-web';
import type { StorageType } from '@vircle/sdk-web'; // 'auto' | 'localStorage' | 'indexedDB'

// 암호화 어댑터
import { WebExtendedCryptoAdapter } from '@vircle/sdk-web';

// 에러 클래스
import {
    VircleWebError,
    VircleConfigError,
    VircleInitializationError,
    VircleStorageError,
    VircleBrowserCompatibilityError,
} from '@vircle/sdk-web';

// Core 타입 재수출
import type {
    VircleConfig,
    EventContext,
    DeviceContext,
    PageContext,
    AppContext,
    UserTraits,
    StorageAdapter,
} from '@vircle/sdk-web';

빌드 파일 설명

| 파일 | 포맷 | 용도 | @vircle/sdk-core-ts | 사용 방법 | |------|------|------|----------------------|-----------| | dist/index.js | CJS | 번들러 (Webpack 등) | 외부 의존성 | require('@vircle/sdk-web') | | dist/index.esm.js | ESM | 번들러 (Vite, Rollup 등) | 외부 의존성 | import { VircleWeb } from '@vircle/sdk-web' | | dist/vircle-web-sdk.standalone.esm.js | ESM | 브라우저 직접 사용 | 번들에 포함 | <script type="module"> | | dist/vircle-web-sdk.min.js | UMD | 레거시 브라우저 | 번들에 포함 | <script src="..."> | | dist/index.d.ts | DTS | TypeScript 타입 정의 | - | 자동 적용 |

중요: 브라우저에서 직접 사용할 때는 반드시 standalone 또는 min 빌드를 사용하세요. npm 패키지로 사용 시 번들러가 @vircle/sdk-core-ts를 함께 번들링합니다.

브라우저 지원

  • Chrome/Edge: 최신 2개 버전
  • Firefox: 최신 2개 버전
  • Safari: 최신 2개 버전
  • iOS Safari: iOS 12+
  • Chrome for Android: Android 6+

암호화 기능(enableEncryption)은 Web Crypto API를 지원하는 브라우저에서만 동작합니다. 미지원 시 자동으로 비활성화됩니다.

보안 고려사항

  1. 클라이언트 측 API 키: 브라우저에서 사용되는 API 키는 공개됩니다. 데이터 수집(Write) 권한만 가진 키를 사용하세요.

  2. 민감한 데이터: 민감한 데이터가 수집되지 않도록 data-vircle-ignore 속성을 적절히 사용하세요.

  3. 스토리지: SDK는 LocalStorage/IndexedDB를 사용하여 데이터를 임시 저장합니다. 민감한 정보는 저장하지 않습니다.

  4. 암호화: enableEncryption: true 설정 시 서버 공개키로 이벤트 페이로드를 암호화합니다. 공개키는 CDN(RemoteConfigService)에서 자동 fetch됩니다.

  5. 페이지 이탈 전송: 페이지 종료 시 fetch()+keepalive로 전송되며, Authorization 헤더를 포함한 인증된 요청으로 전송됩니다. 정상 동작과 동일한 fetch() 기반 전송을 사용합니다.

성능 영향

  • 번들 크기 (standalone): ~106KB (minified, core 포함), npm 패키지는 ~70KB (core 외부 의존성)
  • 이벤트 추적: requestIdleCallback 기반으로 UI 블로킹 없음
  • 컨텍스트 수집: 캐싱으로 반복적인 DOM 접근 방지 (기본 5분)

고급 사용 예제

SPA (Single Page Application) 설정

// singlePageApp: true 설정 시 History API (pushState, replaceState)를 래핑하여
// 라우트 변경을 자동으로 감지하고 페이지뷰를 추적합니다.
// React Router, Vue Router, Next.js 등 대부분의 SPA 프레임워크와 호환됩니다.
const vircle = new VircleWeb({
    apiKey: 'your-api-key',
    singlePageApp: true,     // History API 래핑
    trackPageViews: true,    // 자동 페이지뷰 추적
});

await vircle.initialize();
// 이후 별도의 설정 없이 라우트 변경 시 자동으로 page_view 이벤트가 추적됩니다.

에러 처리와 함께 사용

import { VircleWeb, VircleInitializationError } from '@vircle/sdk-web';

try {
    const vircle = new VircleWeb({
        apiKey: 'your-api-key',
        trackErrors: false, // 자동 에러 추적 비활성화
    });

    await vircle.initialize();

    // 수동으로 에러 추적
    try {
        await riskyOperation();
    } catch (error) {
        await vircle.track('error', {
            message: error.message,
            stack: error.stack,
            component: 'PaymentForm',
        });
        throw error;
    }
} catch (error) {
    if (error instanceof VircleInitializationError) {
        console.error('SDK 초기화 실패:', error.code, error.details);
    }
}

암호화 활성화

const vircle = new VircleWeb({
    apiKey: 'your-api-key',
    enableEncryption: true, // Web Crypto API 가용 시 자동 활성화
    trackPageViews: true,
});

await vircle.initialize();

// 이후 모든 이벤트가 하이브리드 암호화되어 전송됩니다
await vircle.track('purchase', { amount: 99.99 });

성능 최적화 설정

const vircle = new VircleWeb(
    {
        apiKey: 'your-api-key',
        trackPageViews: true,
        trackClicks: true,
        sessionTimeout: 15 * 60 * 1000, // 15분 세션 타임아웃
    },
    {
        contextCacheTime: 600000, // 10분 캐싱 (기본 5분)
        flushOnUnload: true,      // fetch+keepalive로 페이지 이탈 시 자동 전송 (기본값)
    },
);

// 중요한 시점에 수동으로 flush (예: 결제 완료 후)
await vircle.flush();

민감한 정보가 있는 페이지

<!-- 개인정보가 포함된 대시보드 -->
<div class="user-dashboard">
    <!-- 사용자 정보는 추적에서 제외 -->
    <h1 data-vircle-ignore>안녕하세요, 김철수님!</h1>

    <!-- 계좌 정보는 추적에서 완전 제외 -->
    <div data-vircle-ignore>
        <p>계좌번호: 123-456-789</p>
        <p>잔액: ₩1,234,567</p>
        <button>송금하기</button>
    </div>

    <!-- 일반 액션은 추적 -->
    <button>프로필 수정</button>
</div>

동적 컨텍스트 추가

// 글로벌 컨텍스트 설정
vircle.setContext({
    app: {
        version: '2.1.0',
        environment: 'production',
    },
    custom: {
        experiment_id: 'exp_123',
        feature_flags: {
            new_ui: true,
            beta_features: false,
        },
    },
});

// 특정 이벤트에만 컨텍스트 추가
await vircle.track(
    'purchase_completed',
    {
        product_id: 'sku_123',
        price: 49.99,
    },
    {
        custom: {
            payment_method: 'card',
            coupon_used: true,
        },
    },
);

문제 해결

SDK가 초기화되지 않음

// 상태 확인
const status = vircle.getStatus();
console.log('SDK 초기화 상태:', status.isInitialized);

// 네트워크 상태 확인
console.log('온라인 상태:', vircle.isOnline());

이벤트가 전송되지 않음

// 디버그 모드로 재초기화
const vircle = new VircleWeb({
    apiKey: 'your-api-key',
    debug: true, // 콘솔에 디버그 로그 출력
});

// 수동으로 이벤트 플러시
await vircle.flush();

메모리 누수 방지

// React 예제 - 컴포넌트 언마운트 시 반드시 cleanup 호출
// 일반적으로 SDK는 앱 레벨에서 한 번만 초기화합니다
useEffect(() => {
    const vircle = new VircleWeb({
        apiKey: 'your-api-key',
        trackPageViews: true,
    });
    vircle.initialize();

    return () => {
        // cleanup()은 이벤트 리스너 제거, History API 원복, 큐 플러시를 수행합니다
        vircle.cleanup();
    };
}, []);

암호화 관련

// 암호화 미지원 환경에서 enableEncryption: true로 설정한 경우
// 콘솔에 경고 메시지가 출력되고 암호화 없이 동작합니다
// "[Vircle] Web Crypto API를 사용할 수 없습니다. 암호화를 비활성화합니다."

TypeScript 지원

SDK는 완전한 TypeScript 타입 정의를 제공합니다:

import {
    VircleWeb,
    VircleWebConfig,
    VircleWebOptions,
    WebExtendedCryptoAdapter,
    VircleWebError,
} from '@vircle/sdk-web';
import type { EventContext, UserTraits, StorageType } from '@vircle/sdk-web';

// 타입 안전한 설정
const config: VircleWebConfig = {
    apiKey: process.env.VIRCLE_API_KEY!,
    trackPageViews: true,
    trackErrors: true,
    enableEncryption: true,
    storageType: 'auto',
};

const options: VircleWebOptions = {
    contextCacheTime: 300000,
    flushOnUnload: true,
};

const vircle = new VircleWeb(config, options);

// 타입 안전한 이벤트 추적
// track()의 제네릭 파라미터는 Record<string, unknown>을 확장해야 합니다
interface PurchaseProperties {
    product_id: string;
    price: number;
    currency: string;
    quantity: number;
}

await vircle.track<PurchaseProperties>('purchase', {
    product_id: 'sku_123',
    price: 49.99,
    currency: 'USD',
    quantity: 1,
});

// 타입 안전한 사용자 속성
// identify()의 제네릭 파라미터는 Record<string, unknown>을 확장해야 합니다
interface CustomUserTraits {
    email: string;
    plan: 'free' | 'pro' | 'enterprise';
    created_at: string;
}

await vircle.identify<CustomUserTraits>('user-123', {
    email: '[email protected]',
    plan: 'pro',
    created_at: new Date().toISOString(),
});

라이선스

MIT