"지난달 가격을 5% 올렸는데 매출이 빠졌어요. 가격 때문일까요, 시즌 때문일까요?" 이 질문에 A/B 테스트로는 답할 수 없습니다. 가격은 모든 유저에게 동시에 바뀌었고, 한 번 올린 가격은 되돌릴 수 없습니다. 이중차분법(DiD)은 "안 올렸으면 어땠을까"의 가짜 대조군을 시간축에서 만들어, 시즌 추세와 가격 효과를 분리해 줍니다. 마케팅 의사결정에서 가장 자주 써먹게 될 인과추론 도구를, 마케터 시각으로 풀어봅니다.
1. 마케터가 매일 부딪히는 문제 — "안 했으면 어땠을까"
A/B 테스트가 안 되는 마케팅 개입은 의외로 많습니다. 다음 세 가지는 누구나 한 번쯤 부딪힙니다.
- 가격을 5% 올렸다 — 카테고리 전체에 동시 적용. A/B로 절반은 올리고 절반은 안 올릴 수 없음
- 무료배송 임계값을 30,000원 → 50,000원으로 올렸다 — 운영상 두 정책을 동시에 굴릴 수 없음
- 앱 메인 화면을 리뉴얼했다 — 단계적 롤아웃이 가능하긴 하지만 코호트가 달라짐
이런 개입은 한 번 일어나면 되돌릴 수 없습니다. "올리기 전 4주 vs 올린 후 4주"를 비교하면 깔끔할 것 같지만, 그 사이에 시즌이 바뀌고 광고 예산이 바뀌고 경쟁사 프로모션이 떠 있을 수 있습니다. 단순한 사전·사후 비교로는 "가격 효과"와 "시간 효과"가 뒤섞입니다.
DiD는 이 뒤섞임을 떼어내는 가장 단순한 인과추론 도구입니다. 처리군과 대조군을 한 쌍 잡고, 두 그룹의 사전·사후 차이의 차이를 본다는 한 줄짜리 아이디어로 시간 추세를 자동으로 빼냅니다.

