← 프로젝트 목록

장바구니(Cart)

7단계 카트 새로고침 파이프라인 + Lost Update 방지

DDDHexagonal ArchitectureDomain ServiceJPA

배경 및 제약조건

  • 레거시 카트 테이블(g5_shop_cart)이 주문 아이템 테이블을 겸용 — ct_status로 '쇼핑'/'주문' 구분
  • 레거시 DB 테이블 구조 변경 불가, PHP 시스템이 동일 DB를 공유하며 동시 운영
  • 비회원(세션) → 회원 전환 시 카트 병합, 분산된 고아 카트 통합 필요
  • 다양한 프로모션(타임딜, 더블딜, 사은품, 뉴멤버딜, K-POP) 및 쿠폰이 카트 단계에서 적용
  • 국제 배송(150+ 국가) × 멀티 통화(USD 기준) 가격 계산

설계 구조

Hexagonal Architecture

[Adapter-In] CartController
      │ UseCase (Port-In)
[Application] RefreshCartService
      ├── CartPreparationFacade (데이터 준비)
      ├── DiscountFetcher (쿠폰/포인트/리워드)
      ├── ShippingResolutionFacade (배송비)
      └── CartPersistenceFacade (저장 + 응답)
      │ Domain
[Domain]
      ├── Cart (Aggregate Root, ~980줄)
      ├── CartItem (Entity) + CartItemFlags (14종 플래그)
      ├── CartRefreshPolicy (상태 전이 순서 보장)
      ├── CartConsolidator (카트 병합 규칙)
      └── CartPurchaseValidator (구매 제한 검증)
      │ Port-Out
[Adapter-Out] CartPersistenceAdapter → g5_shop_cart (MySQL)

CartRefreshPolicy 7단계 파이프라인:
├── 1. 프로모션 메타 선적용 (maxQty, flags)
├── 2. 재고/품절/수량제한 검증
├── 3. 수량 자동 보정 (부족→가용수량, 초과→최대수량)
├── 4. 프로모션 실적용 (사은품 추가/삭제, 가격 적용)
├── 5. 최적 쿠폰 조합 계산 + 적용
├── 6. 최종 가격 계산 (상품+배송-할인+세금)
├── 7. Cart 상태 반영
└── 8. 금액/국가 기반 구매 제한 검증

Problem & Solution

Problem

카트와 주문이 하나의 테이블을 공유 — PlaceOrder 중 ViewCart의 merge가 주문 아이템을 덮어씀 (Lost Update). 데드락도 발생

Solution

g5_shop_order_item 테이블 신규 도입. '상태값 판별'에서 '테이블 존재 여부 판별'로 전환. 카트 조회 시 order_item에 존재하는 아이템 제외. 5중 방어 레이어로 경합 자체를 원천 제거

Problem

레거시에서 장바구니 조회 시 검증/할인/가격계산이 순서 없이 혼재. 프로모션과 쿠폰 적용 순서에 따라 결과가 달라지는 버그

Solution

CartRefreshPolicy(Domain Service)로 상태 전이 순서를 격리. Application Layer는 데이터 fetch만, '어떤 순서로 계산하는지'는 도메인 규칙으로 명시

Problem

비회원→회원 전환 시 카트 병합, 동일 회원의 분산된 고아 카트 통합, 주문 진행 중 카트 감지 필요

Solution

CartConsolidator 도메인 서비스로 병합 규칙 캡슐화. 고아 카트 통합(동일 상품 수량 합산), ensureCleanCart()로 주문 진행 중 카트 감지 시 새 카트 ID 전환

Problem

카트에 담은 후 상품 가격이 변경되면, 체크아웃 시 인지하지 못한 채 다른 금액으로 결제

Solution

CartItem에 snapshotPrice(담은 시점 판매가) 저장. 체크아웃 진입 시 현재 가격과 비교하여 변동 감지 → 에러 메시지와 함께 카트로 리다이렉트

기술적 챌린지

카트/카트아이템 테이블 미분리

하나의 테이블(g5_shop_cart)이 카트 메타 + 아이템을 모두 담고 있어서, Aggregate 경계 설정과 조회/저장 로직이 복잡. 카트 ID 기준 그룹핑으로 도메인 모델을 매핑해야 함

비회원→회원 전환 병합 + 주문취소 복귀

게스트 카트 → 회원 카트 병합(동일 상품 수량 합산), 고아 카트 통합, 주문 취소 시 아이템이 카트로 자연 복귀해야 하는데 상태값만으로 판별하면 타이밍 이슈 발생

성과

  • Home 이탈률 77.9% 감소 (19.6% → 5.9%) — 카트 새로고침 파이프라인 안정화 기여
  • 결제 중 주문 유실 0건, 데드락/Lost Update 원천 해소
  • 레거시 호환성 100% 유지 — DB 스키마 변경 없이 도메인 모델 전환
  • 도메인 로직 집중 — Cart Aggregate에 모든 비즈니스 규칙 캡슐화
  • 14종 프로모션 통합 — 단일 플래그 시스템으로 관리
  • 가격 변동 감지 — 체크아웃 시 스냅샷 가격과 비교