jtwjs Dev Wiki
  • DEV_ROAD
    • 💪🏻 생존하기
    • Week 1
      • 개발 환경 세팅
      • 타입스크립트
      • 리엑트
      • Testing Library
      • Parcel & ESLint
    • Week 2
      • JSX
      • Virtual DOM
    • Week 3
      • React Component
      • React State
    • Week 4
      • Express
      • Fetch API & CORS
      • React Hook
      • useRef & Custom Hook
    • Week 5
      • TDD
      • React Testing Library
      • MSW
      • Playwrite
      • Snapshot
    • Week 6
      • Separtion of Concerns
      • Principle
      • DI, (Dependency Injection)
      • Reflect-metadata
      • TSyringe
      • External Store
      • Follow Redux
      • usestore-ts
      • useSyncExternalStore
    • Week 7
      • Routing
      • Routes
      • Router
      • Navigation
    • Week 8
      • Design System
      • Style Basics
      • CSS-in-JS
      • Styled-Components
      • Global Style & Theme
    • Week 9
      • 개발하기 전 준비
      • 상품 목록 페이지
      • 상품 상세 페이지
      • 장바구니 페이지
    • Week 10
      • 로그인
      • 로그아웃
      • 회원가입
      • 주문 목록 & 주문 상세
    • Week 11
      • 배송 정보 입력
      • 포트원 결제 요청
      • 배송 및 결제 정보 전달
    • Week 12
      • 관리자 웹사이트개발시작
  • DEV_NOTE
    • TypeScript
      • 기본적 문법
        • Enum
        • 다형성
          • Untitled
        • 구조적 타이핑
        • 제너릭 타입
        • 컨디셔널 타입
        • 함수 메서드 타이핑
        • infer로 타입스크립트의 추론 직접 활용
        • 재귀 타입
        • 템플릿 리터럴 타입
        • 추가적인 타입 검사 satisfies 연산자
        • 타입스크립트 건망증
        • 원시 자료형에도 브랜딩 기법 사용 가능
        • 타입 좁히기
        • 유용한 타입 만들기
        • 데코레이터 함수
        • 앰비언트 선언도 선언 병합이 된다.
        • 앰비언트 선언도 선언병합이 된다.
    • Testing
      • Unit Testing
      • 단위 테스트의 두 분파
      • 좋은 단위 테스트를 구성하는 4대 요소
      • 테스트 대역과 식별할 수 있는 동작
      • 단위 테스트 스타일
      • 가치 있는 단위 테스트를 위한 리팩토링
      • 통합 테스트
      • Cross Browsing Testing
      • 기능 테스트 종류
      • React Testing Pattern
      • 프론트엔드 테스트 입문
        • 테스트 범위
        • 단위 테스트 검증
        • Mock
        • UI 컴포넌트 테스트
        • 테스트 커버리지
        • 웹 통합 테스트
        • MSW
        • 스토리북
        • 시각적 회귀 테스트
        • E2E 테스트
        • Github Actions 설정
        • 깃허브 액션에서 E2E
      • 시프트 레프트
        • 테스트 기본중의 기본
        • 단위 테스트
        • 코드 복잡도
        • 리팩터링
        • 코드 리뷰
        • 통합 테스트 패턴
        • 시스템 테스트의 자동화
        • 탐색적 테스트
      • Test Tip
      • vitest
      • playwright
      • Test Data Generator
      • MSW
    • Algorithm
      • coding test
      • Data Structure
    • Next.js
      • Data Fetching
      • Hydration
      • Next 13
      • Optimization
      • Next 15
    • Tailwind
      • Tailwind CSS
      • Theme
      • Directives
      • Tool
      • Design System
    • Storybook
      • Storybook
      • CSF3
      • CDD
      • Headless Component
    • Funtional Programming
      • 함수형 프로그래밍
      • 참조 투명성
      • 부수효과
      • 함수 합성
      • 제너릭 타입 활용하기
      • 암묵적 입출력
      • 액션과 계산, 데이터
      • 계층형 설계
      • 호출 그래프
      • 함수형 설계
      • 불변성
      • 일급 함수
      • 함수형 도구
    • Git
      • Github Actions
      • Conflict
      • Branch 전략
    • Contents Format
      • Audio
    • 3D Graphic
      • 3D keyword
      • Three.js
      • Geometry
      • Material
      • Light
      • Camera
      • Decal
      • Rotation
      • Text
      • Shadow
      • Fog
      • Post Processing
      • Animation
      • Math
        • Vector Space
        • 벡터의 연산
        • 회전 계산
      • 3D 컨텐츠가 만들어지는 과정
      • R3F
      • Env
      • Scene
      • Transform
      • R3F
      • Interaction & Raycast
      • Rendering Algorithnm
      • Blender
      • Blender
    • Accessibility
      • 접근성이란
    • Interactive Web
      • Parallax
      • Canvas
      • requestAnimationFrame
      • Effect
      • HSL
      • React.js + Canvas
      • Matter.js
    • AWS
      • DevOps
      • Amplify
      • S3
      • 클라우드 컴퓨팅
        • 온프레미스와 클라우드
        • 클라우드 도입효과
        • 클라우드 컴퓨팅의 범위
        • 컴퓨팅 옵션
          • EC2 - Virtual Machin
          • ECS, EKS - Container
          • Lambda - Serverless
        • 네트워크 가상화
        • 스토리지
        • 데이터베이스
        • 데이터 수집
        • 머신 러닝 영역
        • IoT 영역
        • 블록체인 영역
      • 클라우드 아키텍처 설계
    • Network
      • Web Server & WAS
    • System Design
      • System Design
      • Component
      • 의존성을 배제한 개발
      • Error Handling
      • Architecture
        • 모노로틱 아키텍처
        • Clean Architecture
        • Layered Architecture
        • 이벤트 기반 아키텍처
      • 상황을 파악하는 메타인지
      • 중복 문제 해결하기
      • Monorepo Arhitecture
        • 모노레포 운영과 트러블슈팅
        • Module Federation
      • 코드 병목지점
      • API 대응
      • 공통 코드
      • Infra 구축
      • 모듈 기반의 개발 방식
      • Design System
        • 최소 수준의 아키텍처 설정
        • 더 효율적인 디자인시스템 만들기
        • 디자인 시스템과 UI 라이브러리 목적
        • 디자인 토큰
      • 효율적인 업무
        • 업무 프로세스 병목 파악
      • Clean Code
      • Design Pattern
        • CQRS Pattern
        • Strangler Fig Pattern
        • 데코레이터 패턴
        • 커맨드 패턴
        • 전략 패턴
        • 옵저버 패턴
      • A/B 테스팅
      • 대규모 리엑트 웹앱 개발
        • 복잡성 관리
        • 모듈성
        • 성능
        • 디자인 시스템
        • 데이터 패칭
        • 상태 관리
        • 국제화
        • 코드 조직화하기
        • 개인화 A/B 테스팅
        • 확장 가능한 웹 아키텍처
        • 테스팅
        • 툴링
        • 기술적 마이그레이션
        • 타입스크립트
        • 라우팅
        • 사용자 중심 API 디자인
        • 리액트 미래
    • Performance
      • React DevTools
      • Component 최적화
      • Page Load
      • API
    • MFA
      • MSA
      • MFA 도입하기
      • Monorepo
        • Monorepo Tool
        • Yarn Berry Workspace
        • Turborepo
      • MFA Composition
      • SPA 통합
      • Design System
      • Package Manager
        • Yarn
        • pnpm
      • Transpiler & Bundler
        • Babel
        • Rollup
        • esbuild
        • swc
        • Webpack
        • Vite
      • 분해와 통합을 위한 여러 기술 비교
    • State Management
      • Zustand
    • React v18
      • Automatic batching
      • Suspense
      • Transition
    • SEO
      • Search Engine Optimization
      • Open Graph Element
      • Metadata
    • FE Develop
      • Scrubbing
      • Clipboard
    • Refactoring
      • 리팩토링 깊게 들여다보기
      • 긴 코드 조각내기
      • 타입 코드 처리하기
      • 유사한 코드 융합하기
      • 데이터 보호
      • 코드 추가 및 제거
    • OAuth 2.0
    • Analytics
      • Mixpanel
    • ETC
      • VSCode
    • React Hook In Action
      • useContext & Provider
      • 커스텀 훅
      • 코드 분할하기 with Suspense, lazy
      • Suspense와 이미지 적재하기
      • useTransition, uesDeferredValue
      • SuspenseList
