상세페이지를 만들다 보면 늘 같은 고민에 부딪힌다. “어떤 이미지를 맨 위에 둘까?” 예쁜 감성컷 먼저? 기능 설명 먼저? 후기 먼저? 나는 오랫동안 이걸 감으로 정했다. 그러다 문득, 순서만 바꿔도 스크롤 도달률과 전환이 달라지는 걸 로그에서 보고 나서 생각을 바꿨다. 순서는 취향이 아니라 실험 대상이라는 것.

이 글은 상세페이지 이미지 순서를 “선호 디자인순”으로 재배치하고, 그게 진짜 효과가 있는지 A/B로 확인한 과정을 정리한 회고다. 등장하는 브랜드·상품·수치는 전부 합성 데이터이고, 방법론만 일반화해서 적는다.

전체 그림은 어떻게 되나?

먼저 이번 실험의 큰 줄기를 도식으로 깔아둔다. 선호도 조사에서 “순서 가설”을 얻고 → 그 순서로 B안을 만들고 → 트래픽을 반씩 나눠 → 지표로 판정하는 흐름이다.

flowchart LR
  A[선호 디자인<br/>조사 데이터]:::v1 --> B[순서 가설 수립]:::v1
  B --> C{상세페이지<br/>두 버전 제작}:::v2
  C --> D[A안<br/>기존 순서]:::v3
  C --> E[B안<br/>선호 디자인순]:::v3
  D --> F[트래픽 5:5 분배]:::v4
  E --> F
  F --> G[지표 수집<br/>도달·클릭·전환]:::v4
  G --> H{통계 판정}:::v2
  H --> I[채택 또는 롤백]:::v5
  classDef v1 fill:#e7f5ff,stroke:#1c7ed6,color:#10548f;
  classDef v2 fill:#f3f0ff,stroke:#7048e8,color:#4b2fa8;
  classDef v3 fill:#d3f9d8,stroke:#2f9e44,color:#1d6b2c;
  classDef v4 fill:#fff3bf,stroke:#e67700,color:#8a5a00;
  classDef v5 fill:#ffe3e3,stroke:#e03131,color:#a01818;

핵심은 “감으로 순서를 바꾸지 않는다”는 것. 바꾸는 이유(가설)와 바꾼 뒤의 판정 기준(지표)을 먼저 정해두는 게 절반이다.

왜 이미지 순서를 실험 대상으로 봤나?

상세페이지는 세로로 긴 이미지 묶음이다. 방문자는 위에서부터 스크롤하며 읽는데, 아래로 갈수록 이탈한다. 그래서 어떤 블록을 위에 두느냐가 곧 “몇 %의 사람이 그 정보를 보느냐”를 결정한다.

합성 로그로 스크롤 도달률을 그려보면 감이 온다. 아래는 상세페이지 블록별 도달률 예시(합성 데이터).

순서블록도달률(합성)
1메인 감성컷100%
2핵심 기능 설명82%
3사이즈/스펙 표61%
4사용 후기44%
5배송/교환 안내29%

여기서 질문이 생긴다. 만약 “사용 후기”가 구매 결정에 가장 강하게 작용하는 블록이라면? 지금은 44%만 그걸 본다. 후기를 2번으로 올리면 도달률이 80%대로 뛴다. 순서 하나로 “설득 자산의 노출량”이 두 배 가까이 달라지는 셈이다.

‘선호 디자인순’은 무슨 뜻인가?

여기서 말하는 선호 디자인순은, 별도로 진행한 디자인 선호도 조사에서 나온 “사람들이 더 오래 보고 더 선호한 블록 유형” 순서를 말한다. 예를 들어 합성 조사 결과가 이렇게 나왔다고 하자.

flowchart TB
  subgraph 선호도조사["디자인 선호도 (합성 점수)"]
    P1[후기형 블록<br/>선호 88]:::hi
    P2[기능 인포그래픽<br/>선호 76]:::hi
    P3[감성 무드컷<br/>선호 64]:::mid
    P4[스펙 표<br/>선호 51]:::mid
    P5[배송 안내<br/>선호 33]:::lo
  end
  classDef hi fill:#d3f9d8,stroke:#2f9e44,color:#1d6b2c;
  classDef mid fill:#fff3bf,stroke:#e67700,color:#8a5a00;
  classDef lo fill:#ffe3e3,stroke:#e03131,color:#a01818;

