For AI agents: a documentation index is available at /docs/llms.txt. Append .md to any page URL for markdown, or send Accept: text/markdown.
Contentful
Amplitude 실험용 Contentful 플러그인을 사용하면 Contentful에서 콘텐츠 변형을 생성하고, 실험을 사용하여 각 사용자가 볼 수 있는 변형을 제어하고, 해당 변형의 성능을 추적할 수 있습니다.
기능
- Amplitude 실험에서 A/B 테스트를 실행하고 Contentful에서 콘텐츠를 제작하십시오.
- Amplitude 실험의 속성을 읽어들이고(5초마다 새로 고쳐짐), 이러한 속성을 중심으로 콘텐츠를 구축하십시오.
요구 사항
플러그인을 사용하려면 다음 사항이 있어야 합니다.
- Amplitude 실험이 활성화된 Amplitude 플랜에 대한 액세스 권한.
- 귀하의 Amplitude 조직 URL 값입니다. 이 값은 Amplitude에 액세스하는 데 사용하는 URL에 표시됩니다:
https://app.amplitude.com/experiment/<ORG_URL>/dashboard. - 실험 사이드바에서 찾을 수 있는 Management API 키.
설치 및 사용
Contentful 및 Amplitude에서 다음 단계를 완료하여 Amplitude 실험용 Contentful 플러그인을 추가하고 활성화하십시오.
플러그인 설치
- Contentful 마켓플레이스에서 플러그인을 설치합니다.
- 플러그인 구성에 실험에서 생성한 데이터 센터, 조직 URL 및 Management API 키를 입력하십시오.
- 선택한 환경에 대해 설치를 클릭합니다.
- 저장을 클릭하여 플러그인 설정을 완료합니다.
플러그인을 활성화하면 콘텐츠 모델 탭에 Variant Container 콘텐츠 모델이 나타납니다.
콘텐츠 모델에 변형 컨테이너 추가
Contentful의 변형 컨테이너는 Amplitude 실험과 함께 작동하여 각 사용자가 볼 수 있는 실험 변형을 결정합니다.
최상의 결과를 얻으려면 Amplitude는 실험을 진행할 때 참조 콘텐츠 유형을 사용할 것을 권장합니다.
변형 컨테이너를 추가하려면
- 변형 컨테이너를 추가할 페이지의 콘텐츠 모델을 엽니다.
- 필드 추가를 클릭합니다. 참조 필드를 선택합니다.
- 필드의 이름을 입력합니다. 최상의 결과를 얻으려면 필드의 목적과 일치하는 이름을 입력하십시오. 예를 들어,
Hero Banner또는Demo CTA. - 하나의 참조를 유형으로 선택합니다.
- 추가 및 구성을 클릭합니다.
- 필드 구성의 이름 및 필드 ID 탭에서 지정된 항목 유형만 허용을 선택한 다음 변형 컨테이너를 선택합니다. 이 설정은 Contentful이 페이지 콘텐츠를 제공할 때 Contentful API 응답을 일관되게 유지합니다.
- 확인을 눌러 필드를 생성한 다음 저장을 눌러 콘텐츠 모델을 업데이트합니다.
실험에 콘텐츠 추가
변형 컨테이너와 참조 필드를 구성한 후 실험을 추가할 페이지를 엽니다.
실험에 콘텐츠를 추가하려면
- 콘텐츠 편집기에서 해당 페이지를 선택합니다.
- 이전 단계에서 만든 필드를 찾아 연관된 콘텐츠 추가 선택기를 클릭합니다.
- 추가할 콘텐츠로 변형 컨테이너를 선택합니다.
- 필드 구성에서 실험의 플래그 키를 입력합니다. 플래그 키 필드에는 입력할 때 일치하는 키가 표시됩니다.
- 플래그 키를 선택하면 해당 키와 연관된 전체 변형이 변형 섹션에 나타납니다.
- 각 변형에 대해 기존 항목 링크 또는 새 콘텐츠 유형 생성을 선택하여 변형을 채웁니다.
- 게시를 클릭하여 변형 컨테이너를 게시합니다.
- 실험이 활성화된 상태에서 업데이트된 페이지를 게시하려면 게시를 클릭합니다.
프런트엔드와 통합
Contentful은 사용자가 페이지를 요청할 때 JSON을 반환합니다.
{
"__typename": "PageLanding",
"sys": {
"id": "2cayfg7wVF5WezADCHgSgL",
"spaceId": "4y4crvvoco9a"
},
"internalName": "Homepage",
"heroBanner": {
"__typename": "VariationContainer",
"experiment": {
"id": "183980",
"key": "contentful-hero-banner",
"name": "contentful-hero-banner",
"tags": [],
"state": "running",
"deleted": false,
"enabled": true,
"endDate": null,
"decision": null,
"variants": [
{
"key": "control"
},
{
"key": "treatment"
}
],
"projectId": "289220",
"startDate": "2024-02-22",
"deployments": ["14"],
"description": "",
"bucketingKey": "amplitude_id",
"bucketingSalt": "28fWNw1M",
"bucketingUnit": "User",
"decisionReason": null,
"evaluationMode": "remote",
"experimentType": "hypothesis-testing",
"rolloutWeights": {
"control": 1,
"treatment": 1
},
"targetSegments": [],
"stickyBucketing": false,
"rolledOutVariant": null,
"rolloutPercentage": 0
},
"experimentId": "contentful-hero-banner",
"meta": {
"control": "6rmYK8YKYtTkKcFRY9Pf2w",
"treatment": "kwDjI2f2vKE2GvQoeqq1d"
},
"variationsCollection": {
"items": [
{
"__typename": "Hero",
"sys": {
"id": "6rmYK8YKYtTkKcFRY9Pf2w",
"spaceId": "4y4crvvoco9a"
},
"preHeadline": "Organic Products",
"headline": "100% Fresh Food",
"cta": "Shop Now",
"description": "Whatever you want from your local stores, brought right to your door. \t\t\t\t\t\t\t",
"image": {
"__typename": "Asset",
"sys": {
"id": "6PkraSxWWd96AU6FTYVssd"
},
"title": "Fresh food",
"description": "",
"width": 2560,
"height": 960,
"url": "https://images.ctfassets.net/4y4crvvoco9a/6PkraSxWWd96AU6FTYVssd/69b8d7f7cabb9f578097d50f2bf8aa70/Hero-3-1-scaled.jpg",
"contentType": "image/jpeg"
}
},
{
"__typename": "Hero",
"sys": {
"id": "kwDjI2f2vKE2GvQoeqq1d",
"spaceId": "4y4crvvoco9a"
},
"preHeadline": "Exclusive Offer",
"headline": "Loyalty Program",
"cta": "Get Free Shipping",
"description": "We missed you! Finish your order today and get free shipping when you join our loyalty program.",
"image": {
"__typename": "Asset",
"sys": {
"id": "6WFOK0460CwrW7aChl1QjM"
},
"title": "Loyalty Green",
"description": "",
"width": 1920,
"height": 720,
"url": "https://images.ctfassets.net/4y4crvvoco9a/6WFOK0460CwrW7aChl1QjM/e52fb2129b848203f6006ff9881309d9/Hero-_-loyalty-green.jpg",
"contentType": "image/jpeg"
}
}
]
}
}
}
experiment 객체에는 실험에 대한 유용한 메타데이터가 포함되어 있습니다. 프런트 엔드를 렌더링하고 실험을 포함하려면 meta 및 variationsCollection 객체를 사용하십시오. Amplitude 실험은 변형 식별자를 제공하고 이를 meta 오브젝트의 옵션과 일치시킵니다. 변형을 설정한 후 다음을 수행할 수 있습니다.
variationsCollection객체의 항목을 반복하여 일치하는 ID를 가진 변형을 표시합니다.- 배열을 검색하지 않으려면 변형 ID를 사용하여 Contentful에 직접 호출하십시오.
자세한 내용은 다음 React 및 TypeScript 예제를 참조하십시오.
import React, { useEffect, useState } from "react";
import { Experiment } from "@amplitude/experiment-js-client";
import sdk from "contentful-sdk";
// --- Types (adjust to your schema) ---
type Hero = {
__typename: "Hero";
sys: { id: string };
// ...other fields you render
};
type HeroBanner = {
experimentId?: string; // key you use in Amplitude Experiment
meta?: Record<string, string>; // maps variant key -> variation id
variationsCollection?: { items: Array<Hero | null | undefined> };
};
// --- Experiment init ---
const CLIENT_KEY =
process.env.NEXT_PUBLIC_AMPLITUDE_EXPERIMENT_CLIENT_KEY ?? "";
export const experiment = Experiment.initialize(CLIENT_KEY, {
// Only enable verbose logging in dev if you like:
debug: process.env.NODE_ENV !== "production",
});
// --- Component hook snippet ---
export function useHeroVariant(userId: string | undefined) {
const [hero, setHero] = useState<Hero | null>(null);
useEffect(() => {
// Skip until we have a user id
if (!userId) return;
let isMounted = true;
(async () => {
try {
// 1) Fetch variants for this user
await experiment.fetch({ user_id: userId });
// 2) Load the banner/experiment mapping from Contentful
const heroBanner = await sdk.getEntry<HeroBanner>("ENTRY_ID_HERE");
if (!heroBanner) return;
// 3) Resolve the variant key from the experiment
const experimentKey = heroBanner.experimentId ?? "control";
const { value: variantKey } = experiment.variant(experimentKey);
if (!variantKey) return;
// 4) Map the variant key -> variation id through Contentful metadata
const variationId = heroBanner.meta?.[variantKey];
if (!variationId) return;
// 5) Find the matching Hero item
const match =
heroBanner.variationsCollection?.items.find(
(item): item is Hero =>
!!item &&
item.__typename === "Hero" &&
item.sys?.id === variationId,
) ?? null;
if (isMounted) setHero(match);
} catch (err) {
// Consider forwarding to your logger
console.error("Failed to resolve hero variant", err);
if (isMounted) setHero(null);
}
})();
return () => {
isMounted = false;
};
}, [userId]); // Note: don't include heroBanner (it's local) or setHero
return hero;
}
사용자가 experiment.variant() 반환하는 값으로 off 을(를) 받는 경우를 고려하십시오.
이 내용이 도움이 되었나요?