Powered by GitBook
On this page
  • A/B 테스팅
  • A/B 테스팅을 하는 이유
  • A/B 테스팅 구현하기 (with Next.js)
  • Next.js middleware를 이용한 A/B 테스팅
  • 다른 A/B 테스팅과 접근방식 비교
  • A/B 테스팅 구현
  • 미들웨어 추가
  • Variant 구성
  • Configcat을 사용한 구현
  • ConfigCat 구성
  • 개발 피쳐에 대한 검증 및 롤백전략 수립
  • Amplitude?
  • 구성
  • 설치
  • 사용을 위한 커스텀 훅
  • Amplitude로의 테스트 시각화
  1. DEV_NOTE
  2. System Design

A/B 테스팅

A/B 테스팅

레이아웃이나 컨텐츠의 특정 변경이 방문자의 참여도(리텐션,구매율 등)을 어떻게 향상시키는지 평가

리텐션이란 사용자가 특정 제품, 서비스, 플랫폼 등을 얼마나 오랫동안 계속해서 이용하는지를 나타내는 지표

  • A/B 버전을 나눠 사용자의 반응을 측정하는것이 A/B 테스트

    • 트래픽 유도

    • 일관성 유지를 위해 선택 버전 식별자(쿠키) 유지

    • 나갔다 오는 경우, 새로고침에도 동일 컨텐츠(A/B)를 보여주기 위함

