Skip to content

커스텀 액션

TypeScript 구별 유니온을 사용한 타입 안전한 액션 정의입니다.

액션 타입 정의

typescript
type MyAction =
  | { type: 'paywall'; data: PaywallData }
  | { type: 'show_modal'; nudgeId: string }
  | { type: 'notification'; message: string }
  | { type: 'credit_reward'; points: number };

Coordinator 설정

typescript
const coordinator = startCoordinator<MyAction>({
  nudgeRules: [
    {
      nudgeId: 'test',
      when: (t) => t.type === 'funnel_completed',
      buildAction: (): MyAction => ({
        type: 'paywall',
        data: { entryType: 'test' },
      }),
    },
  ],
});

액션 러너 구현

typescript
const runner = {
  run: async (action: MyAction) => {
    switch (action.type) {
      case 'paywall':
        showPaywall(action.data);
        break;
      case 'show_modal':
        showModal(action.nudgeId);
        break;
      case 'notification':
        sendNotification(action.message);
        break;
      case 'credit_reward':
        grantPoints(action.points);
        break;
    }
  },
};

프로덕션 예제

typescript
type PaywallData = { entryType: string };

type NudgeAction = { type: 'paywall'; data: PaywallData } | { type: 'show_modal'; nudgeId: string };

const paywallRule: NudgeRule<'free' | 'pro', NudgeAction> = {
  nudgeId: 'paywall',
  when: (t) => t.type === 'funnel_completed',
  cooldownDuration: '3d',
  maxPerWindow: 3,
  windowDuration: '3d',
  personalTiers: ['free'],
  buildAction: () => ({
    type: 'paywall',
    data: { entryType: 'exiting_thread' },
  }),
};

const coordinator = startCoordinator<'free' | 'pro', NudgeAction>({
  nudgeRules: [paywallRule],
  runner: {
    run: async (action) => {
      if (action.type === 'paywall') {
        showPaywall(action.data);
      }
    },
  },
});

타입 안전성

typescript
// 유효함
const rule: NudgeRule<any, MyAction> = {
  nudgeId: 'test',
  when: () => true,
  buildAction: () => ({
    type: 'paywall',
    data: { entryType: 'test' },
  }),
};

// TypeScript 에러: 유효하지 않은 액션 타입
const badRule: NudgeRule<any, MyAction> = {
  nudgeId: 'test',
  when: () => true,
  buildAction: () => ({
    type: 'invalid_type', // 에러!
    data: {},
  }),
};

비동기 액션 처리

typescript
const runner = {
  run: async (action: MyAction) => {
    try {
      switch (action.type) {
        case 'credit_reward':
          await database.addPoints(userId, action.points);
          break;
        case 'notification':
          await emailService.send(userId, action.message).catch((err) => {
            console.error('Failed:', err);
          });
          break;
      }
    } catch (error) {
      console.error('Action runner error:', error);
    }
  },
};

다음 단계

Released under the MIT License.