이커머스 시스템을 기술적으로 완벽하게 만들어 놓고도 — 결제가 P99 100ms 에 들어오고, 정산이 5분 안에 끝나고, ArchUnit 으로 헥사고날 경계까지 강제했는데도 — 서비스가 안 굴러가는 순간들 이 있다. 그건 코드 버그가 아니다. 재고 숫자가 맞지 않고, 쿠폰을 누가 부담할지 합의가 없고, 세금계산서를 언제 발행하는지 회계팀이 모르고, 환불 거부에 소비자원이 들어오고, 셀러가 가격을 비합리적으로 올리는데도 막을 권한이 없는코드 한 줄로는 절대 풀 수 없는 문제들이다.

이 글은 이커머스 도메인에서 반복적으로 마주치는 비기술 문제 7 가지 를 정리하고, 그 비기술 문제가 어떻게 *기술 설계 에 침투해서 시스템 구조를 바꿔놓는지* 그 패턴을 본다. (1) 재고 drift, (2) 쿠폰·프로모션 의 조합 폭발 + 부담 책임, (3) 정산의 회계·세무 복잡도, (4) 환불·취소의 법정 7일 룰 과 회계 처리, (5) 외부 의존 (PG / 카드사 / 3PL), (6) 이해관계자 충돌, (7) 어뷰징 / 사기 / 분쟁 — 그리고 마지막으로 그 모든 것이 기술 설계 에 어떻게 침투하는가.


TL;DR

“이커머스의 본질은 기술이 아니라 약속의 다층 정합성이다” — 셀러와의 약속, 구매자와의 약속, 카드사·PG 와의 약속, 국세청·소비자보호법과의 약속, 3PL 과의 약속. 이 약속들이 서로 충돌 한다. 코드는 그 충돌을 해결하지 못한다 — 다만 *기록 할 뿐이다. 그래서 이커머스 시스템의 진짜 설계 능력은 *충돌을 *명시화 하고 책임 layer 를 *분리 하고 되돌릴 수 있는 상태기록 하는 것*.

이 글에서 다룰 7 가지 비기술 문제 가 결국 기술 설계 에 침투하는 4 가지 공통 패턴:

비기술 문제 기술 설계 침투
결정권자가 여럿이고 합의 비용이 큼 상태 머신 + 감사 로그 + 명시 권한 으로 누가 언제 무엇을 결정했는지 영구 기록
외부 시스템·법·인간 행동 변경 빈도 높음 adapter 분리 + 정책 외부화코어 비즈니스 로직 보호
사고 발생 후 책임 분담 의 모호함 추적 가능 사슬 (orderId → paymentId → settlementId) 으로 감사 가능성 보장
새 사고 의 예측 불가능성 소프트 삭제 + 이벤트 소싱 친화 + 멱등 재시도되돌리기 가능한 상태 유지

“이커머스의 진짜 어려움은 *법무·회계·세무·분쟁 부서가 기술팀에게 묻는 질문에 답할 수 있어야 한다 는 것”* — “7 일 전에 그 주문 어떤 쿠폰 어떤 금액으로 결제됐고 누가 환불 처리했고 정산서에 반영됐나” — 이 한 질문에 답하지 못하면 기술 시스템이 비즈니스 무게를 못 견디는 것.


0. 들어가며 — 왜 비기술 문제가 더 어렵나

기술적 버그재현 가능 하다. 로그 가 남는다. 고치면 끝 난다. 그런데 비기술 문제 는:

  • 재현 불가능 — 같은 셀러가 같은 상품에 같은 행동을 반복하지 않는다
  • 로그가 부족전화 통화 / 카톡 / 회의실 결정 의 흔적이 코드에 안 남는다
  • 고쳐도 끝나지 않음다음 달에 비슷한 사고가 다른 셀러에게서 다른 모양 으로 다시 발생

이 비대칭이 본질이다. 기술 문제는 해결 의 문제고, 비기술 문제는 관리 의 문제다. 우리가 코드를 잘 짜는 능력으로 비기술 문제를 해결 하려고 들면 과도 설계 (모든 시나리오에 자동화 코드) 가 되거나 과소 설계 (사람이 모든 걸 책임지는 단순 CRUD) 가 된다. 정공은 그 사이 — 어디까지가 코드의 영역이고 어디부터가 사람의 영역인지 *경계를 명시 하는 것*.


