이커머스를 위한 룰 엔진: 할인, 프로모션, 자격
이커머스의 할인·프로모션·자격은 다른 어떤 로직보다 빠르게 불어나고, 결제 코드 안에서 무너지기 쉽습니다. 이 규칙들을 어디에 둬야 하는지, 코드에 박아 넣은 방식이 왜 실패하는지, 수익 사고 없이 프로모션을 내보내는 방법.
한 고객이 장바구니 하나에 세 가지를 함께 담습니다. 이메일로 받은 쿠폰, VIP 등급에 주어지는 12% 할인, 사이트 전체에 걸린 여름 세일 15%. 이 고객은 결국 얼마를 할인받을까요? 결제 로직 어딘가에 "할인은 한 번에 하나만"이라고 박아 뒀다면, 솔직한 답은 "코드가 먼저 검사한 조건이 무엇이든 그게 적용된다"입니다. 그리고 그 적용이 틀렸다는 사실은 월말 정산에서 재무팀이 수익을 맞춰 볼 때야 드러납니다.
이커머스는 이런 결정으로 굴러가고, 이런 결정은 시스템의 어느 부분보다도 빠르게 불어납니다. 이 글에서는 이 규칙들을 어디에 둬야 하는지, 코드에 박아 넣은 방식이 왜 무너지는지, 할인과 프로모션과 자격을 결제 코드 바깥으로 어떻게 빼내는지 살펴봅니다.
이커머스 결정은 왜 폭발적으로 불어나는가
대부분의 분야에는 1년에 몇 번 바뀌는 비즈니스 규칙이 몇 개 있을 뿐입니다. 이커머스에는 매주 바뀌는 규칙이 산더미처럼 쌓이는데, 하나하나 떼어 놓고 보면 별것 아닙니다.
- 쿠폰 코드: 결제할 때 입력, 일정 기간만 유효, 특정 상품이나 첫 구매 고객으로 한정.
- 등급 할인: 회원 등급에 따라 VIP는 12%, Gold는 8%.
- 시즌 프로모션: 사이트 전체 여름 세일, 주말 반짝 특가, 특정 품목 재고 정리.
- 무료배송 기준: 5만 원 이상 구매 시 적용, 회원은 3만 5천 원 이상.
- 자격: 실제로 누가 해당되는지. 고객군, 지역, 주문 이력 같은 조건.
- 번들 가격: 두 개를 사면 세 번째는 할인가.
이 중 하나만 떼어 보면 신입 개발자가 점심 먹기 전에 짤 수 있는 조건문 하나에 불과합니다. 문제는 이것들이 같은 주문에 동시에 걸리고, 그 조합의 수가 아무도 따라잡지 못할 만큼 빠르게 불어난다는 데 있습니다. 서로 독립된 프로모션이 셋만 돼도 겹치는 경우의 수가 여덟 가지이고, 넷이 되면 다시 그 두 배로 뜁니다. 누구도 그 열여섯 가지를 일일이 따져 보지 않으니, 이 조합들이 어떻게 맞물리는지는 고객이나 회계 담당자가 들춰낼 때까지 검증 없이 방치됩니다.
이커머스가 규칙 많은 다른 분야와 갈라지는 이유는, 세 가지 조건이 한꺼번에 겹치기 때문입니다. 우선 규칙이 자주 바뀝니다. 마케팅은 거의 매주 새 캠페인을 돌리고, 그 일정은 멈추는 법이 없습니다. 게다가 규칙을 정하는 사람과 시스템에 반영하는 사람이 다릅니다. 어떤 프로모션을 언제 돌릴지는 상품 기획과 마케팅이 정하지만, 실제 반영은 개발팀만 할 수 있습니다. 마지막으로 규칙이 곧장 매출로 이어집니다. 잘못 겹친 할인은 오류를 내지 않습니다. 원가 밑으로 팔리면서도 정산 전까지는 멀쩡한 주문처럼 보일 뿐입니다. 변경 빈도, 권한 분리, 금전 영향이 모두 같은 쪽을 가리킵니다. 그리고 그 방향은 이 로직을 코드 속에 묻어 두는 것과는 정반대입니다.
코드에 박아 넣은 방식, 그리고 그것이 무너지는 자리
시작은 늘 똑같습니다. 결제 서비스 안에 들어간 if-else 블록입니다. 쿠폰이 유효하면 적용하고, 등급이 VIP면 그 할인도 적용하고, 여름 세일 기간이면 그것까지 적용합니다. 프로모션이 하나뿐이고 개발자도 한 명이라면, 이 정도가 딱 맞는 구조입니다. 오히려 더 무거운 도구를 들이는 쪽이 실수입니다.
이 구조는 정해진 네 군데에서 무너집니다. 그리고 사업이 커지면서 대체로 다음 순서로 드러납니다.
바꿀 때마다 배포해야 합니다. 마케팅은 금요일 오전 9시에 세일을 열고 싶은데, 배포 가능한 시간은 화요일 오후입니다. 결국 프로모션은 캠페인 일정이 아니라 개발 일정에 끌려가고, 할인 한 줄 고치는 일이 스프린트 티켓 하나, 코드 리뷰, 배포로 불어납니다. 로직이 복잡한 게 아닙니다. 그걸 감싼 배포 과정이 발목을 잡습니다.
할인끼리 아무도 예상 못 한 방식으로 맞물립니다. 따로 보면 둘 다 맞는 규칙이 합쳐져, 아무도 승인한 적 없는 결과를 냅니다. 가장 값비싼 문제라 아래에서 따로 다룹니다.
가격이 왜 그렇게 나왔는지 아무도 답하지 못합니다. 고객이 왜 자기 주문에는 할인이 안 들어갔는지 메일로 물어 옵니다. 고객지원은 개발팀에 떠넘깁니다. 재무팀은 지난 화요일에 어떤 프로모션이 돌고 있었고 어느 주문에 영향을 줬는지 캐묻습니다. 하지만 결제 코드에는 최종 금액만 남을 뿐, 중간 과정은 모두 사라진 뒤입니다. 어떤 규칙이 걸렸고 어떤 규칙이 빠졌는지, 왜 그랬는지, 그 모든 기록이 사라집니다. 결국 지난 결정을 되짚는 유일한 방법은 git 이력을 뒤지며 추측하는 것뿐입니다.
정작 로직의 주인이 손을 못 댑니다. 프로모션의 내용은 마케팅과 상품 기획이 정하는데, 정작 모든 변경은 개발자의 백로그를 거쳐야 합니다. 문제를 가장 잘 아는 팀은 규칙에 손댈 수 없고, 규칙에 손댈 수 있는 팀은 그 일을 하려고 남의 영역으로 맥락을 옮겨 와야 합니다.
따로는 맞는 두 할인이 겹치면 틀려지는 순간
여기 진짜 돈이 새는 지점이 있습니다. 12% 할인을 받는 VIP 회원이 15%를 깎아 주는 여름 세일 기간에 장을 보면, 두 규칙이 같은 주문에 동시에 걸립니다. 규칙은 따로 보면 둘 다 맞습니다. 그런데 주문은 27% 할인으로 빠져나가고, 누구도 의도하지 않은 이 일을 재무팀이 월말에야 수천 건 단위로 발견합니다.
여기서 많은 팀이 놓치는 핵심은 이것입니다. 규칙 하나하나가 맞다고 해서, 그 규칙들을 모은 전체가 맞다는 보장은 전혀 없습니다. 할인 각각은 옳아도 그 조합은 얼마든지 틀릴 수 있습니다. 버그가 특정 규칙 안에 있는 게 아니라, 규칙들이 맞물리는 사이에 숨어 있기 때문입니다. 줄줄이 이어진 if-else에는 "이 둘은 절대 같이 적용되면 안 된다"는 개념 자체가 없습니다. 각 분기를 따로 판단해 걸리는 것마다 그대로 얹을 뿐인데, 그게 바로 우리가 원치 않는 동작입니다.
구조적인 해법은, 프로모션을 새로 만들 때마다 사람이 일일이 배타성을 챙기는 게 아니라, 그 배타성을 규칙 집합 자체의 성질로 박아 두는 것입니다. 서로 경쟁하는 할인들을 하나의 mutex 그룹으로 묶습니다. 그러면 주문 하나당 최대 하나만 살아남고, 무엇을 남길지는 미리 정해 둔 전략으로 결정됩니다. 우선순위가 가장 높은 것, 고객에게 가장 이득인 것, 혹은 먼저 걸린 것. 밀려난 할인도 그냥 사라지지 않습니다. 왜 밀렸는지가 결정 기록에 함께 남아서, 고객지원은 가격을 설명할 수 있고 재무팀은 감사할 수 있습니다.
여기까지가 개념이고, 이 글에서 챙겨 갈 핵심입니다. 실제 규칙을 어떻게 정의하는지, 밀려난 할인이 결정 추적에 어떻게 남는지, 그리고 까다로운 경계 상황은 VIP 할인 중첩 패턴에서 처음부터 끝까지 다룹니다. 같은 폭의 할인이 맞붙는 동점 상황, 전략 선택, 아무것도 걸리지 않을 때 벌어지는 일까지 모두요. 한 가지만 기억하면 됩니다. 할인 중첩은 if 한 줄씩 고쳐서 잡는 버그가 아니라, 제어 흐름 바깥으로 끄집어내 규칙 집합에 명시해야 하는 성질이라는 점입니다.
자격: 누구에게 줄 것인가, 그리고 왜 계속 바뀌는가
할인이 "얼마"를 정한다면, 자격은 "누구"를 정합니다. 그리고 팀들은 이 "누구" 쪽을 자주 얕봅니다. 프로모션에는 저마다 대상이 있습니다. 첫 구매 고객만, 특정 지역 고객, 주문 횟수가 일정 이상인 회원, 앱으로 들어온 고객, 마케팅이 캠페인 하나를 위해 따로 묶은 고객군. 누구에게 줄지 정하지 않은 할인은 아무 의미가 없습니다.
자격도 할인처럼 조합이 불어나는 성질이 있고, 바뀌는 속도는 더 빠릅니다. 할인은 한 시즌 내내 유지될 수 있어도, 그 뒤에서 누구를 겨냥할지는 캠페인마다 손봅니다. 이번 주는 서울의 신규 가입자, 다음 주는 전국의 재구매 고객, 그다음 주는 어제까지 존재하지도 않던 고객군. 이 대상 선정이 if customer.segment == "new" && customer.region == "KR" 식으로 코드에 박혀 있으면, 캠페인 하나에 고객군 하나를 더하는 일조차 코드 수정과 리뷰와 배포가 됩니다. 마케팅이 한 시간 전에 켜고 싶어 한 로직을 위해서 말입니다.
자격을 규칙으로 빼내면 이 조건들이 데이터가 됩니다. 고객은 segment IN [...], region EQUALS, order_count >= N을 만족할 때 대상이 되고, 마케팅은 개발자를 거치지 않고 대상을 조정합니다. 이렇게 하면 할인만으로는 막지 못하는 빈틈도 메워집니다. 자격과 할인은 사실 한 결정의 양면입니다. 누가 대상이고, 그 대상이 무엇을 받는가. 이 둘이 같은 규칙 집합 안에 있으면, 서로를 모르는 대상 선정 로직과 가격 로직으로 갈라지지 않고, 둘이 어떻게 맞물리는지가 한자리에서 드러납니다.
감사 측면도 빼놓을 수 없습니다. "이 고객은 왜 프로모션을 못 받았나"는 이커머스 고객지원에 가장 자주 들어오는 문의 가운데 하나입니다. 자격이 규칙으로 정리돼 있으면 그 답은 결정 추적에 그대로 남아 있습니다. 해당 고객이 order_count 조건에 걸렸는지 지역을 벗어났는지가, 그를 제외한 규칙과 값까지 함께 기록됩니다. 어림짐작할 일도, 3주 전 대상 기준이 어땠는지 알아내려고 코드를 헤맬 일도 없습니다.
프로모션, 띄우기 전에 미리 돌려 보기
값비싼 순간이 하나 더 있습니다. 프로모션을 눈 감고 내보내는 것입니다. 여름 세일 기준 금액을 낮추거나 등급 할인을 새로 추가할 때, 실제로 주문 몇 건이 영향을 받을까요? 수익은 어떻게 될까요? 기존 어떤 프로모션과 새로 겹칠까요? 내보내기 전까지 이 모든 답은 추측이고, 그 추측은 대개 지나치게 낙관적입니다.
스테이징 환경으로는 이 문제가 풀리지 않습니다. 스테이징에는 실제 주문 분포가 없기 때문입니다. "이 변경이 주문 몇 건을 할인했을까"는 실제 트래픽에 관한 질문입니다. 실제 고객이 하루 동안 담는 장바구니 규모, 회원 등급, 쿠폰, 지역이 어떻게 분포하는지에 달려 있습니다. 사람이 직접 짠 테스트 케이스 몇 개로는 규칙이 작동하는지 정도만 확인할 수 있을 뿐, 한 달치 주문이 그리는 분포를 재현하지는 못합니다. 그런데 영향 범위를 말해 주는 건 바로 그 분포입니다.
실제로 통하는 방법은, 바꾼 규칙을 과거의 진짜 주문에 다시 돌려 보는 것입니다. 적용하려는 프로모션을 지난달 실제 장바구니에 걸어 보고, 무엇이든 내보내기 전에 결과를 확인합니다. 주문 몇 건이 새로 대상이 되는지, 어느 고객군에 몰리는지, 수익이 어느 쪽으로 움직이는지를 봅니다. 감이 아니라 근거로 판단하고, 잘못된 조합을 분기 말 정산이 아니라 월요일 아침 보고서에서 잡아냅니다. 틀렸을 때 치르는 비용이 "실매출"에서 "화면 위 숫자"로 내려갑니다.
바뀐 규칙을 실제 이력에 돌려 그 영향을 나란히 비교하는 일, 그러니까 걸린 주문과 지표 변화량과 규칙별 통계를 들여다보는 작업은 변경 영향 시뮬레이션 패턴에서 다룹니다. 이 대목에서 챙길 원칙은 이것입니다. 프로모션을 바꾸면 그 영향 범위는 반드시 측정할 수 있고, 남은 건 그것을 고객보다 먼저 재느냐 나중에 재느냐의 선택뿐입니다.
이런 게 필요 없는 경우
도구를 들이기 전에 규모를 솔직하게 따져 보세요. 작은 일에 과한 도구를 쓰는 것은, 큰 일에 도구가 없는 것만큼이나 잘못입니다. 파는 상품이 하나뿐이거나, 프로모션을 아예 돌리지 않거나, 한 번 정하면 바뀌지 않는 고정 할인 하나만 있다면, 설정 값 하나로 충분합니다. 그런 상황에서 규칙 플랫폼은 관리하기만 번거로운 짐일 뿐입니다.
그리고 할인을 바꾸는 사람도, 그 할인을 이해해야 하는 사람도 결국 개발자 한 명뿐이라면, 그냥 코드에 두고 풀 리퀘스트로 고치면 됩니다. 이 로직을 결제 코드 밖으로 빼야 하는 이유는 할인이 있다는 사실 자체가 아니라, 누가 그것을 바꾸고, 얼마나 자주 바꾸며, 그 결과를 누가 책임지느냐에 있습니다. 되풀이되는 프로모션도 없고, 배포를 기다리는 마케팅 담당자도 없고, 무엇이 켜져 있었느냐는 월말 추궁도 없다면, 이 방식은 들이는 무게만큼의 값어치가 없습니다. 그냥 손을 떼는 게 맞습니다.
이 글이 다루는 건, 프로모션이 불어나고, 대상이 캠페인마다 달라지며, 그 둘을 운영하는 사람이 배포 권한이 있는 사람과 다른, 바로 그 지점입니다. 아직 거기에 이르지 않았다면, 있지도 않은 문제를 풀겠다고 장치부터 만들지 마세요.
이커머스 규칙은 불어나고, 매주 바뀌며, 곧장 매출에 닿습니다. 이걸 결제 코드에 박아 넣으면 배포 병목, 소리 없이 쌓이는 중첩 버그, 제때 못 바꾸는 자격 조건, 그리고 지난 화요일에 무엇이 돌고 있었는지 답하지 못하는 상태까지 한꺼번에 떠안습니다. 이것들을 데이터로 빼내면 그림이 달라집니다. 마케팅이 프로모션과 대상을 직접 운영하고, 중첩은 요행에 기대는 대신 mutex 그룹으로 막히며, 변경의 영향은 정산 뒤가 아니라 내보내기 전에 확인하는 것이 됩니다.
LexQ는 이커머스 전용 도구가 아니라 의사결정 운영 플랫폼입니다. 바로 그렇기 때문에 이 분야에도, 다른 많은 분야에도 들어맞습니다. 이커머스만을 겨냥해 만든 도구는 이커머스 모델에 갇히지만, 결정을 데이터로 다루는 플랫폼은 똑같은 장치(상호 배타, 시뮬레이션, 감사 추적)를 핀테크 한도나 SaaS 이용 권한에도 그대로 가져다 씁니다.
다음 프로모션은 띄우기 전에 미리 돌려 보세요.
→ lexq.io에서 무료로 시작하기
함께 읽기
- VIP 등급 할인 중첩을 mutex 그룹으로 해결하기: 규칙 정의 전체, 결정 추적, 경계 상황까지
- 변경 영향 시뮬레이션으로 배포 전에 규칙 검증하기: 실제 주문으로 프로모션 영향 범위 측정
- 모든 비즈니스 규칙 변경마다 코드를 배포하지 않는 법: 결제 사례 뒤에 깔린 일반적인 이야기