DiD는 두 번의 차분으로 시간 추세와 그룹 차이를 동시에 빼낸다. 평행 추세가 가정 그대로 유지되었다면, 처리 시점 이후의 처리군 추가 변동이 곧 효과 추정치다.
📌 이 글에서 다루는 것
DiD(Difference-in-Differences, 이중차분법)는 1855년 콜레라 연구까지 거슬러 올라가는 고전이지만, 2020년대 마케팅 의사결정에 다시 떠오른 이유는 단순합니다. A/B를 못 돌리는데 의사결정은 해야 하기 때문입니다. 이 글은 코드보다는 "어떤 가정 위에 서 있는지"와 "언제 깨지는지"를 마케터 시각으로 풀어냅니다.
2. DiD의 직관 — 두 번 빼는 이유
DiD의 핵심 식은 한 줄입니다.
여기서 는 처리군(treated), 는 대조군(control), pre·post는 개입 전·후 평균입니다. 표로 보면 직관이 더 분명합니다.
| 그룹 \ 시점 | 사전(pre) | 사후(post) | 사전→사후 변화 |
|---|---|---|---|
| 처리군 (가격 5% 올린 카테고리) | 100 | 92 | -8 |
| 대조군 (가격 동결한 카테고리) | 100 | 95 | -5 |
| 차이의 차이 | — | — | -3 |
처리군은 8 만큼 빠졌고, 대조군은 5 만큼 빠졌습니다. 단순히 처리군의 -8을 가격 효과라고 보고하면 안 됩니다. 대조군도 5만큼 빠진 걸 보면 시즌·매크로 환경이 -5는 가져갔다는 신호이기 때문입니다. 그 5를 빼면 가격 인상이 만든 추가 손실은 -3으로 추정됩니다.
이게 DiD의 전부입니다. 처리군의 사전·사후 차분에서 "같은 기간 동안 대조군이 자연스럽게 움직인 만큼"을 한 번 더 빼는 것. 두 번 빼기 때문에 "이중(double)" 차분(diff-in-diff)입니다.
💡 DiD가 빼주는 것·못 빼주는 것
DiD는 시간에 따라 두 그룹에 똑같이 작용한 충격을 빼냅니다. 시즌, 거시경제, 사회적 이슈처럼 양쪽에 같은 강도로 들이친 것들이 그 대상입니다. 반대로 처리군에만 따로 들이친 충격은 빼주지 못합니다. 가격을 올린 그 시점에 같은 카테고리의 경쟁사가 갑자기 50% 세일에 들어갔다면, DiD는 그 효과까지 가격 탓으로 돌립니다.
3. 평행 추세 가정 — DiD의 심장
DiD가 작동하려면 단 하나의 가정이 성립해야 합니다.
처리가 일어나지 않았다면, 처리군과 대조군은 사전 시점부터 사후 시점까지 평행하게 움직였을 것이다.
이걸 평행 추세 가정(parallel trends assumption)이라고 부릅니다. 두 그룹의 절댓값이 같을 필요는 없습니다. 처리군 매출 1,000만 원, 대조군 매출 200만 원이어도 됩니다. 둘이 같은 기울기로 움직였을 거라는 가정이면 충분합니다.
문제는 이 가정이 본질적으로 검증 불가능하다는 점입니다. "처리가 안 일어났을 때의 처리군 추세"는 실제로는 관측되지 않습니다. 그래서 평행 추세는 데이터로 증명하는 게 아니라, 데이터로 의심해보고 의심이 들지 않을 때까지 가정으로 받아들이는 것에 가깝습니다.
⚠️ 평행 추세는 가정이지 검증이 아님
사전 6주의 추세가 평행했다는 것은 사후 추세가 평행했을 거라는 방증일 뿐 증명이 아닙니다. 사전 추세 그래프가 깨끗해도 사후에 처리군에만 따로 작용한 충격이 있으면 추정치는 망가집니다. DiD 보고서에는 항상 "어떤 평행 추세를 어떻게 검증했는가"가 같이 있어야 합니다.
평행 추세가 깨지는 시나리오는 크게 세 가지입니다.
3-1. Ashenfelter dip — 처리 직전에만 처리군이 푹 꺼진다
Ashenfelter(1978)가 직업 훈련 프로그램에서 발견한 현상으로, 처리 직전에 처리군이 일시적으로 나빠진 뒤 처리 시점에 다시 회복합니다. 마케팅으로 옮기면 "프로모션을 끝내기 직전 1~2주 매출이 푹 꺼진다"가 비슷한 패턴입니다. 종료를 알린 시점부터 사재기·관망이 일어나기 때문입니다. 이 dip을 못 보면 종료 효과를 과장합니다.
3-2. 선택 편의 — 처리·대조군이 애초에 다른 길을 가고 있었다
가격을 올린 카테고리는 "올려도 버틸 만한" 카테고리였을 가능성이 큽니다. 대조군으로 잡은 카테고리는 "민감해서 못 올린" 카테고리일 수 있습니다. 두 그룹은 사전부터 다른 추세를 가졌을 수 있고, DiD는 그 차이까지 효과로 잡아냅니다.
3-3. 외부 충격 — 처리 시점에 다른 일이 동시에 벌어졌다
가격을 올린 그 주에 경쟁사가 신규 광고를 쏘기 시작했다거나, 같은 카테고리에 대형 신상품이 출시됐다면 DiD는 그 효과까지 가격 탓으로 돌립니다. 이건 사전 추세를 아무리 봐도 잡히지 않습니다. 사후 외부 이벤트 로그를 따로 모아 같이 봐야 합니다.
4. 회귀식으로 푸는 DiD — 표준오차까지 한 묶음
직관은 단순한 차분이지만, 실무에서는 회귀로 푸는 게 표준입니다. 표준오차·신뢰구간·공변량 보정을 한 번에 처리할 수 있기 때문입니다.
각 변수의 의미는 단순합니다.
- — 처리군이면 1, 대조군이면 0 (그룹 더미)
- — 사후 시점이면 1, 사전 시점이면 0 (시점 더미)
- — 처리군 × 사후 시점 교차항. 처리군에만, 사후에만 1이 됨
- — 우리가 보고 싶은 처리 효과. 교차항의 계수
마법은 마지막 줄에 있습니다. 가 정확히 §2에서 손으로 계산한 차이의 차이와 같습니다. 은 그룹 간 평균 차이, 는 시점 간 평균 차이를 흡수해 가고, 남은 처리 효과만 에 떨어집니다.
python
Top comments (0)