1. 재고 drift — 실 재고와 시스템 재고는 절대 같지 않다

1-1. 본질적 비대칭

시스템 재고DB 의 숫자. 실 재고창고의 박스. 둘이 같은 적은 없다. 시스템 재고가 100 인데:

  • 입고 과정에서 13 개 파손 — 검수 안 했음 → 시스템 100, 실 87
  • 셀러가 다른 채널에서 30 개 팜 — 동기화 안 함 → 시스템 100, 실 70
  • 상자 라벨 잘못 붙음 — 다른 SKU 로 검수됨 → A 시스템 100, A 실 80, B 시스템 50, B 실 70
  • 분실 / 도난 — 누군가가 가져감 → 시스템 100, 실 90
  • 유통기한 만료 — 폐기 안 함 → 팔리지만 받는 사람은 환불 요청

이 차이는 코드로 풀리지 않는다. 물리 세계의 검수 프로세스 만이 푼다.

1-2. 기술 설계 침투 — 재고 ≠ 단일 숫자

순진한 모델은:

products.stock INTEGER  -- 100, 99, 98, ...

이걸로는 재고 drift 의 *원인 추적 이 불가능. 누가 언제 무엇을 했는지 흔적이 없다. 정공은 *재고를 시계열 이벤트의 적분 으로 모델링.

inventory_events
  id, sku_id, delta, reason, ref_type, ref_id, occurred_at, recorded_by

   sku_id     | delta | reason         | ref_type     | ref_id
  -----------+-------+----------------+--------------+--------
   SKU-A     | +100  | INITIAL_STOCK  | NULL         | NULL
   SKU-A     | -3    | ORDER_DEDUCT   | order        | O-001
   SKU-A     | -13   | DAMAGE         | manual       | M-002
   SKU-A     | +50   | RESTOCK        | inbound      | I-005
   SKU-A     | -5    | INVENTORY_AUDIT| stocktake    | S-006

현재 재고 = SUM(delta). 그러나 더 중요한 건 각 변경의 출처 추적. 셀러가 등록한 +100 인지, MD 가 손으로 줄인 -13 인지, 주문 차감 -3 인지. 책임 사슬 이 코드에 명시되어 있어야 분쟁 시 답변 가능.

추가로 논리 재고 (시스템이 팔 수 있다고 약속 한 수) 와 물리 재고 (창고에 실제로 있는 수) 를 분리 한다:

sku_a.physical_stock = 100     -- 창고 직원이 본 숫자
sku_a.reserved_stock = 12      -- 결제 진행 중이라 잠금
sku_a.committed_stock = 5      -- 결제 완료, 배송 대기
sku_a.available_stock = physical_stock - reserved_stock - committed_stock = 83

available_stock 만이 주문 가능 수. 이 모델은 checkout 동시성 + 재고 drift 둘 다 풀어준다.

1-3. 핵심 패턴

비기술 문제 코드 패턴
재고 drift 의 원인이 사람·물리 inventory_events 로 모든 변경에 reason / actor / ref 기록
checkout 동시성 + 물리 재고 분리 available / reserved / committed 의 상태 머신
재고 부재 발견 시점이 출고 직전 결제 후 재고 확정 단계최종 검증 (이미 commit 된 차감이 실 재고로 못 만들면 자동 환불 + 셀러 페널티)

2. 쿠폰·프로모션 — 조합 폭발 + 부담 책임

2-1. 조합 폭발의 본질

쿠폰 하나는 단순하다 — “10% 할인”. 그런데 현실:

  • 회원 등급 쿠폰 (브론즈/실버/골드)
  • 첫 구매 쿠폰
  • 카테고리 쿠폰 (식품 5%)
  • 셀러 쿠폰 (특정 셀러만)
  • 카드사 쿠폰 (특정 카드만)
  • 시간대 쿠폰 (“10시~12시”)
  • 묶음 쿠폰 (“3개 사면 1개 무료”)
  • 적립금
  • 무료배송 쿠폰
  • 쿠폰 + 쿠폰 중복 가능 / 불가능 정책