기존 A안은 “감성컷 → 기능 → 스펙 → 후기 → 배송” 순이었다. 선호도 조사대로라면 후기와 기능 인포그래픽을 앞으로 당겨야 한다. 그래서 B안은 이렇게 재배치했다.

  • A안(기존): 감성컷 → 기능 → 스펙 → 후기 → 배송
  • B안(선호순): 후기 → 기능 → 감성컷 → 스펙 → 배송

주의할 점 하나. 선호도 조사는 “사람들이 좋다고 말한 것”이지 “실제로 사게 만드는 것”이 아니다. 말과 행동은 다르다. 그래서 조사 결과를 그대로 믿지 않고, A/B로 실제 전환을 확인하는 단계가 반드시 필요하다. 조사는 가설의 재료일 뿐이다.

A/B 테스트는 어떻게 설계했나?

A/B의 생명은 “두 그룹이 순서 말고는 똑같아야 한다”는 것이다. 방문자를 무작위로 반씩 나누고, 같은 기간·같은 유입 채널에서 노출해야 순서의 효과만 뽑아낼 수 있다.

sequenceDiagram
  participant U as 방문자
  participant S as 분배 로직
  participant A as A안 페이지
  participant B as B안 페이지
  participant L as 이벤트 로그
  U->>S: 상세페이지 진입
  S->>S: 방문자 해시 % 2 로 그룹 결정
  alt 그룹 0
    S->>A: A안 노출
    A->>L: 도달·클릭·전환 기록
  else 그룹 1
    S->>B: B안 노출
    B->>L: 도달·클릭·전환 기록
  end
  L->>L: variant 태그와 함께 적재

분배는 방문자 식별자를 해시해서 짝/홀로 가르는 방식이 깔끔하다. 같은 사람이 매번 같은 버전을 보게 되어(일관 노출), 새로고침마다 화면이 바뀌는 혼란을 막는다. 아래는 개념을 보여주는 합성 예시 코드다.

import hashlib
 
def assign_variant(visitor_id: str) -> str:
    """방문자 ID를 해시해 A/B 그룹을 안정적으로 배정 (합성 예시)."""
    h = hashlib.md5(visitor_id.encode()).hexdigest()
    # 16진수 마지막 자리를 짝/홀로 분배 -> 대략 5:5
    return "B" if int(h[-1], 16) % 2 else "A"
 
sample = ["user_1001", "user_1002", "user_1003", "user_1004"]
for uid in sample:
    print(uid, "->", assign_variant(uid))

이렇게 하면 배포 시점에 랜덤이 아니라 방문자별로 고정된 그룹이 나온다. 실험이 끝나기 전까지는 배정 규칙을 절대 바꾸지 않는 게 원칙이다.

어떤 지표로 성패를 판정하나?

지표를 미리 못 박아두지 않으면, 결과를 보고 나서 유리한 숫자만 골라 “성공”이라 우기게 된다. 그래서 실험 전에 주요 지표 하나(전환율)를 정하고, 보조 지표는 해석용으로만 쓴다.

flowchart LR
  subgraph 주지표["판정 기준 (사전 고정)"]
    M1[구매 전환율]:::main
  end
  subgraph 보조["해석용 보조 지표"]
    M2[후기블록<br/>도달률]:::sub
    M3[장바구니<br/>담기율]:::sub
    M4[평균 체류시간]:::sub
  end
  M1 --> D{B가<br/>유의하게 높은가}:::judge
  M2 --> D
  M3 --> D
  D --> R[채택/롤백 결정]:::main
  classDef main fill:#e7f5ff,stroke:#1c7ed6,color:#10548f;
  classDef sub fill:#f3f0ff,stroke:#7048e8,color:#4b2fa8;
  classDef judge fill:#fff3bf,stroke:#e67700,color:#8a5a00;

합성 데이터로 만든 결과 표는 이렇다.

지표A안(기존)B안(선호순)차이
방문자 수5,0204,980
후기블록 도달률44%79%+35%p
장바구니 담기율9.1%10.4%+1.3%p
구매 전환율2.8%3.4%+0.6%p

보기에는 B안이 다 이겼다. 하지만 여기서 멈추면 안 된다. “이 차이가 우연이 아닌가?”를 물어야 한다.

그 차이가 진짜인지 어떻게 확인하나?

전환율 2.8% vs 3.4%는 표본이 작으면 그냥 흔들림일 수 있다. 그래서 두 비율이 통계적으로 다른지 간단한 비율 검정으로 확인한다. 합성 수치로 돌려본 예시다.