A/B 테스팅을 하는 이유

  • 무엇이 더 나은 성과를 가져다 주는지 파악

  • 전환율, 이탈율, 신뢰도, 수익 창출같은 지표를 무엇이 어떻게 유도하는지 파악

  • 보다 나은 결과를 가져다 주는 변화를 적용

  • 데이터 기반의 객관적 의사결정을 하기 위한 수단

A/B 테스팅 구현하기 (with Next.js)

  • Next.js의 middleware 개념 이용

  • 다른 A/B 테스트 프레임워크와 비교할때 개발자 워크플로에 적합

  • Next.js의 성능 이점을 유지

Next.js Middleware란?

  • 사용자가 작성할 수 있는 임의의 함수

  • Http request가 Next.js 서버에 도달하기 전에 실행

  • Vercel과 같은 플랫폼의 클라우드 엣지(i.e CDN)에 배포

    • 서버로 리라우팅

    • 직접 응답해서 서버를 단락

  • 빠른 성능으로 인한 런타임 환경의 제한

  • 빠른 봇 탐지, 피처 플래그, analytics, 라우팅, A/B 테스트 등에 사용

Next.js middleware를 이용한 A/B 테스팅

  • 미들웨어 기능은 Edge(예: CDN)에서 실행

  • 요청을 신속하게 재라우팅

  • SSG와 함께 사용하여 A/B 테스트를 엣지에서 제공

  • 처음 접근시 어떤 페이지를 보여줄지 선택

  • 요청을 변경하여 프록시하고 쿠키를 설정

  • SSG의 경우 Edge를 통해 제공

  • SSR의 경우 서버에서 제공

    • SSR을 사용한다 하더라도 Edge에서 경로가 제공되면 사용자는 컨텐츠의 대한 지연시간이 단축되는 이점을 누릴 수 있음

  • Edge에서 라우팅 시 컨텐츠 지연시간 단축

다른 A/B 테스팅과 접근방식 비교

