NGINX split client module A/B 테스트 활용 가이드

이 포스트는 NGINX split client module 을 활용하여, 요청을 전송한 클라이언트를 확률에 따라 그룹별로 분리하고, 그룹별로 요청을 다르게 처리하도록 구성해 A/B 테스트에 활용하는 방법에 관해 설명합니다.

NGINX split client module을 활용한 A/B 테스트를 통해 이전 버전의 앱과 신규 버전의 앱을 구성 후 소수의 클라이언트의 요청만 신규 버전으로 전달해 사용자의 반응을 미리 확인하거나, 3개 이상의 그룹으로 분리하여 다양한 UI별 선호도를 확인하는 등 클라이언트의 요청을 분산하여 다양한 방법으로 활용할 수 있습니다.

해당 모듈은 NGINX OSS, NGINX Plus 모두 사용 가능하며 컴파일 설치 시 별도 매개변수 없이 자동으로 빌드됩니다.

목차

1. A/B 테스트란?
2. NGINX split client module 사용 방법

3. NGINX split client module 테스트
4. 추가 활용 예시

5. 결론

1. A/B 테스트란?

A/B 테스트는 콘텐츠의 두 가지 이상의 요소를 비교하여 어느 쪽이 더 효과적인지를 분석하는 방법입니다. 웹사이트 디자인, 마케팅 캠페인 등 다양한 영역에서 활용할 수 있습니다. 사용자는 무작위로 두 가지 이상의 그룹으로 나뉘어 각각의 버전을 사용하고, 이를 통해 얻은 데이터로 어떤 버전이 목표 달성에 더 유리한지를 평가할 수 있습니다.

2. NGINX split client module 사용 방법

NGINX split client module(ngx_http_split_clients_module)은 split_clients 지시문을 통해 사용할 수 있습니다. 해당 지시문은 http 컨텍스트에서만 사용할 수 있습니다.

split_clients 지시문의 사용 예시는 다음과 같습니다.

split_clients $remote_addr $variant {
               0.5%        .one;
               2.0%        .two;
               *            "";
}
  1. $remote_addr(클라이언트 IP)는 클라이언트를 구분하기 위한 키(key) 값입니다.
  2. $variant는 분리된 그룹별로 할당될 값을 저장하는 변수입니다.
  3. 동일한 키($remote_addr)의 요청은 항상 같은 그룹에 할당됩니다.
  4. NGINX는 키($remote_addr) 값을 해시 함수에 통과시켜 확률에 따라 그룹으로 할당합니다.
  5. 확률의 *값은 100에서 나머지 확률을 뺀 값입니다.
  6. 모든 확률의 합은 100%를 넘을 수 없으며, 합이 100% 미만일 경우 * “”; 설정이 자동으로 적용됩니다.
  • 0.5%의 클라이언트에게는 .one 값이 할당
  • 2.0%의 클라이언트에게는 .two 값이 할당
  • 나머지 97.5%의 클라이언트에게는 빈 문자열("") 할당

요청은 설정한 확률에 따라 분배되지만, 정확하게 비율에 맞게 나눠떨어지지는 않습니다. 즉, 3% 설정 후 100번의 요청을 전송해도, 무조건 3번의 요청이 해당 그룹으로 할당되지는 않습니다.

위 예시 설정과 아래 예시를 결합하면, 다음과 같은 결과가 나타납니다.

