Optimizely 커넥터
Tajo를 통해 Optimizely Feature Experimentation을 Brevo에 연결하여 실험 결과를 동기화하고, 기능 플래그 세그먼트별로 캠페인을 타겟팅하며, A/B 테스트 데이터와 오디언스 인사이트로 마케팅 자동화를 강화하십시오.
개요
| 속성 | 값 |
|---|---|
| 플랫폼 | Optimizely |
| 카테고리 | 실험 (Custom) |
| 설정 복잡도 | 중간 |
| 공식 통합 | 아니오 |
| 동기화 데이터 | 실험, 오디언스, 이벤트, 기능 플래그 |
| 인증 방법 | 개인 액세스 토큰 / OAuth 2.0 |
기능
- 실험 동기화 - A/B 테스트 변형 할당을 Brevo 연락처 속성으로 푸시
- 오디언스 타겟팅 - Optimizely 오디언스를 Brevo 캠페인 세그먼테이션에 사용
- 전환 추적 - Optimizely 이벤트를 추적하고 Brevo 이벤트 추적에 매핑
- 기능 플래그 동기화 - 활성화된 기능 플래그별로 연락처 세그먼트화
- 결과 보고 - 실험 후 분석 마케팅 캠페인을 위해 실험 결과 동기화
- 다중 프로젝트 지원 - 여러 Optimizely 프로젝트를 단일 Tajo 인스턴스에 연결
사전 요구 사항
시작하기 전에 다음이 준비되어 있는지 확인하십시오.
- Optimizely Feature Experimentation 계정
- Optimizely 앱 설정의 개인 액세스 토큰
- Optimizely 환경의 SDK 키
- API 접근이 가능한 Brevo 계정
- 커넥터 권한이 있는 Tajo 계정
인증
개인 액세스 토큰
# https://app.optimizely.com/v2/accountsettings/tokens 에서 생성export OPTIMIZELY_ACCESS_TOKEN=your_personal_access_tokenexport OPTIMIZELY_SDK_KEY=your_sdk_keyexport TAJO_API_KEY=your_tajo_api_keyexport BREVO_API_KEY=your_brevo_api_key// 모든 REST API 요청은 Bearer 토큰 인증 사용const headers = { 'Authorization': `Bearer ${process.env.OPTIMIZELY_ACCESS_TOKEN}`, 'Content-Type': 'application/json'};SDK 인증
// 기능 플래그 평가를 위해 SDK 사용const optimizelySDK = require('@optimizely/optimizely-sdk');
const optimizelyClient = optimizelySDK.createInstance({ sdkKey: process.env.OPTIMIZELY_SDK_KEY, datafileOptions: { autoUpdate: true, updateInterval: 60000 // 1분 }});
await optimizelyClient.onReady();구성
기본 설정
connectors: optimizely: enabled: true access_token: "${OPTIMIZELY_ACCESS_TOKEN}" sdk_key: "${OPTIMIZELY_SDK_KEY}" project_id: "12345678"
sync: experiments: true audiences: true events: true feature_flags: true schedule: "0 */2 * * *" # 2시간마다
mapping: experiment_variation: EXPERIMENT_VARIATION feature_flags: ENABLED_FEATURES audience_segments: OPT_SEGMENTS필드 매핑
field_mapping: user_id: email experiment_key: EXPERIMENT_NAME variation_key: VARIATION_NAME feature_key: FEATURE_FLAG enabled: FEATURE_ENABLED audience_name: AUDIENCE_SEGMENT decision_timestamp: EXPERIMENT_DATEAPI 엔드포인트
| 엔드포인트 | 메서드 | 설명 |
|---|---|---|
https://api.optimizely.com/v2/projects | GET | 프로젝트 목록 |
https://api.optimizely.com/v2/experiments | GET | 실험 목록 |
https://api.optimizely.com/v2/experiments/{id} | GET | 실험 세부 정보 가져오기 |
https://api.optimizely.com/v2/experiments/{id}/results | GET | 실험 결과 가져오기 |
https://api.optimizely.com/v2/features | GET | 기능 플래그 목록 |
https://api.optimizely.com/v2/features/{id} | GET | 기능 플래그 가져오기 |
https://api.optimizely.com/v2/audiences | GET | 오디언스 목록 |
https://api.optimizely.com/v2/events | GET | 추적된 이벤트 목록 |
https://logx.optimizely.com/v1/events | POST | 이벤트 추적 (SDK 엔드포인트) |
코드 예제
커넥터 초기화
import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({ apiKey: process.env.TAJO_API_KEY, brevoApiKey: process.env.BREVO_API_KEY});
await tajo.connectors.connect('optimizely', { accessToken: process.env.OPTIMIZELY_ACCESS_TOKEN, sdkKey: process.env.OPTIMIZELY_SDK_KEY, projectId: '12345678'});실험 결정을 Brevo로 동기화
// 실험 결정을 추적하고 Brevo로 동기화const optimizelyClient = optimizelySDK.createInstance({ sdkKey: process.env.OPTIMIZELY_SDK_KEY});
await optimizelyClient.onReady();
// 결정 알림 리스너 등록optimizelyClient.notificationCenter.addNotificationListener( optimizelySDK.enums.NOTIFICATION_TYPES.DECISION, async (decisionObject) => { const { type, userId, attributes, decisionInfo } = decisionObject;
if (type === 'feature' || type === 'ab-test') { const email = attributes.email; if (email) { await tajo.contacts.update(email, { attributes: { EXPERIMENT_NAME: decisionInfo.experimentKey || decisionInfo.featureKey, VARIATION_NAME: decisionInfo.variationKey, FEATURE_ENABLED: decisionInfo.featureEnabled || false, EXPERIMENT_DATE: new Date().toISOString() } }); } } });실험 결과 동기화
// 실험 결과를 가져오고 승리 세그먼트 동기화const resultsResponse = await fetch( `https://api.optimizely.com/v2/experiments/${experimentId}/results`, { headers: { 'Authorization': `Bearer ${process.env.OPTIMIZELY_ACCESS_TOKEN}` } });
const results = await resultsResponse.json();
// 변형 처리 및 연락처 세그먼트 업데이트for (const variation of results.metrics) { const isWinner = variation.is_improvement && variation.statistical_significance >= 0.95;
if (isWinner) { // 승리 변형에 속한 사용자를 위한 Brevo 세그먼트 생성 console.log(`Winning variation: ${variation.variation_name}`); }}기능 플래그 기반 세그먼테이션
// 사용자 세그먼테이션을 위한 기능 플래그 평가async function syncFeatureFlags(userEmail, userId) { const features = ['new_checkout', 'loyalty_program', 'ai_recommendations']; const enabledFeatures = [];
for (const feature of features) { const user = optimizelyClient.createUserContext(userId, { email: userEmail }); const decision = user.decide(feature);
if (decision.enabled) { enabledFeatures.push(feature); } }
await tajo.contacts.update(userEmail, { attributes: { ENABLED_FEATURES: enabledFeatures.join(', '), FEATURE_FLAGS_SYNCED: new Date().toISOString() } });}속도 제한
| 엔드포인트 | 제한 | 참고 |
|---|---|---|
| REST API | 50 요청/분 | 개인 액세스 토큰당 |
| Results API | 10 요청/분 | 더 높은 지연 시간, 무거운 쿼리 |
| SDK 이벤트 디스패치 | 10,000 이벤트/배치 | SDK 이벤트 프로세서를 통해 |
| Datafile CDN | 무제한 | 자동 업데이트로 캐싱됨 |
Results API 지연 시간
Experiment Results API는 대용량 데이터 세트를 처리하므로 응답에 30초 이상 걸릴 수 있습니다. 애플리케이션 차단을 피하려면 비동기 폴링이나 캐싱을 사용하십시오.
문제 해결
| 문제 | 원인 | 해결 방법 |
|---|---|---|
| 401 Unauthorized | 토큰 만료/잘못됨 | 개인 액세스 토큰 재생성 |
| SDK가 준비되지 않음 | Datafile이 로드되지 않음 | onReady() 프라미스가 해결될 때까지 대기 |
| 결정이 로깅되지 않음 | 알림이 등록되지 않음 | 결정을 내리기 전에 리스너 등록 |
| 오래된 기능 플래그 | Datafile 캐시 | 자동 새로 고침을 위해 updateInterval 설정 |
| 결과 누락 | 실험이 시작되지 않음 | 실험 상태가 “running”인지 확인 |
디버그 모드
connectors: optimizely: debug: true log_level: verbose log_decisions: true log_events: true모범 사례
- 결정에 SDK 사용 - 실시간 플래그 평가에는 SDK, 관리에는 REST API 사용
- 이벤트 배치 처리 구현 - 네트워크 오버헤드를 줄이기 위해 SDK 이벤트 배치 처리
- Datafile 캐싱 - 적절한 간격으로 자동 업데이트 활성화
- 승리 변형 동기화 - 실험이 종료된 후 연락처 세그먼트 업데이트
- 타겟팅에 속성 사용 - 오디언스 매칭을 위해 이메일 및 사용자 속성 전달
- 실험 상태 모니터링 - 실행 중이거나 종료된 실험의 데이터만 동기화
보안
- 개인 액세스 토큰 - REST API용 Bearer 토큰 인증
- SDK 키 격리 - 환경별(dev, staging, prod) 별도의 SDK 키
- 서버 측 평가 - 노출을 방지하기 위해 서버 측에서 기능 플래그 평가
- 토큰 교체 - 개인 액세스 토큰을 주기적으로 교체
- 최소 권한 - 쓰기 접근이 필요하지 않을 때는 읽기 전용 토큰 사용
- 암호화된 전송 - 모든 API 및 SDK 통신에 TLS 1.2+ 사용