from math import sqrt
from statistics import NormalDist
 
# 합성 데이터
n_a, conv_a = 5020, int(5020 * 0.028)   # A안 전환수
n_b, conv_b = 4980, int(4980 * 0.034)   # B안 전환수
 
p_a, p_b = conv_a / n_a, conv_b / n_b
p_pool = (conv_a + conv_b) / (n_a + n_b)
se = sqrt(p_pool * (1 - p_pool) * (1 / n_a + 1 / n_b))
z = (p_b - p_a) / se
p_value = 2 * (1 - NormalDist().cdf(abs(z)))
 
print(f"A={p_a:.3%}, B={p_b:.3%}")
print(f"z={z:.2f}, p-value={p_value:.4f}")

이 합성 예시에서 p-value가 대략 0.05 근처로 나온다. 관례적으로 0.05 미만이면 “우연이라고 보기 어렵다”고 판단한다. 여기서 중요한 건 숫자 자체보다 판단 절차다.

  • 실험 전에 필요한 최소 표본을 정한다(표본이 너무 적으면 어떤 차이도 못 믿는다).
  • 정해둔 기간·표본을 채우기 전에 훔쳐보고 멈추지 않는다(중간에 끊으면 착시가 생긴다).
  • 주지표(전환율)로만 채택 여부를 정하고, 보조 지표는 “왜 그런지” 설명에만 쓴다.

이번 케이스에선 후기블록 도달률이 44%→79%로 뛴 게 전환 상승의 그럴듯한 이유가 된다. 후기를 더 많은 사람이 봤고, 후기가 구매를 밀어줬다는 스토리가 지표끼리 앞뒤가 맞는다.

이 실험에서 조심할 함정은?

몇 번 데며 배운 것들을 정리해둔다.

stateDiagram-v2
  [*] --> 설계
  설계 --> 실행: 지표·표본 고정
  실행 --> 오염: 중간에 순서 또 변경
  오염 --> 재시작: 데이터 폐기
  재시작 --> 실행
  실행 --> 판정: 표본 충족
  판정 --> 채택: 유의미 & 스토리 일치
  판정 --> 롤백: 차이 없음/역효과
  채택 --> [*]
  롤백 --> [*]
  • 여러 개를 한꺼번에 바꾸지 않기. 순서도 바꾸고 카피도 바꾸면 무엇이 효과였는지 못 가린다. 이번엔 “순서”만 건드렸다.
  • 계절·행사 섞이지 않게. 세일 기간이 한쪽에만 걸리면 순서 효과인지 세일 효과인지 알 수 없다. 같은 기간에 동시 노출이 원칙.
  • 말한 선호 ≠ 산 행동. 선호도 조사에서 1등이던 감성컷은 정작 전환에선 후기에 밀렸다. 조사는 가설, 검증은 A/B.
  • 작은 차이에 과몰입 금지. 0.6%p가 매출로 의미 있는 규모인지, 재배치 비용보다 이득이 큰지 함께 본다.

정리하면

flowchart LR
  A[선호도조사<br/>=가설]:::a --> B[순서만 바꾼<br/>B안 제작]:::b
  B --> C[5:5 분배<br/>동시 노출]:::c
  C --> D[주지표 전환율<br/>+ 유의성 검정]:::d
  D --> E[스토리 일치 시<br/>채택]:::e
  classDef a fill:#e7f5ff,stroke:#1c7ed6,color:#10548f;
  classDef b fill:#d3f9d8,stroke:#2f9e44,color:#1d6b2c;
  classDef c fill:#fff3bf,stroke:#e67700,color:#8a5a00;
  classDef d fill:#f3f0ff,stroke:#7048e8,color:#4b2fa8;
  classDef e fill:#ffe3e3,stroke:#e03131,color:#a01818;

핵심 3줄로 남긴다.

  1. 상세페이지 이미지 순서는 취향이 아니라 “설득 자산의 노출량”을 결정하는 실험 변수다.
  2. 선호도 조사는 가설을 주지만, 실제 채택은 순서만 바꾼 A/B와 전환율 검정으로 정한다.
  3. 한 번에 하나만 바꾸고, 지표·표본을 미리 고정하고, 지표끼리 앞뒤가 맞는지까지 확인한다.

이 글의 모든 수치와 표는 합성 데이터이며, 방법론만 일반화한 회고다. 다음엔 같은 틀로 “이미지 개수(길이)“를 실험 대상으로 놓고 정리해볼 생각이다.