server {
    location / {
        index index${variant}.html;
  • 0.5%의 클라이언트에게는 index.one.html 페이지를 표시
  • 2.0%의 클라이언트에게는 index.two.html 페이지를 표시
  • 나머지 클라이언트에게는 index.html 페이지를 표시

이처럼 NGINX split client module을 통해 지정된 확률에 따라 클라이언트를 분리할 수 있으며, 구분하는 키값에 따라 클라이언트는 동일한 그룹에 계속 속하기 때문에 반복적인 요청에도 다른 그룹으로 이동되지 않아 A/B 테스트 진행 중에도 클라이언트는 일관된 경험을 할 수 있습니다.

3. NGINX split client module 테스트

모듈을 간단하게 구성하여 테스트하기 위해 다음과 같이 NGINX를 구성했습니다.

ab.conf
split_clients $http_user_id $version {
              70%          v1;
              *            v2;
}

server {

        listen 8080;

        root /usr/share/nginx/html;

        location / {
                index ${version}.html;
        }
}

User-ID 헤더의 값에 따라 클라이언트를 70:30 확률로 그룹으로 분리하고, $version 변수에 v1/v2 값을 할당합니다.
NGINX 서버의 8080 포트로 요청을 전송하면 그룹에 따라 v1.html 혹은 v2.html 페이지를 표시합니다.
각 HTML 페이지에는 ‘App v1’ 또는 ‘App v2’ 텍스트가 포함되어 있습니다.

User-ID 헤더의 값을 변경하며 요청을 전송하기 위해 파이썬의 requests 모듈을 통해 요청을 전송하고, User-ID 헤더의 값에 따른 응답 및 응답별 총횟수를 출력하도록 구성했습니다.

import requests
from collections import Counter

# 요청을 보낼 URL
url = 'http://192.168.200.176:8080/'  # NGINX 서버 주소

# 사용자 ID 및 응답 결과를 저장할 리스트
results = []

# ID 1부터 1000까지 반복 요청
for user_id in range(1, 1001):
    response = requests.get(url, headers={'User-ID': str(user_id)})
    # 응답 텍스트 저장
    results.append(response.text)

# 각 응답 결과물의 횟수 계산
result_count = Counter(results)

# 출력 결과 (ID 1부터 1000)
for user_id, result in zip(range(1, 1001), results):
    print(f"User ID: {user_id}, Response: {result}", end="")

# 최종 응답 결과물별 횟수 출력
print("\n응답별 횟수:")
for result, count in result_count.items():
    print(f"{result}: {count}회")

해당 코드를 통해 요청을 전송하여 다음과 같은 출력을 확인할 수 있습니다.

User ID: 1, Response: App V1
User ID: 2, Response: App V1
User ID: 3, Response: App V1
User ID: 4, Response: App V1
User ID: 5, Response: App V1
User ID: 6, Response: App V1
User ID: 7, Response: App V2
User ID: 8, Response: App V1
User ID: 9, Response: App V1
User ID: 10, Response: App V1
...
User ID: 998, Response: App V1
User ID: 999, Response: App V1
User ID: 1000, Response: App V1

응답별 횟수:
App V1
: 720회
App V2
: 280회

설정한 확률의 근삿값으로 응답 횟수가 나타난 것을 확인할 수 있습니다.

동일한 ID로 여러번 요청을 전송하면, 매번 같은 응답을 수신하는지 확인하겠습니다.
이전의 단계에서 App V2 응답을 수신했던 User-ID 헤더의 값이 7인 요청을 100회 전송하겠습니다.

import requests
from collections import Counter

# 요청을 보낼 URL
url = 'http://192.168.200.176:8080/'  # NGINX 서버 주소

# 사용자 ID 7의 응답 결과를 저장할 리스트
results = []

# ID 7로 100번 반복 요청
for _ in range(100):
    response = requests.get(url, headers={'User-ID': '7'})
    # 응답 텍스트 저장
    results.append(response.text)

# 각 응답 결과물의 횟수 계산
result_count = Counter(results)

# 최종 응답 결과물별 횟수 출력
print("응답별 횟수:")
for result, count in result_count.items():
    print(f"{result}: {count}회")
응답별 횟수:
App V2
: 100회

100회의 응답이 모두 이전과 동일함을 확인할 수 있습니다.

키값을 통해 분류된 클라이언트는 동일한 그룹에 계속 속하기 때문에, 반복되는 요청에도 일관된 경험을 할 수 있습니다.

4. 추가 활용 예시

NGINX의 if 조건식과 NGINX split client module을 통해 할당된 변수를 조합하여 다음과 같이 여러 그룹으로 분류하여 활용할 수 있습니다.

split_clients $http_user_id $version {
              33.3%        v1;
              33.3%        v2;
              *            v3;
}

upstream app-v1 {
        server localhost:8001;
}

upstream app-v2 {
        server localhost:8002;
}

upstream app-v3 {
        server localhost:8003;
}

server {
        listen 9000;

        location / {
            if ($version = v1) {
                    proxy_pass http://app-v1;
            }
            if ($version = v2) {
                    proxy_pass http://app-v2;
            }
            if ($version = v3) {
                    proxy_pass http://app-v3;
            }
        }
}

동일한 확률로 클라이언트를 User-ID 헤더의 값에 따라 3개의 그룹으로 나누고, 변숫값에 따라 서로 다른 upstream 그룹으로 요청을 프록시 합니다.

이전과 같이 테스트 요청 전송 시 결과입니다.

User ID: 1, Response: Server1
User ID: 2, Response: Server1
User ID: 3, Response: Server2
User ID: 4, Response: Server1
User ID: 5, Response: Server2
User ID: 6, Response: Server2
User ID: 7, Response: Server3
User ID: 8, Response: Server1
User ID: 9, Response: Server2
User ID: 10, Response: Server1

응답별 횟수:
Server1
: 366회
Server2
: 328회
Server3
: 306회

5. 결론

이번 포스트에서는 NGINX split client module을 활용하여 A/B 테스트에 활용하는 방법을 알아봤습니다.

NGINX split client module을 통해 클라이언트를 식별할 키값 / 그룹별 확률을 손쉽게 설정하여 A/B 테스트에 활용할 수 있었습니다. 또한 동일한 키값을 가진 클라이언트는 동일한 그룹에 계속 속하게 되어 여러 번 요청을 전송해도 동일한 응답 결과를 수신하는 것도 알게 되었습니다.
NGINX split client module을 활용하면 이처럼 A/B 테스트를 위한 환경을 손쉽게 구성할 수 있습니다.

NGINX split client module을 포함한 다양한 기능이 있는 NGINX OSS의 엔터프라이즈 버전인 NGINX Plus를 체험해 보고 싶으시다면 NGINX STORE를 통해 문의해 무료로 NGINX Plus trial을 체험해 보세요.

NGINX STORE를 통한 솔루션 도입 및 기술지원 무료 상담 신청

* indicates required