상품 1 개 결제에 적용 가능한 쿠폰 조합이 수십 종. 그중 최적 조합을 자동 추천 할지 사용자가 선택 할지 결정해야 한다. 자동 추천하면 “왜 다른 쿠폰을 안 썼냐” 분쟁. 사용자 선택이면 “몰랐다” 클레임.

2-2. 부담 책임 이라는 진짜 비기술 문제

10% 할인을 누가 부담 하나?

  • 플랫폼 부담 (마케팅 비용) — 셀러 정산은 원가 그대로
  • 셀러 부담 — 셀러가 자기 마진에서 빼 줌
  • 카드사 부담 — 카드사 제휴 마케팅
  • 분담 — 셀러 5% + 플랫폼 5%

이 결정은 코드가 아니라 *합의서. 마케팅 / MD / 셀러 / CFO 사이에서 사전 협상. 그런데 *대규모 프로모션 때 협상이 PG 등록 시점에 끝나지 않은 채 출시 되는 일이 빈번. 정산 시점에 “누가 부담하기로 했지” 가 분쟁의 핵.

2-3. 기술 설계 침투 — 모든 할인의 부담 주체 명시화

코드에 coupon 의 부담 분배적용 시점에 박제:

discount_application
  id, order_id, line_item_id, coupon_id,
  amount, bearer_type, bearer_share_bps

  order_id  | line_item_id | coupon_id | amount | bearer_type | bearer_share_bps
  ---------+--------------+-----------+--------+-------------+------------------
   O-001   | LI-1         | C-A       | 5000   | platform    | 10000  (100%)
   O-001   | LI-1         | C-B       | 3000   | seller      | 10000  (100%)
   O-001   | LI-2         | C-C       | 2000   | platform    | 5000   (50%)
   O-001   | LI-2         | C-C       | 2000   | seller      | 5000   (50%)

적용 시점의 *부담 정책 스냅샷. 정책이 이후에 바뀌어도 이 주문의 정산 계산은 적용 시점 정책으로 영원히 동일. 과거 정산을 재계산할 일이 없도록 정책을 불변 데이터 로 박제.

이게 settlement 시스템 에서 마지막에 가장 어려운 부분. 결제 금액은 단순 계산이지만 정산 금액그 결제에 적용된 *모든 할인의 부담 주체별 분배 + 수수료율 + 환불 시 누구한테서 차감. 이걸 주문 1 건 = 정산 계산 1 줄 이 아니라 주문 1 건 = N 줄의 정산 항목 으로 모델링.

2-4. 핵심 패턴

비기술 문제 코드 패턴
쿠폰 부담 합의 가 정산 시점에야 분쟁 discount_application 에 부담 주체 + 분담률 적용 시점 박제
쿠폰 정책 변경 시 과거 거래 영향 우려 정책을 immutable snapshot 으로 저장
쿠폰 조합 폭발 의 UI 복잡도 백엔드는 모든 조합 평가 가능, UI 는 추천 1개 + 상세 선택 분리

3. 정산 — 회계·세무 의 복잡도

3-1. 정산 = 단순 송금 아님

순진한 정산:

정산금액 = 결제금액 × (1 - 수수료율)

현실:

  • 카드 PG 수수료 2.5% (체크카드 / 신용카드 / 간편결제 다름)
  • 플랫폼 수수료 3% ~ 12% (카테고리·셀러 등급별 차등)
  • 카드 매출세금계산서 발행 의무 (사업자 셀러)
  • 부가세 신고 — 셀러가 사업자면 부가세 별도 처리
  • 원천징수 — 일부 셀러는 사업자가 아니라 개인사업자/프리랜서 → 3.3% 원천징수
  • 정산 주기 합의익월 15일 / 매주 수요일 / 주문 후 7일
  • 분리 정산 — 묶음배송이라도 셀러별로 정산
  • 연말정산 / 종합소득세 신고용 자료 제공

3-2. 회계 부서가 묻는 질문

