이 페이지에서

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 플러그인을 추가하고 활성화하십시오.

플러그인 설치
  1. Contentful 마켓플레이스에서 플러그인을 설치합니다.
  2. 플러그인 구성에 실험에서 생성한 데이터 센터, 조직 URL 및 Management API 키를 입력하십시오.
  3. 선택한 환경에 대해 설치를 클릭합니다.
  4. 저장을 클릭하여 플러그인 설정을 완료합니다.

플러그인을 활성화하면 콘텐츠 모델 탭에 Variant Container 콘텐츠 모델이 나타납니다.

콘텐츠 모델에 변형 컨테이너 추가

Contentful의 변형 컨테이너는 Amplitude 실험과 함께 작동하여 각 사용자가 볼 수 있는 실험 변형을 결정합니다.

최상의 결과를 얻으려면 Amplitude는 실험을 진행할 때 참조 콘텐츠 유형을 사용할 것을 권장합니다.

변형 컨테이너를 추가하려면
  1. 변형 컨테이너를 추가할 페이지의 콘텐츠 모델을 엽니다.
  2. 필드 추가를 클릭합니다. 참조 필드를 선택합니다.
  3. 필드의 이름을 입력합니다. 최상의 결과를 얻으려면 필드의 목적과 일치하는 이름을 입력하십시오. 예를 들어, Hero Banner 또는 Demo CTA.
  4. 하나의 참조를 유형으로 선택합니다.
  5. 추가 및 구성을 클릭합니다.
  6. 필드 구성의 이름 및 필드 ID 탭에서 지정된 항목 유형만 허용을 선택한 다음 변형 컨테이너를 선택합니다. 이 설정은 Contentful이 페이지 콘텐츠를 제공할 때 Contentful API 응답을 일관되게 유지합니다.
  7. 확인을 눌러 필드를 생성한 다음 저장을 눌러 콘텐츠 모델을 업데이트합니다.

실험에 콘텐츠 추가

변형 컨테이너와 참조 필드를 구성한 후 실험을 추가할 페이지를 엽니다.

실험에 콘텐츠를 추가하려면
  1. 콘텐츠 편집기에서 해당 페이지를 선택합니다.
  2. 이전 단계에서 만든 필드를 찾아 연관된 콘텐츠 추가 선택기를 클릭합니다.
  3. 추가할 콘텐츠로 변형 컨테이너를 선택합니다.
  4. 필드 구성에서 실험의 플래그 키를 입력합니다. 플래그 키 필드에는 입력할 때 일치하는 키가 표시됩니다.
  5. 플래그 키를 선택하면 해당 키와 연관된 전체 변형이 변형 섹션에 나타납니다.
  6. 각 변형에 대해 기존 항목 링크 또는 새 콘텐츠 유형 생성을 선택하여 변형을 채웁니다.
  7. 게시를 클릭하여 변형 컨테이너를 게시합니다.
  8. 실험이 활성화된 상태에서 업데이트된 페이지를 게시하려면 게시를 클릭합니다.

프런트엔드와 통합

Contentful은 사용자가 페이지를 요청할 때 JSON을 반환합니다.

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 예제를 참조하십시오.

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 을(를) 받는 경우를 고려하십시오.

이 내용이 도움이 되었나요?