OPEN HYPER STEP
← 목록으로 (stack-analysis)
STACK-ANALYSIS · 71 / 90
stack-analysis
CHAPTER 71 / 90
읽기 약 2
FUNCTION

이커머스 아키텍처: 상품/주문/결제


핵심 개념

도메인 모델·이벤트 흐름·MSA vs 모놀리스 — 쿠팡·11번가·당근 패턴.

본문

핵심 도메인 모델

📋 코드 (19줄)
[Catalog] 상품 도메인
- Product, Variant, Category, Tag
- Inventory, Pricing

[Customer] 고객 도메인
- User, Address, PaymentMethod
- Cart, Wishlist

[Order] 주문 도메인
- Order, OrderItem, Status
- Payment, Refund

[Fulfillment] 배송 도메인
- Shipment, Tracking
- Warehouse, Carrier

[Marketing] 마케팅
- Coupon, Promotion
- Review, Rating

주문 흐름 (이벤트)

📋 코드 (21줄)
1. 사용자가 장바구니 → 체크아웃
   → Order 생성 (status: pending)

2. 결제 시도 → PG 호출
   → Payment 생성 (status: processing)

3. PG 응답 (성공)
   → Payment.status = completed
   → Order.status = paid
   → 재고 차감 (트랜잭션)
   → 결제 완료 이메일

4. 사장이 주문 확인 → 발송
   → Shipment 생성
   → Order.status = shipped
   → 배송 시작 알림

5. 배송 완료 (택배사 webhook)
   → Order.status = delivered
   → 7일 후 자동 확정
   → Order.status = completed

모놀리스 vs MSA

📋 코드 (19줄)
[모놀리스] (~$10M GMV)
- 단일 Next.js + PostgreSQL
- 트랜잭션 강력
- 시작 빠름
- 작은 팀에 적합


[모듈러 모놀리스] ($10~100M GMV)
- 단일 배포 + 도메인 분리
- shared DB + private 스키마
- 점진적 분해 가능

[MSA] ($100M+ GMV)
- Catalog Service
- Order Service
- Payment Service
- Fulfillment Service
- Notification Service
- 각자 DB + Kafka로 통신

데이터 모델

SQL📋 코드 (63줄)
-- 상품
CREATE TABLE products (
  id UUID PRIMARY KEY,
  name VARCHAR NOT NULL,
  description TEXT,
  category_id UUID,
  base_price DECIMAL(10,0) NOT NULL,
  status VARCHAR NOT NULL DEFAULT 'draft',  -- draft, active, archived
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE product_variants (
  id UUID PRIMARY KEY,
  product_id UUID NOT NULL REFERENCES products(id),
  sku VARCHAR UNIQUE NOT NULL,
  name VARCHAR NOT NULL,  -- "Red - L"
  attributes JSONB,        -- { color: 'red', size: 'L' }
  price DECIMAL(10,0) NOT NULL,
  stock_quantity INT NOT NULL DEFAULT 0,
  weight_g INT
);


-- 주문
CREATE TABLE orders (
  id UUID PRIMARY KEY,
  order_number VARCHAR UNIQUE NOT NULL,  -- 사람 친화 (ORD-20260429-001)
  user_id UUID NOT NULL,
  status VARCHAR NOT NULL,  -- pending, paid, shipped, delivered, cancelled
  subtotal DECIMAL(10,0) NOT NULL,
  shipping_fee DECIMAL(10,0) DEFAULT 0,
  discount DECIMAL(10,0) DEFAULT 0,
  tax DECIMAL(10,0) DEFAULT 0,
  total DECIMAL(10,0) NOT NULL,
  shipping_address JSONB NOT NULL,
  billing_address JSONB,
  created_at TIMESTAMP DEFAULT NOW(),
  paid_at TIMESTAMP
);

CREATE TABLE order_items (
  id UUID PRIMARY KEY,
  order_id UUID NOT NULL REFERENCES orders(id),
  variant_id UUID NOT NULL,
  product_name VARCHAR NOT NULL,    -- 주문 시점 스냅샷
  variant_name VARCHAR NOT NULL,
  unit_price DECIMAL(10,0) NOT NULL, -- 스냅샷
  quantity INT NOT NULL,
  subtotal DECIMAL(10,0) NOT NULL
);


-- 결제
CREATE TABLE payments (
  id UUID PRIMARY KEY,
  order_id UUID NOT NULL REFERENCES orders(id),
  pg_provider VARCHAR NOT NULL,  -- toss, kakao, stripe
  pg_transaction_id VARCHAR UNIQUE,
  amount DECIMAL(10,0) NOT NULL,
  status VARCHAR NOT NULL,
  paid_at TIMESTAMP,
  failed_reason VARCHAR
);

주문 시 트랜잭션

TYPESCRIPT📋 코드 (48줄)
async function placeOrder(userId: string, items: CartItem[], addressId: string) {
  return db.$transaction(async (tx) => {
    // 1. 재고 검증 + 차감 (atomic)
    for (const item of items) {
      const updated = await tx.productVariant.updateMany({
        where: {
          id: item.variantId,
          stockQuantity: { gte: item.quantity },
        },
        data: {
          stockQuantity: { decrement: item.quantity },
        },
      });
      if (updated.count === 0) {
        throw new ConflictError(`재고 부족: ${item.variantId}`);
      }
    }

    // 2. 주문 생성
    const subtotal = items.reduce((s, i) => s + i.unitPrice * i.quantity, 0);
    const order = await tx.order.create({
      data: {
        userId,
        orderNumber: await generateOrderNumber(),
        status: 'pending',
        subtotal,
        total: subtotal,  // 배송비·할인 계산 후 갱신
        shippingAddress: await tx.address.findUnique({ where: { id: addressId } }),
        items: {
          create: items.map(i => ({
            variantId: i.variantId,
            productName: i.productName,
            variantName: i.variantName,
            unitPrice: i.unitPrice,
            quantity: i.quantity,
            subtotal: i.unitPrice * i.quantity,
          })),
        },
      },
      include: { items: true },
    });

    // 3. 장바구니 비우기
    await tx.cart.deleteMany({ where: { userId } });

    return order;
  }, { isolationLevel: 'Serializable' });
}

다음 챕터

CH.72 "상품 카탈로그: 카테고리/태그/변형".


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 프로젝트의 이커머스 아키텍처 부분을 분석해서
실전 분석 + 개선 우선순위를 알려줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

이커머스 아키텍처 관련 실제 서비스 5개를
비교 분석해서 패턴 추출를 알려줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 코드베이스에서 이커머스 아키텍처
최적화 가능 위치를 보고해줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 한국 풀스택 시장의
이커머스 아키텍처 트렌드를 솔직히 알려줘.

⭐ 이것만 기억하세요
이커머스 아키텍처: 상품/주문/결제 이 3가지만 확실히 잡으세요
1.이커머스는 5도메인 — Catalog·Customer·Order·Fulfillment·Marketing
2.주문 시 가격·이름은 스냅샷 저장 — 상품 변경 후에도 주문 보존
3.재고 차감은 트랜잭션 + 조건부 update — race condition 방지


공유하기
진행도 71 / 90