진짜 어려움은 회계팀의 과거 거래에 대한 정확한 답변:

  • 2026 년 3 월에 셀러 A 에게 지급된 총액은?
  • 그 중 부가세 별도는?
  • 원천징수 금액은? 그 셀러는 사업자인가 개인인가?
  • 환불된 거래의 회계 처리는?
  • 프로모션 부담 비용은 마케팅 비용으로 잡혔나?

세무조사 시 이 질문들에 근거 자료7 년 보관 해야 한다. 코드가 7 년 후에도 같은 답 을 내야 한다.

3-3. 기술 설계 침투 — immutable 정산 ledger

정산은 덮어쓰는 데이터가 아님. 모든 정산 결과는 append-only ledger. 환불·취소 시 이전 항목 수정 X — 역분개 항목 추가.

settlement_ledger
  id, seller_id, occurred_at, account_type, amount, currency,
  ref_type, ref_id, idempotency_key, posted_by

  seller_id | type     | amount  | ref_type    | ref_id
  ----------+----------+---------+-------------+--------
   S-A      | SALE     | +50000  | order       | O-001
   S-A      | PG_FEE   | -1250   | payment     | P-001
   S-A      | FEE      | -1500   | settlement  | ST-001
   S-A      | VAT_OUT  | -4545   | settlement  | ST-001
   S-A      | REFUND   | -50000  | refund      | R-001  (역분개)
   S-A      | PG_FEE   | +1250   | payment     | P-001  (역분개)
   S-A      | FEE      | +1500   | settlement  | ST-001 (역분개)
   S-A      | VAT_OUT  | +4545   | settlement  | ST-001 (역분개)

잔액 = SUM(amount). 복식부기 처럼 모든 거래를 *append 한다. 7 년 후 누군가가 *“이 셀러의 2026-03 매출은?” 이라 물으면 해당 월 범위의 SALE 합 으로 즉답 가능. 세무조사 대응 가능.

3-4. 핵심 패턴

비기술 문제 코드 패턴
회계 7 년 보관 + 정확한 과거 답변 append-only ledger, 수정 X — 역분개 추가
셀러별 사업자 / 개인 / 원천징수 구분 seller 의 세무 타입시점 별 effective_from 으로 보관
PG 수수료 / 플랫폼 수수료 / VAT 분리 account_type 별 별도 행 으로 분개

4. 환불 — 법정 7 일 룰 과 회계 처리

4-1. 법적 기한 — 7일 단순청약철회

전자상거래법 17조: 소비자는 *물품 수령 후 7일 이내 단순 변심 으로 청약 철회 가능. 셀러가 거부 못 한다. 예외 (개봉 후 가치 훼손 / 디지털 콘텐츠 등) 가 있지만 *기본 정책은 7 일 무조건 환불. 즉 결제 후 7 일 + 배송 기간 = 약 14 일 동안 모든 거래는 “환불될 수 있는 미확정 상태”.

이게 정산에 침투. 결제 후 즉시 셀러에게 송금 하면 나중 환불 시 셀러로부터 회수 해야 함 — 회수 안 되는 셀러 도주 위험. 그래서 정산 보류 기간 이 필수.

4-2. 기술 설계 침투 — 정산 상태 머신 + 보류 기간

order: PAID → SHIPPED → DELIVERED → SETTLED
                              ↓
                         REFUND_REQUESTED → REFUNDED

각 상태에 정산 가능 여부 가 다르다:

  • PAID → 정산 불가 (배송 안 됨)
  • SHIPPED → 정산 불가
  • DELIVERED환불 가능 기간 (D+7) 동안 정산 보류
  • DELIVERED + 7일 → 자동 SETTLED — 정산 가능
  • REFUND_REQUESTED → 정산 역분개

환불 기간 동안의 자금플랫폼 계좌예치. 셀러에게 미지급. 셀러는 정산이 보름 늦어진다는 사실에 불만 — 그러나 법적 의무 라 협상 불가.