클라이언트 사이드 A/B testing

  • Google Optimize, Adobe Target, VWO 등 사용

  • 페이지에 스크립트 태그 추가

  • 쉽게 구현

  • 스크립트가 서버에 연결, 버킷 확인, DOM 동적 수정

    • 이로인한 지연 깜빡임 상황 발생

    • Web vitals, SEO에 영향

서버사이드 A/B testing

  • 개발자가 원하는 방식으로 구현 가능

  • A/B 테스트 서비스에 요청하여 버킷 결정, 이벤트 보고

  • 모든 요청이 오리진 서버에 도달해야 함

    • 지연시간 길어질 수 있음

    • 두 사용자가 동일 URL에서 다른 응답을 볼 수 있음

    • CDN 캐싱의 효율이 떨어짐

미들웨어를 이요한 A/B testing

  • 개발자가 원하는 방식으로 구현 가능

  • 엣지에서 버킷화

    • 사용자에게 짧은 지연시간 제공

  • 초기 요청에 대한 버킷 처리

    • 미들웨어가 초기 http 요청에서 바로 사용자 버킷을 결정 -> 추가적인 요청 필요 X

    • 클라이언트 방식 대비 로드시간의 감소

  • Static Site Generation (SSG) 활용

    • page variant가 SSG로 빌드되어 Edge에서 제공시 CDN 성능 이점을 활용해 A/B 테스트를 전적으로 Edge에서만 실행할 수 있음 -> 빠른 응답 가능

A/B 테스팅 구현

