Next.js Middleware로 AB 테스트 구현하기 🍪
서비스를 개선할 때 AB 테스트는 매우 중요한 도구입니다. 오늘은 Next.js의 Middleware를 활용하여 간단하게 AB 테스트를 구현한 경험을 공유하고자 합니다.
AB 테스트는 두 가지 버전(A와 B)의 웹페이지나 기능을 사용자들에게 무작위로 보여주고, 어떤 버전이 더 효과적인지 측정하는 방법입니다.
이번 AB 테스트는 상세페이지 개편 이후 실제 구매로 전환되는 비율에 변화가 있는지를 테스트했습니다. 기존에 Google Tag Manager(GTM)가 연결되어 있었기 때문에 특정 상세페이지에 진입했을 때 50:50으로 각각 다른 페이지로 보내주기만 하면 이를 활용하여 결제 페이지 진입률이나 최종 구매 전환율 등을 추적할 수 있었습니다.
Next.js Middleware로 구현하기
Next.js의 Middleware를 사용하면 요청이 처리되기 전에 서버에서 로직을 실행할 수 있습니다. 이 방법을 활용하면 서버 사이드에서 처리되어 깜빡임이 없고, Cloud Run의 환경변수도 활용하기 용이하여 선택했습니다.
const validateABTestConfig = () => {const startDate = process.env.AB_TEST_START_DATE;const endDate = process.env.AB_TEST_END_DATE;const aRatio = process.env.AB_TEST_A_RATIO ? Number(process.env.AB_TEST_A_RATIO) : 0.5;if (!startDate || !endDate) {return null;}return {START: new Date(startDate),END: new Date(endDate),A_RATIO: aRatio,};};const AB_TEST_CONFIG = validateABTestConfig();const getRandomBucket = () => {return Math.random() < AB_TEST_CONFIG.A_RATIO ? 'A' : 'B';};const isInTestPeriod = () => {if (!AB_TEST_CONFIG) {return false;}const now = new Date();return now >= AB_TEST_CONFIG.START && now <= AB_TEST_CONFIG.END;};
AB 테스트의 시작일과 종료일을 환경변수로 관리함으로써, 배포 없이도 테스트 기간을 조정할 수 있도록 했습니다. 설정된 환경변수는 Cloud Run에서 쉽게 수정할 수 있습니다. 만약 환경변수가 설정되어 있지 않다면 AB 테스트는 자동으로 비활성화 되도록 구현했습니다.
Math.random()을 사용한 무작위 분배는 모수가 충분히 큰 경우 대체로 50:50에 가까운 결과를 보여주겠지만, 트래픽이 적은 경우에는 다소 불균형한 분배가 발생할 수 있습니다. 이를 위해 GTM으로 수집되는 실제 버킷별 사용자 수를 모니터링하면서 A_RATIO 환경변수를 조정할 수 있도록 구현했습니다. Redis나 DB를 사용하면 더 정확한 50:50 분배가 가능하겠지만, 추가 인프라 없이 간단하게 해결할 수 있다는 점에서 이 방식을 선택했습니다.
Middleware에서 URL rewrites
export function middleware(request: NextRequest) {if (!isInTestPeriod()) {return NextResponse.next();}const cookieValue = request.cookies.get(COOKIE_NAME)?.valuconst bucket = cookieValue || getRandomBucket();// A 버킷 사용자는 _abtest 접미사가 붙은 페이지로 이동const pathSuffix = bucket === 'A' ? '_abtest' : '';const response = NextResponse.rewrite(new URL(`${request.url}${pathSuffix}`));if (!cookieValue) {response.cookies.set(COOKIE_NAME, bucket);}return response;}
이 middleware는 다음과 같은 순서로 동작합니다
예를 들어, A 버킷에 할당된 사용자가 /courses 페이지에 접근하면, 내부적으로 /courses_abtest 페이지의 컨텐츠를 보여줍니다. 여기서 중요한 점은 이것이 리다이렉트가 아닌 rewrite라는 것입니다.
Rewrite vs Redirect의 차이
여기서는 사용자가 URL이 변경되었다는 것을 인지할 필요가 없고, A/B 테스트가 진행 중이라는 것을 알 수 없어야 하므로 Redirect 대신 Rewrite를 사용했습니다.
테스트 대상 페이지 설정
config 객체의 matcher 배열을 통해 AB 테스트를 적용할 페이지들을 지정할 수 있습니다.
export const config = {matcher: ['/slug1','/slug2',// ... 기타 테스트 대상 페이지],};
마치며 - AB Test의 결과
Next.js Middleware를 활용한 AB 테스트 구현은 간단하면서도 효과적인 방법이었습니다. 실제로 이번 테스트를 통해 새로운 디자인이 기존 버전 대비 결제 페이지 진입률 4% 상승, 스크롤 1/4 지점 도달률 6% 상승이라는 의미 있는 결과를 얻을 수 있었습니다.
이러한 정량적인 데이터를 바탕으로 더 나은 사용자 경험을 제공하는 방향으로 개선을 결정할 수 있었고, 앞으로도 다양한 가설을 검증하는데 이 AB 테스트 시스템을 활용할 수 있을 것 같습니다.