복잡한 환불 케이스:

  • 부분 환불 — 묶음배송 3 개 중 1 개만 환불
  • 배송비 환불 — 셀러 책임 / 구매자 책임에 따라 다름
  • 쿠폰 사용 거래의 환불 — 사용한 쿠폰을 복원 할지 소진 할지
  • 적립금 사용 거래의 환불적립금 차감 후 환불할 금액 산정

이 복잡도가 settlement 시스템 의 가장 큰 코드 surface.

4-3. 핵심 패턴

비기술 문제 코드 패턴
법정 7 일 동안 정산 보류 order 의 상태 머신 + 정산 가능 시점 별도 컬럼
환불 시 복원 / 소진 결정 분기 refund_policy 의 coupon_restore / point_restore / shipping_refund 명시
부분 환불 의 정산 역분개 settlement_ledger 의 line_item 단위 분개

5. 외부 의존 — PG / 카드사 / 3PL 의 통제 불가

5-1. 우리가 통제 못 하는 시스템들

이커머스는 수많은 외부 시스템에 의존:

  • PG (Payment Gateway) — 토스페이먼츠 / 나이스 / KG이니시스 …
  • 카드사 — 신한·국민·우리·하나… 각각의 점검 시간
  • 간편결제 — 카카오페이 / 네이버페이 / 페이코 …
  • PG 의 *PG — 해외카드는 GlobalPG 거쳐 발급사로
  • 3PL (Third-Party Logistics) — 택배사 연동 API각자 다른 스펙
  • 물류센터 — 자체 또는 풀필먼트 업체
  • 세무·회계 — 더존·SAP 등 ERP 연동
  • 알림톡 / SMS / 메일 — 외부 통신사
  • 주소·지번 — 행자부 API
  • 지도 — 카카오맵 / 네이버지도

우리가 코드를 잘 짜도외부 시스템이 다운 되면 기능이 죽는다. 카드사 1 개 점검만으로 결제 5% 가 실패한다.

5-2. 외부 시스템은 *언제든 스펙을 바꾼다*

  • PG 의 결제 API 가 v1 → v2 으로 변경
  • 카드사 3DS 인증 정책 강화
  • 택배사 송장 조회 API 인증 방식 변경
  • 수수료율 인상

이 변경을 우리가 의사결정 X. 통지받고 따라가야 함.

5-3. 기술 설계 침투 — adapter 분리 + Circuit Breaker + Outbox

이게 Hexagonal Architecture진짜 정공 인 이유. 외부 의존을 *port 로 추상화* + adapter 가 실제 구현 — 외부 스펙 변경 시 adapter 만 교체. 코어 도메인은 PG 가 토스든 이니시스든 모름.

추가로 Resilience4j 의 Circuit Breaker — 외부 시스템 5xx 연속 시 자동 차단 + fallback. Bulkhead — 외부 호출 thread pool 격리 → 한 외부 시스템 hang 이 다른 기능 마비 안 시킴.

Outbox + Kafka — 결제 완료 이벤트를 우리 시스템에 기록 + 다른 모든 시스템 (정산 / CS / 마케팅) 은 이벤트로 받음. 외부 시스템 다운 시에도 우리는 이벤트 발행 → DB 안에 보존 → 외부 복구 시 retry.

5-4. 핵심 패턴

비기술 문제 코드 패턴
외부 시스템 스펙 변경 빈번 port / adapter 분리, adapter 교체로 끝
외부 시스템 다운 → 우리도 다운 Circuit Breaker + Bulkhead + fallback
외부 시스템 retry 정책 + at-least-once Outbox + 멱등 처리 + DLQ

6. 이해관계자 충돌 — 셀러 vs 구매자 vs 플랫폼 vs CS vs MD vs 엔지니어

6-1. 진짜 비기술

이커머스의 모든 결정최소 4 명의 의견 충돌:

이해관계자 우선순위
셀러 정산 빨리, 수수료 낮게, 노출 많이
구매자 가격 싸게, 배송 빠르게, 환불 쉽게
플랫폼 (CEO/CFO) 수익률, 시장점유율, 브랜드
CS (Customer Service) 클레임 적게, 처리 빠르게
MD (Merchandising) 카테고리 다양화, 차별화 상품
마케팅 프로모션 효과 측정
법무 분쟁 최소화, 규제 준수
재무 정확한 정산, 빠른 결산
엔지니어 유지보수성, 시스템 안정성