미들웨어 추가

  • ex: /marketing/* 경로에 대한 미들웨어 적용

  • 쿠키가 존재한다면 버켓 유지, 아니라면 버켓 선정

  • variant로 프록시하여 응답 rewrite

  • 미들웨어는 올바른 버킷을 선택하고 사용자에게 올바른 바리언트로 투명하게 라우팅

import { NextRequest, NextResponse } from 'next/server'

import COOKIE_NAME = 'bucket-marketing'

// 랜덤 버켓 선택
// Optional: 써드파티 서비스를 이용하여 버킷을 받을 수도 있다.
const MARKETING_BUCKETS = ['original', 'a', 'b'] as const
const getBucket = () => MARKETING_BUCKET[Math.floor(Math.random() * 
MARKETING_BUCKET.length)]

export function middleware(req: NextRequest) {
  if (req.nextUrl.pathname.startWith('/marketing')) {
    const cookies = req.cookies;
    // Get the bucket cookie
    const bucket = cookies.get(COOKIE_NAME)?.value || getBucket()
    
    // 적절한 variant로 프록시
    const res = NextResponse.rewrite(new URL(`/marketing/${bucket}`, req.url))
    
    // 쿠키가 없다면 버킷을 추가
    if (!cookies.get(COOKIE_NAME)) {
      res.cookies.set(COOKIE_NAME, bucket)
    }
    
    return res
  }
}

Variant 구성

테스트 하려는 지면이 props/marketing.tsx 라고 가정 page/marketing 디렉토리 생성 후 해당 지면 이동

  • Variant 구성 (A,B 또는 A,B,C)

  • /pages/marketing/original.tsx

    • variant original (원래구성)

  • /pages/marketing/a.tsx

    • variant a

  • /pages/marketing/b.tsx

    • variant b

  • 키 값에 따라 화면이 달라지며, 쿠키 존재시 동일 버켓으로 rewirte

getStaticProps를 통해 SSG를 사용한다면 최상의 성능을 위해 각각의 변형은 CDN Edge 내에서 제공될것

  • middleware는 동일한 역할

  • configcat의 variant 값들을 불러오는 lib/config.json 은 빌드타임에 구성

import { type NextRequest, NextResponse } from 'next/server';
import { type FlagMatcher, getValue, type Flags } from '@lib/configcat';

const FLAGS: FlagsMatcher = {
  '/about': {
    cookie: 'flag-newAboutPage',
    name: process.env.FEATURE_FLAG_ABOUT_PAGE as Flags,
    rewrite: (enabled) => (enabled ? '/about/b' : '/about'),
  },
  '/marketing': {
    cookie: 'flag-newMarketingPage',
    name: process.env.FEATURE_FLAG_MARKETING_PAGE as Flags,
    rewrite: (enabled) => (enabled ? '/marketing/b' : '/marketing')
  }
}

export const config = {
  matcher: ['/about', '/marketing'],
}

export async function middleware(req: NextRequest) {
  const url = req.nextUrl;
  const flag = FLAGS[url.pathname]
  
  if (!flag) return
  
  const value = req.cookies.get(flag.cookie)?.value || (getValue(flag.name) ? '1' : '0')
  
  // Create a rewrite to the page matching the flag
  url.pahtname = flag.rewrite(value === '1')
  const res = NextResponse.rewrite(url)
  
  // Add the cookie to the response if it's not present
  if (!req.cookies.has(flag.cookie)) {
    res.cookies.set(flag.cookie, value)
  }
  
  return res
}

ConfigCat 구성

  • 회원가입 후 대시보드에서 4개의 FeatureFlag 설정

    • isMyFirstFeatureEnabled (on)

    • clientSideFeatureEnabled (on)

    • newAboutPage

      • 50% 비율로 on/off 결정

    • newMarketingPage

      • 50% 비율로 on/off 결정

개발 피쳐에 대한 검증 및 롤백전략 수립

Amplitude를 이용한 A/B 테스트 결과 수집 및 분석

Amplitude?

  • 서비스의 사용자 행동을 분석할 수 있는 도구

  • 이벤트 기준의 정보, 사용자의 퍼널/리텐션 등을 다양한 방법으로 볼 수 있는 도구

구성

  • 회원가입 후 수집할 이벤트로 visited와 button_clicked를 추가

  • Amplitude의 API Key를 받아 ".env.local"의 NEXT_PUBLIC_AMPLITUDE_API_KEY 항목에 적용

설치

  • @types/amplitude-js로 설치된 type 오류 처리

사용을 위한 커스텀 훅

  • amplitude initialize를 위한 useAmplitudeInit()

  • API Key를 환경변수로부터 받아서 init() 수행

import { useEffect } from 'react';
import amplitude from 'amplitude-js'

const AMPLITUDE_API_KEY = process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY ?? ''

const useAmplitudeInit = () => {
  useEffect(() => {
    amplitude.getInstance().init(AMPLITUDE_API_KEY)
  })
}

export default useAmplitudeInit
  • _app.tsx 에서 initialize 동작 수행

export default function MyApp({ Component, pageProps }: AppProps) {
  const Layout = getLayout<LayoutProps>(Component)
  // Amplitude init
  useAmplitudeInit()
}
  • event를 Amplitude로 보내기 위한 hook 구성

  • About page에서 다음 이벤트를 발송

    • visited

    • button_clicked

import { useCallback } from 'react';
import amplitude from 'amplitude-js';

import Cookies from 'js-cookie'

const useAmplitudeAboutEvents = () => {
  const logVisited = useCallback(() => {
    const value = COokies.get('flag-newAboutPage')
    const identity = new amplitude.Identify();
    identify.set('flag-newAboutPage', value === '1')
    
    amplitude.getInstance().identify(identify);
    amplitude.getInstance().logEvent('visited')
  }, [])
  
  const logButtonCLicked = useCallback(() => {
    amplitude.getInstance().logEvent('button_clicked)
  }, [])
  
  return { logVisited, logButtonClicked }
}

export default useAmplitudeAboutEvents
  • About 페이지에서 visited 이벤트를 처리하기 위한 logVisited() 및 버튼 클릭 이벤트 logButtonClicked() 호출

  • about/index.tsxm about/b.tsx 모두 적용

Amplitude로의 테스트 시각화

Amplitude를 이용하여 test 결과를 시각화하여 표시

Previous옵저버 패턴Next대규모 리엑트 웹앱 개발

Last updated 8 months ago

에서 실행되기에 요청을 신속하게 재작성

을 사용한 구현

amplitude-js SDK 설치:

엣지
Configcat
npm