💻 PROJECT/Team Projects

[ 🚚 ONESTOP ] 정책 설계 — 옵션 중복 방지와 상태 관리

eunjiom 2026. 5. 15. 20:53

1. 상품 옵션 최소 1개 — 무옵션(기본) 처리

무옵션 상품도 product_item에 기본 옵션 1개는 있어야 재고 관리가 가능함

재고(stock)가 product_item에 있기 때문에 옵션이 0개면 재고를 어디에 붙일지 애매해짐. 무옵션 상품은 아래처럼 처리하기로 결정함

option_value_1: "기본"
option_value_2~5: null
stock: 100

2. Composite Unique Constraint — 옵션 조합 중복 불가

동일 상품 내에서 옵션 조합이 중복되면 재고 관리가 불가능해짐

예를 들어 색상(빨강) + 사이즈(M) 조합이 두 개 존재하면 어느 쪽 재고를 차감해야 할지 알 수 없음

 

선택한 방법 - Composite Unique Constraint

  • product_item 테이블에 (product_id, option_value_1 ~ option_value_5) 복합 유니크 제약 적용
  • DB 레벨에서 중복을 막아서 동시에 두 요청이 들어와도 하나는 반드시 실패하므로 정합성 보장
  • 애플리케이션 레벨에서만 막으면 동시성 문제에서 취약함

3. 상품 승인/반려 플로우 — 기술 선택

상태 관리 — Enum 상태머신

  • String 컬럼 - 잘못된 값이 들어올 수 있고 허용되지 않는 상태 전이를 막기 어려움
  • Enum 상태머신 - 허용된 상태값만 존재하고 컴파일 시점에 오류를 잡을 수 있음. 상태 전이 로직을 Enum 안에 캡슐화해서 전이 규칙을 강제할 수 있어서 선택함
public enum ProductStatus {
    TEMP_SAVED, APPROVE_REQUESTED, APPROVED,
    REJECTED, DISCONTINUED, FORCE_INACTIVE;

    public boolean canRequestApproval() {
        return this == TEMP_SAVED || this == REJECTED;
    }
}

 

반려 시 기존 정보 유지 — Soft Update

  • 이력 테이블 - 변경 전 데이터를 별도 테이블에 저장하는 방식. 데이터 추적은 가능하지만 MVP 단계에서는 오버스펙
  • Soft Update - 상태값만 REJECTED로 바꾸고 기존 정보를 유지하는 방식. 반려 후 수정 없이 오랜 시간 두더라도 별도 만료 처리 없이 REJECTED 상태로 유지되고 판매자가 언제든 수정 후 재승인 요청 가능. 만료 기간이 필요하면 스케줄러를 추가하면 되지만 MVP에서는 불필요하다고 판단해서 선택함

동시성 — 낙관적 락 (@Version)

  • 비관적 락 - 승인/반려는 자주 일어나는 작업이 아니라서 매번 DB를 잠그면 오버헤드가 큼
  • 낙관적 락 - @Version 필드로 충돌을 감지하고 충돌 시 예외를 던져서 처리함. 관리자 동시 접근 가능성이 낮아서 충돌이 드물고 재시도 비용이 낮기 때문에 선택함