Skip to content

시작하기

퍼널 정의, 규칙 설정, coordinator 초기화 세 단계로 첫 넛지를 구현합니다.

설치

bash
pnpm add @liner-engineering/orbit-core

구현 단계

1. 퍼널 정의

순차 단계를 통한 사용자 진행 상황을 추적합니다.

typescript
const paywallExitingThreadFunnel = {
  funnelId: 'paywall--exiting-thread',
  scopes: ['session'],
  steps: [(e) => e.name === 'make_chat', (e) => e.name === 'click_icon_to_main_view'],
};

2. 규칙 설정

트리거 조건, 타겟팅, 노출 제한을 정의합니다.

typescript
const paywallRule = {
  nudgeId: 'paywall',
  when: (t) => t.type === 'funnel_completed' && t.funnelId === 'paywall--exiting-thread',
  cooldownDuration: '3d',
  maxPerWindow: 3,
  windowDuration: '3d',
  personalTiers: ['free'],
  buildAction: () => ({
    type: 'paywall',
    data: { entryType: 'exiting_thread' },
  }),
};

3. Coordinator 초기화

typescript
const coordinator = startCoordinator({
  funnels: [paywallExitingThreadFunnel],
  nudgeRules: [paywallRule],
  runner: {
    run: async (action) => {
      if (action.type === 'paywall') {
        showPaywall(action.data);
      }
    },
  },
  cooldownStorage: new NudgeCooldownStorage({ storage: localStorage }),
  exposureStorage: new NudgeExposureLogStorage({ storage: localStorage }),
});

4. 이벤트 추적

typescript
const tracker = generateEventTracker({
  getUserId: () => currentUser.id,
  getSessionId: () => currentSession.id,
});

tracker.track('make_chat');
tracker.track('click_icon_to_main_view');

React 통합

typescript
import { useEffect, useRef } from 'react';

function App() {
  const coordinatorRef = useRef(null);

  useEffect(() => {
    coordinatorRef.current = startCoordinator({
      funnels: [paywallExitingThreadFunnel],
      nudgeRules: [paywallRule],
      runner: { run: async (action) => { /* handle action */ } },
      cooldownStorage: new NudgeCooldownStorage({ storage: localStorage }),
      exposureStorage: new NudgeExposureLogStorage({ storage: localStorage }),
    });

    return () => coordinatorRef.current?.cleanup();
  }, []);

  return <YourApp />;
}

React Native 통합 (MMKV)

typescript
import { useEffect, useRef } from 'react';
import { MMKV } from 'react-native-mmkv';

const storage = new MMKV();

class MMKVStorageAdapter {
  get(key) {
    const value = storage.getString(key);
    return value ? JSON.parse(value) : undefined;
  }
  set(key, value) {
    storage.set(key, JSON.stringify(value));
  }
  delete(key) {
    storage.delete(key);
  }
}

function App() {
  const coordinatorRef = useRef(null);

  useEffect(() => {
    const mmkvStorage = new MMKVStorageAdapter();

    coordinatorRef.current = startCoordinator({
      funnels: [paywallExitingThreadFunnel],
      nudgeRules: [paywallRule],
      runner: { run: async (action) => { /* 액션 처리 */ } },
      cooldownStorage: new NudgeCooldownStorage({ storage: mmkvStorage }),
      exposureStorage: new NudgeExposureLogStorage({ storage: mmkvStorage }),
    });

    return () => coordinatorRef.current?.cleanup();
  }, []);

  return <YourApp />;
}

전체 예제

typescript
import {
  startCoordinator,
  generateEventTracker,
  NudgeCooldownStorage,
  NudgeExposureLogStorage,
} from '@liner-engineering/orbit-core/nudge-coordinator';

const funnel = {
  funnelId: 'paywall--exiting-thread',
  scopes: ['session'],
  steps: [(e) => e.name === 'make_chat', (e) => e.name === 'click_icon_to_main_view'],
};

const rule = {
  nudgeId: 'paywall',
  when: (t) => t.type === 'funnel_completed' && t.funnelId === 'paywall--exiting-thread',
  cooldownDuration: '3d',
  maxPerWindow: 3,
  windowDuration: '3d',
  personalTiers: ['free'],
  buildAction: () => ({ type: 'paywall', data: { entryType: 'exiting_thread' } }),
};

const coordinator = startCoordinator({
  funnels: [funnel],
  nudgeRules: [rule],
  runner: { run: (action) => action.type === 'paywall' && showPaywall(action.data) },
  cooldownStorage: new NudgeCooldownStorage({ storage: localStorage }),
  exposureStorage: new NudgeExposureLogStorage({ storage: localStorage }),
});

const tracker = generateEventTracker({
  getUserId: () => 'user123',
  getSessionId: () => 'session456',
});

tracker.track('make_chat');
tracker.track('click_icon_to_main_view');

다음 단계

Released under the MIT License.