같은 사고 에 대해 각자 다른 해석. 셀러가 어드민에서 가격을 0 원으로 잘못 등록 하고 그 시간에 5000 명 주문 했다 — 플랫폼은 환불? 강제 취소? 그대로 출고?

  • 셀러: 부도나니까 취소 해주세요
  • 구매자: 이미 결제했는데?
  • CS: 전화 폭주, 처리 시간 부족
  • 법무: 판매자 일방 취소는 위험
  • 재무: 환불 처리 비용
  • 엔지니어: 0원 등록 자체를 막을 수 있게 검증 추가하자

6-2. 기술 설계 침투 — 권한 / 감사 / 상태 머신

누가 무엇을 결정할 수 있는지역할 기반 권한 으로 명시:

admin_user.role:
  SUPER_ADMIN     | 모든 권한
  MD              | 상품 관리, 정산 조회
  CS              | 환불 처리, 주문 변경 (단 셀러 동의 필요)
  FINANCE         | 정산 보고서, 세금계산서
  MARKETING       | 쿠폰 / 프로모션 등록
  LEGAL           | 분쟁 케이스 검토

모든 권한 행사audit_log 에 기록:

audit_log
  who | action            | target          | when            | reason
  ----+-------------------+-----------------+-----------------+-----------------
  CS1 | REFUND_FORCE      | order O-001     | 2026-06-08 10am | seller agreed via phone
  MD1 | PRICE_OVERRIDE    | product P-100   | 2026-06-08 11am | bulk import error fix

“왜 그랬는지” 가 코드에 영원히 남는다. 분쟁 시 근거 자료 로 활용. 셀러가 1 년 후 *“내가 그때 동의 안 했다” 라 하면 audit_log 의 reason 으로 반박 가능*.

6-3. 핵심 패턴

비기술 문제 코드 패턴
각 부서의 권한 충돌 RBAC (role-based access control) + 명시된 분리
결정의 사후 분쟁 audit_log 의 who / action / target / when / reason
예외 처리의 일관성 예외 처리도 상태 머신의 transition 으로 코드화

7. 어뷰징 / 사기 / 분쟁 — 고객의 비합리적 행동

7-1. 대다수는 정상이지만 일부는 적극적 어뷰징

  • 쿠폰 다중 사용 — 가족 계정으로 첫 구매 쿠폰 반복
  • 환불 사기받았는데 안 받았다 주장
  • 리뷰 조작5 점 리뷰 1 원 알바
  • 경쟁사 *별점 테러
  • 가격 오류 (0 원 등록) 대량 주문셀러 책임 이지만 플랫폼이 떠안음
  • 재고 부족 시 *암표상 — 한정판 수십개 매입 후 외부 재판매
  • 셀러의 자기 거래 — 매출 보이려고 자기 상품 자기가 결제 + 환불

7-2. 기술 설계 침투 — 이상 탐지 + 한도 + 사후 분석

순진한 시스템은 모든 요청을 신뢰. 정공은:

  • rate limit (시간당 N 회) — 같은 IP / 같은 카드 / 같은 디바이스
  • 행동 패턴 점수 — 신규 계정 / 빈번한 환불 / 짧은 클릭 간격
  • 블랙리스트 — 사기 이력 카드 / IP / 디바이스
  • 2 차 인증 — 고가 결제 시 SMS / 본인 인증
  • 셀러 자기 거래 탐지 — 같은 IP 의 매출 패턴 검증

이걸 코어 결제 경로 에 직접 박지 말고fraud-detection 서비스별도 분리 해서 결제 후 비동기 분석 + 의심 거래 hold. false positive 가 진짜 거래를 막는 것 이 더 큰 손실이라 결제는 일단 허용 + 사후 검토 패턴이 정공.

7-3. 핵심 패턴

비기술 문제 코드 패턴
어뷰징은 점점 정교화 rule + ML 점수 + 사람 검토의 3 단계
false positive 가 진짜 거래 차단 결제 허용 + 사후 hold/review
분쟁 시 증거 부족 모든 결정 디바이스 fingerprint + IP + 행동 로그 보존

8. 그 모든 것이 *기술 설계 에 침투하는 4 가지 공통 패턴*

위 7 가지 비기술 문제가 결국 코드 설계 로 어떻게 침투하는지 — 4 가지 패턴으로 수렴한다.

8-1. 상태 머신 + audit_log (사후 답변 가능성)

분쟁 / 회계 / 법무 가 6 개월 후에 묻는 질문 에 답하려면 모든 상태 변화누구에 의해 언제 발생했는지 기록되어야 함. order → payment → settlement → refund 의 각 상태 transition 에 audit_log 가 따라간다. 상태 변경 = 1 코드, audit 기록 = 또 다른 1 코드 가 아니라 상태 변경 메서드 자체가 audit 을 강제 하는 구조.

8-2. immutable snapshot + append-only ledger (시점 별 진실)

쿠폰 / 정산 / 수수료 / 정책. 지금 정책그 거래가 발생했을 때의 정책 이 다르다. 거래 시점의 정책을 immutable snapshot 으로 박아두지 않으면 6 개월 후 정산 계산이 달라진다. ledger 도 마찬가지 — 환불은 기존 항목 수정 이 아니라 역분개 항목 추가. 영원히 무엇이 일어났는지 추적 가능.

8-3. port / adapter + 정책 외부화 (변경 흡수)

외부 의존 (PG / 카드사 / 3PL) 의 스펙 변경 빈도가 높다. 이걸 코어 도메인 에 박아두면 PG 1 곳 바꾸려고 도메인 전체 리팩토링. Hexagonal 의 outbound port + adapter진짜 효과를 보는 영역. 마찬가지로 수수료율 / 쿠폰 정책 / 환불 정책코드 상수가 아니라 DB / config 의 *시점별 active 항목* 으로.

8-4. 멱등 + 재시도 가능한 워크플로 (실패에서의 복구)

이커머스의 모든 외부 통신언젠가 실패. 결제 OK 인데 PG 응답 못 받은 상태, 송금 OK 인데 cron 다운, 배송 처리 OK 인데 알림 발송 실패. 모든 endpoint 가 idempotency_key 받고 멱등 보장. 실패한 단계는 그 단계만 재시도 가능 — 전체 trans 다시 안 함. Outbox + Kafka + processed_events3 단 멱등 방어 가 이 패턴의 구체화.


9. 마무리 — 기술 + 비기술 의 통합 사이클

“이커머스의 진짜 어려움은 *코드가 풀지 못하는 문제코드에 흔적을 남기는 방식 을 설계하는 것”. 우리는 *재고 drift 를 막을 수 없지만 *원인을 추적할 수 있고, 쿠폰 부담 책임 합의 를 강제할 수 없지만 *적용 시점 정책을 박제할 수 있고, 외부 시스템 다운을 막을 수 없지만 *우리 시스템이 같이 안 죽도록 분리할 수 있다*.

좋은 이커머스 시스템의 신호:

  • 6 개월 전 거래 에 “어떻게 됐었지?” 라는 질문에 5 분 안에 답 할 수 있는가
  • 새 PG 추가 가 2 시간 안에 PR 1 개 인가
  • 셀러 1000 명 의 정산이 월말 1 일 안에 끝나고 수정 가능 한가
  • 사고 발생 시 되돌리기사람 없이 가능한가
  • 부서 간 결정이 코드에 기록 되어 분쟁 시 근거 가 되는가

“진짜 좋은 시스템은 기술적으로 화려한 게 아니라 *비기술 문제가 닥쳤을 때 답할 수 있는 시스템”* — 이 글이 코드 작성보다 그 답할 수 있는 구조 설계 를 한 단계 더 의식하게 해줬길.

이커머스 도메인의 기술적 글 (Hexagonal / Outbox / MSA 분리 / GitOps) 을 이전 글에서 다뤘다면, 이 글은 그 모든 기술이 결국 풀려고 했던 비기술 문제들 의 지도. 어느 한 쪽만 보면 과도 설계 거나 과소 설계. 둘이 만나는 지점 이 진짜 정공이다.