diff --git a/docs/week2/01-requirements.md b/docs/week2/01-requirements.md new file mode 100644 index 0000000..774656b --- /dev/null +++ b/docs/week2/01-requirements.md @@ -0,0 +1,212 @@ +# 요구사항 명세서 (Requirements Specification) + +## 1. 개요 + +### 1.1 태스크 목표 정의 + +#### 해결하고자 하는 문제 +- 기존 대형 이커머스는 상품 위주의 탐색 경험으로, 사용자가 좋아하는 상품을 발견하고 모으는 즐거움이 부족 +- 구매 시 매번 결제 정보를 입력해야 하는 번거로움으로 인한 구매 전환 장벽 존재 +- 사용자의 취향과 관심사를 반영한 개인화된 쇼핑 경험 부재 + +#### 태스크 목표 +- 좋아요 기능을 통해 사용자가 관심 있는 상품을 손쉽게 큐레이션할 수 있는 환경 제공 +- 포인트 선충전 모델로 구매 전환 장벽을 낮추고 빠른 결제 경험 제공 +- 사용자 행동 데이터(좋아요, 구매 패턴)를 수집하여 향후 추천/랭킹 시스템 구축 기반 마련 + +#### 2주차 구현 범위 +본 명세서는 다음 기능의 설계 및 구현을 다룹니다: +- **상품 탐색**: 상품 목록 조회, 상품 상세 조회, 브랜드 조회 +- **좋아요 관리**: 좋아요 등록/취소, 좋아요한 상품 목록 조회 (멱등성 보장) +- **주문 처리**: 주문 생성 및 결제 (재고 차감, 포인트 차감, 외부 시스템 연동) + +#### 제외 범위 +다음 기능은 1주차에서 구현 완료되었으며, 본 명세서에서는 다루지 않습니다: +- 회원가입 및 내 정보 조회 +- 포인트 충전 및 보유 포인트 조회 +- 인증/인가 (X-USER-ID 헤더 기반 식별) + +### 1.2 기대 효과 + +- **사용자 재방문율 증가**: 좋아요 기능을 통한 관심 상품 큐레이션으로 재방문 유도 +- **전환율 향상**: 포인트 선충전 모델로 구매 시 결제 단계를 최소화하여 전환 장벽 감소 +- **데이터 기반 확장 가능성**: 사용자 행동 데이터를 수집하여 향후 추천/랭킹 시스템 구축 기반 마련 + +--- + +## 2. 도메인 용어집 + +### Products 도메인 + +**핵심 개념:** +- **브랜드 (Brand)**: 상품을 생산하고 제공하는 패션 브랜드 또는 제조사 +- **상품 (Product)**: 구매 가능한 개별 패션 아이템. 가격, 소속 브랜드, 재고 수량 정보를 포함 + +**상태:** +- **판매중 (AVAILABLE)**: 재고가 있어 구매 가능한 상태 +- **품절 (OUT_OF_STOCK)**: 재고가 소진되어 구매 불가능한 상태 +- **단종 (DISCONTINUED)**: 더 이상 판매하지 않는 상태 + +### Likes 도메인 + +**핵심 개념:** +- **좋아요 (ProductLike)**: 사용자가 특정 상품에 대해 표시하는 관심 표현. 사용자는 각 상품에 최대 1개의 좋아요만 등록 가능 + +### Orders 도메인 + +**핵심 개념:** +- **주문 (Order)**: 사용자가 특정 상품의 구매를 요청하고 결제를 완료한 거래 단위 +- **결제 (Payment)**: 주문에 대한 포인트 차감 및 결제 처리 정보 + +**주문 상태:** +- **접수됨 (PLACED)**: 주문이 시스템에 접수된 상태 +- **결제 완료됨 (PAID)**: 결제가 완료되어 처리 대기 중인 상태 + +**결제 상태:** +- **준비 (READY)**: 결제가 준비된 상태 +- **완료 (PAID)**: 결제가 완료된 상태 + +**행위:** +- **주문하다 (place)**: 사용자가 상품을 주문하는 행위 +- **결제하다 (pay)**: 주문에 대한 결제를 완료하는 행위 + +### 공통 + +**핵심 개념:** +- **사용자 (User)**: 감성 이커머스 플랫폼에 가입하여 상품을 탐색하고 구매하는 개인. "구매자"와 동일한 의미로 사용 +- **포인트 (Point)**: 사용자가 사전에 충전하여 보유한 가상 화폐 잔액. 상품 구매 시 결제 수단으로 사용됨 + +--- + +## 3. 유스케이스 명세 + +### 3.1 주요 사용자 +- **구매자**: 패션 상품을 탐색하고 구매하는 일반 사용자 + +### 3.2 구매자의 목표 + +1. **관심 있는 상품 탐색 및 좋아요로 관리하기** + - 다양한 상품 둘러보기 + - 마음에 드는 상품 좋아요로 저장 + - 좋아요 한 상품 다시 보기 + +2. **상품 주문 및 결제하기** + - 원하는 상품 주문 요청 + - 포인트 차감 및 재고 확보 + - 주문 완료 처리 + +### 3.3 유스케이스 시나리오 + +#### 유스케이스 1: 관심 있는 상품 탐색 및 좋아요로 관리하기 + +**주요 시나리오:** +1. 사용자가 상품 목록 화면에 진입한다 +2. 시스템은 최신 등록된 상품 목록을 조회한다 (각 상품의 좋아요 수 포함) +3. 사용자가 관심 있는 상품의 상세 정보를 조회한다 (좋아요 수 포함) +4. 사용자가 해당 상품에 좋아요를 등록한다 +5. 시스템은 좋아요를 저장하고 해당 상품의 좋아요 수를 1 증가시킨다 +6. 사용자가 "내가 좋아요 한 상품 목록"을 조회하여 저장한 상품들을 확인한다 + +**대체 시나리오 1 (브랜드별 상품 필터링):** +1. 사용자가 브랜드 정보를 조회한다 +2. 사용자가 특정 브랜드를 선택하여 해당 브랜드의 상품만 필터링한다 +3. 시스템은 선택된 브랜드의 상품 목록을 조회하여 반환한다 +4. 해당 브랜드에 상품이 없으면 빈 목록을 표시한다 + +**대체 시나리오 2 (정렬 기준 변경):** +1. 사용자가 정렬 기준을 선택한다 (최신순/가격낮은순/인기순) +2. 시스템은 선택된 기준으로 상품을 정렬하여 반환한다 + +**대체 시나리오 3 (좋아요 중복 등록 - 멱등성):** +1. 사용자가 이미 좋아요를 등록한 상품에 다시 좋아요를 등록한다 +2. 시스템은 오류 없이 성공 응답을 반환하며, 좋아요 상태는 변경되지 않는다 +3. 상품의 좋아요 수는 증가하지 않는다 + +**대체 시나리오 4 (좋아요 취소):** +1. 사용자가 좋아요를 등록한 상품의 좋아요를 취소한다 +2. 시스템은 좋아요를 제거하고 해당 상품의 좋아요 수를 1 감소시킨다 +3. 사용자가 "내가 좋아요 한 상품 목록"에서 해당 상품이 제거된 것을 확인한다 + +**대체 시나리오 5 (좋아요 취소 중복 요청 - 멱등성):** +1. 사용자가 좋아요를 등록하지 않은 상품에 좋아요 취소를 요청한다 +2. 시스템은 오류 없이 성공 응답을 반환하며, 좋아요 상태는 변경되지 않는다 +3. 상품의 좋아요 수는 감소하지 않는다 + +**예외 시나리오 1 (존재하지 않는 상품):** +1. 사용자가 존재하지 않는 상품에 좋아요를 등록하거나 상세 조회를 시도한다 +2. 시스템은 "상품을 찾을 수 없음" 오류를 반환한다 + +**예외 시나리오 2 (잘못된 정렬 기준):** +1. 사용자가 지원하지 않는 정렬 기준을 입력한다 +2. 시스템은 "지원하지 않는 정렬 기준" 오류를 반환한다 + +--- + +#### 유스케이스 2: 상품 주문 및 결제하기 + +**주요 시나리오:** +1. 사용자가 원하는 상품을 선택하고 주문을 요청한다 +2. 시스템은 상품 재고를 차감한다 +3. 시스템은 주문을 접수 상태로 생성한다 +4. 시스템은 결제를 준비 상태로 생성한다 +5. 시스템은 사용자의 포인트를 차감한다 +6. 시스템은 외부 PG 시스템에 결제 요청을 전송한다 +7. 사용자에게 주문 생성 완료 응답을 반환한다 (결제 준비 상태) + +**대체 시나리오 1 (전액 포인트 결제):** +1. 사용자가 상품 주문을 요청한다 +2. 시스템은 재고를 차감한다 +3. 시스템은 주문을 접수 상태로 생성한다 +4. 시스템은 결제를 준비 상태로 생성한다 +5. 시스템은 포인트를 차감한다 (전액 결제 가능) +6. 외부 PG 호출을 스킵하고 결제를 완료 처리한다 +7. 사용자에게 주문 완료 응답을 반환한다 + +**예외 시나리오 1 (재고 부족):** +1. 사용자가 상품 주문을 요청한다 +2. 시스템이 재고 차감을 시도하지만 재고가 부족하여 실패한다 +3. 시스템은 "재고 부족" 오류를 반환한다 +4. 주문이 생성되지 않으며, 포인트도 차감되지 않는다 + +**예외 시나리오 2 (포인트 부족):** +1. 사용자가 상품 주문을 요청한다 +2. 시스템이 재고를 차감한다 +3. 시스템이 포인트 차감을 시도하지만 잔액이 부족하여 실패한다 +4. 시스템은 트랜잭션을 롤백한다 +5. 시스템은 "포인트 부족" 오류를 반환한다 +6. 주문 및 결제가 생성되지 않는다 + +**예외 시나리오 3 (외부 PG 시스템 실패):** +1. 사용자가 상품 주문을 요청한다 +2. 시스템이 재고, 포인트 차감 및 주문/결제 생성을 시도한다 +3. 외부 PG 시스템에 결제 요청 전송 시 오류가 발생한다 +4. 시스템은 트랜잭션을 롤백한다 +5. 시스템은 "결제 요청 실패" 오류를 반환한다 +6. 주문 및 결제가 생성되지 않으며, 재고와 포인트도 차감되지 않는다 + +--- + +## 4. 기술 요구사항 + +### 4.1 데이터 정합성 + +**주문 처리 (재고/포인트 차감)** +- 강한 일관성 (Strong Consistency) 필요 +- 재고 차감, 포인트 차감, 주문 생성, 결제 생성이 모두 성공하거나 모두 실패해야 함 +- 트랜잭션 롤백을 통한 원자성 보장 + +**좋아요 카운트** +- 최종 일관성 (Eventual Consistency) 허용 +- 좋아요 수는 대략적인 인기도 지표로, 실시간 정확성 불필요 +- 사용자의 좋아요 등록/취소 상태는 즉시 반영되어야 함 + +### 4.2 에러 처리 + +**주문 생성 실패** +- 재고 부족: 재고 부족 정보 제공 +- 포인트 부족: 포인트 잔액 부족 정보 제공 +- 외부 PG 시스템 실패: 결제 요청 실패 정보 전달 (전체 롤백) + +**좋아요 처리** +- 존재하지 않는 상품: "상품을 찾을 수 없음" 명확히 구분 +- 멱등성 보장: 중복 요청에도 사용자에게 성공 응답 전달 diff --git a/docs/week2/02-sequence-diagrams.md b/docs/week2/02-sequence-diagrams.md new file mode 100644 index 0000000..118377a --- /dev/null +++ b/docs/week2/02-sequence-diagrams.md @@ -0,0 +1,71 @@ +# 시퀀스 다이어그램 (Sequence Diagrams) + +본 문서는 감성 이커머스의 핵심 유스케이스에 대한 객체 간 상호작용을 시각화합니다. + +--- + +## 1. 상품 목록 조회 + +사용자가 상품 목록을 조회하는 시나리오입니다. 각 상품의 좋아요 수를 함께 조회하여 인기도를 표시합니다. + +![img](https://i.imgur.com/Og1yTzK.png) + +**주요 흐름:** + +1. 사용자가 페이징, 정렬 기준과 함께 상품 목록 조회를 요청 +2. Facade가 정렬 기준에 따라 상품 목록을 조회 (Slice 반환) +3. 조회된 상품 ID 목록으로 좋아요 수를 일괄 조회 +4. Facade에서 상품 정보와 좋아요 수를 조합하여 ProductInfo 리스트 생성 + +**고려사항:** + +- ProductLikeCount를 일괄 조회하여 N+1 문제 방지 +- 정렬 기준: latest(최신순), price_asc(가격낮은순), likes_desc(인기순) + +--- + +## 2. 상품 좋아요 등록 + +사용자가 상품에 좋아요를 등록하는 시나리오입니다. 멱등성을 보장하여 중복 등록 시에도 오류 없이 처리됩니다. + +![img_1.png](https://i.imgur.com/ttJAMA7.png) + +**주요 흐름:** + +1. 사용자가 특정 상품에 좋아요 등록 요청 +2. Facade가 기존 좋아요 존재 여부 확인 +3. 이미 존재하면 아무 작업 없이 성공 응답 (멱등성 보장) +4. 존재하지 않으면 ProductLike 생성 및 저장 +5. ProductLikeCountRepository의 increment()로 좋아요 수 증가 +6. 성공 응답 + +**고려사항:** + +- 멱등성 보장: 중복 등록 시도에도 오류 없이 성공 응답 +- 좋아요 수 증가는 Repository 레벨에서 원자적으로 처리 + +--- + +## 3. 주문 생성 및 결제 + +사용자가 상품을 주문하고 결제하는 시나리오입니다. 전액 포인트 결제 여부에 따라 외부 PG 호출 여부가 결정됩니다. + +![img_2.png](https://i.imgur.com/EM0MjPP.png) + +**주요 흐름:** + +1. 재고를 비관적 락으로 조회하여 차감 (StockManager) +2. 주문을 접수 상태(PLACED)로 생성 (Order.place) +3. 포인트를 차감하고 이력 기록 (PointAccountManager) +4. **결제 처리 (PaymentManager.pay)** + - Payment 생성 (READY 상태) + - 결제 처리 필요 여부 판단 (Payment.needsExternalPayment()) + - 외부 결제 필요: PG 시스템에 결제 요청 전송 + - 전액 포인트: 즉시 결제 완료 처리 (PAID 상태) +5. 성공 시 트랜잭션 커밋, 실패 시 자동 롤백 + +**고려사항:** + +- 재고 차감은 비관적 락을 통한 동시성 제어 +- 전액 포인트 결제 여부는 Payment 도메인 객체가 판단 (actualPaymentAmount == 0) +- 재고 부족, 포인트 부족, PG 실패 시 트랜잭션 롤백으로 일관성 보장 diff --git a/docs/week2/03-class-diagram.md b/docs/week2/03-class-diagram.md new file mode 100644 index 0000000..cbad680 --- /dev/null +++ b/docs/week2/03-class-diagram.md @@ -0,0 +1,78 @@ +# 클래스 다이어그램 (Class Diagram) + +본 문서는 감성 이커머스의 도메인 모델 구조를 시각화합니다. + +--- + +## 1. Products 도메인 + +![img_3.png](https://i.imgur.com/Xv2rXLF.png) + +**주요 구조:** + +- **Brand**: 브랜드 정보 엔티티 +- **Product**: 상품 엔티티. 상태 관리 행위(품절/재판매/단종) 포함 +- **Stock**: 재고 엔티티. 차감/증가/소진 확인 행위 포함 +- **StockManager**: 재고 차감 시 Stock과 Product를 함께 조율하는 도메인 서비스 +- **Money**: 금액 값 객체. 금액 계산 로직 캡슐화 +- **ProductStatus**: 상품 상태 열거형 + +**설계 의도:** + +- Product와 Stock은 각각 독립적인 애그리게이트 루트 +- StockManager가 두 애그리게이트를 조율하여 비즈니스 로직 수행 + +--- + +## 2. Likes 도메인 + +![img_6.png](https://i.imgur.com/5h67fSJ.png) + +**주요 구조:** + +- **ProductLike**: 사용자의 상품 좋아요 기록 엔티티 + +**설계 의도:** + +- 단순한 관계 엔티티로 별도의 비즈니스 로직 없음 + +--- + +## 3. Orders 도메인 + +![img_5.png](https://i.imgur.com/4XGVtOe.png) + +**주요 구조:** + +- **Order**: 주문 엔티티. 주문 생성(place) 및 결제 완료(pay) 행위 포함 +- **OrderItem**: 주문 항목 엔티티. 주문 시점의 상품 정보 스냅샷 +- **Payment**: 결제 엔티티. 결제 생성, 외부 결제 필요 여부 판단, 완료 처리 행위 포함 +- **PaymentManager**: 결제 생성 및 PG 호출 조율을 담당하는 도메인 서비스 +- **OrderStatus**: 주문 상태 열거형 +- **PaymentStatus**: 결제 상태 열거형 + +**설계 의도:** + +- Order와 OrderItem은 하나의 애그리게이트 (강한 일관성) +- Payment는 독립적인 애그리게이트 +- PaymentManager가 결제 프로세스 전체를 조율 + +--- + +## 4. Points 도메인 + +![point](https://i.imgur.com/y2eAyVc.png) + +**주요 구조:** + +- **PointAccount**: 포인트 잔액 관리 엔티티. 증가/감소 행위 포함 +- **PointHistory**: 포인트 거래 이력 엔티티. referenceId로 충전/결제 추적 +- **PointAccountManager**: 포인트 충전(charge) 및 결제(pay) 시 PointAccount와 PointHistory를 함께 조율하는 도메인 서비스 +- **PointType**: 포인트 거래 타입 열거형 + +**설계 의도:** + +- PointAccount와 PointHistory는 각각 독립적인 애그리게이트 +- PointAccountManager가 두 애그리게이트를 조율하여 일관성 보장 +- PointHistory의 referenceId로 충전ID나 결제ID 추적 가능 +- PointHistory는 생성 후 수정 불가 (감사 추적) \ No newline at end of file diff --git a/docs/week2/04-erd.md b/docs/week2/04-erd.md new file mode 100644 index 0000000..6744bc7 --- /dev/null +++ b/docs/week2/04-erd.md @@ -0,0 +1,275 @@ +# ERD (Entity Relationship Diagram) + +본 문서는 감성 이커머스의 데이터베이스 구조를 정의합니다. + +--- + +## 1. ERD 다이어그램 + +![img_4.png](https://i.imgur.com/TTCD1uh.png) + +**관계 표기:** + +- **실선 (||--|{)**: FK 제약이 있는 관계 (Order ↔ OrderItem) +- **점선 (||..o{)**: ID로만 참조하는 관계 (FK 제약 없음) + +--- + +## 2. 테이블 상세 정의 + +### 2.1 Products 도메인 + +#### brands (브랜드) + +| 컬럼명 | 타입 | 제약 | 설명 | +|------------|--------------|--------------------|---------------------| +| brand_id | BIGINT | PK, AUTO_INCREMENT | 브랜드 ID | +| name | VARCHAR(100) | NOT NULL, UNIQUE | 브랜드 명 | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: brand_id +- UK: name + +--- + +#### products (상품) + +| 컬럼명 | 타입 | 제약 | 설명 | +|------------|---------------|--------------------|--------------------------------------------| +| product_id | BIGINT | PK, AUTO_INCREMENT | 상품 ID | +| brand_id | BIGINT | NOT NULL | 브랜드 ID (참조) | +| name | VARCHAR(200) | NOT NULL | 상품명 | +| price | DECIMAL(15,2) | NOT NULL | 가격 | +| status | VARCHAR(20) | NOT NULL | 상태 (AVAILABLE, OUT_OF_STOCK, DISCONTINUED) | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: product_id +- IDX: brand_id (브랜드별 상품 조회용) +- IDX: created_at (정렬용) + +--- + +#### stocks (재고) + +| 컬럼명 | 타입 | 제약 | 설명 | +|------------|-----------|--------------------|---------------------| +| stock_id | BIGINT | PK, AUTO_INCREMENT | 재고 ID | +| product_id | BIGINT | NOT NULL, UNIQUE | 상품 ID (참조) | +| quantity | INT | NOT NULL | 재고 수량 | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: stock_id +- UK: product_id + +**제약:** + +- quantity >= 0 (CHECK) + +--- + +### 2.2 Likes 도메인 + +#### product_likes (상품 좋아요) + +| 컬럼명 | 타입 | 제약 | 설명 | +|-----------------|-----------|--------------------|---------------------| +| product_like_id | BIGINT | PK, AUTO_INCREMENT | 좋아요 ID | +| user_id | BIGINT | NOT NULL | 사용자 ID (참조) | +| product_id | BIGINT | NOT NULL | 상품 ID (참조) | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: product_like_id +- UK: (user_id, product_id) +- IDX: user_id (내가 좋아요한 목록 조회용) +- IDX: product_id (상품별 좋아요 조회용) + +**특이사항:** + +- 좋아요 취소에 대해 하드 딜리트로 처리 + +--- + +#### product_like_counts (상품 좋아요 수) + +| 컬럼명 | 타입 | 제약 | 설명 | +|-----------------------|-----------|--------------------|---------------------| +| product_like_count_id | BIGINT | PK, AUTO_INCREMENT | 좋아요 수 ID | +| product_id | BIGINT | NOT NULL, UNIQUE | 상품 ID (참조) | +| count | INT | NOT NULL | 좋아요 수 | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: product_like_count_id +- UK: product_id +- IDX: (count DESC) + +**제약:** + +- count >= 0 (CHECK) + +--- + +### 2.3 Orders 도메인 + +#### orders (주문) + +| 컬럼명 | 타입 | 제약 | 설명 | +|--------------|---------------|--------------------|----------------------| +| order_id | BIGINT | PK, AUTO_INCREMENT | 주문 ID | +| user_id | BIGINT | NOT NULL | 사용자 ID (참조) | +| total_amount | DECIMAL(15,2) | NOT NULL | 총 주문 금액 | +| status | VARCHAR(20) | NOT NULL | 주문 상태 (PLACED, PAID) | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: order_id +- IDX: user_id (사용자별 주문 목록 조회용) +- IDX: created_at (최신 주문 조회용) + +--- + +#### order_items (주문 항목) + +| 컬럼명 | 타입 | 제약 | 설명 | +|---------------|---------------|--------------------|----------------------------| +| order_item_id | BIGINT | PK, AUTO_INCREMENT | 주문 항목 ID | +| order_id | BIGINT | NOT NULL, FK | 주문 ID | +| product_id | BIGINT | NOT NULL | 상품 ID (참조) | +| product_name | VARCHAR(200) | NOT NULL | 상품명 (스냅샷) | +| unit_price | DECIMAL(15,2) | NOT NULL | 단가 (스냅샷) | +| quantity | INT | NOT NULL | 수량 | +| subtotal | DECIMAL(15,2) | NOT NULL | 소계 (unit_price × quantity) | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: order_item_id +- FK: order_id → orders(order_id) ON DELETE CASCADE + +--- + +#### payments (결제) + +| 컬럼명 | 타입 | 제약 | 설명 | +|----------------------|---------------|--------------------|---------------------| +| payment_id | BIGINT | PK, AUTO_INCREMENT | 결제 ID | +| order_id | BIGINT | NOT NULL, UNIQUE | 주문 ID (참조) | +| user_id | BIGINT | NOT NULL | 사용자 ID (참조) | +| total_amount | DECIMAL(15,2) | NOT NULL | 총 주문 금액 | +| used_point | DECIMAL(15,2) | NOT NULL | 사용 포인트 | +| paid_amount | DECIMAL(15,2) | NOT NULL | 실제 결제 금액 | +| status | VARCHAR(20) | NOT NULL | 결제 상태 (READY, PAID) | +| external_payment_key | VARCHAR(200) | NULL | PG사 결제 키 | +| approve_code | VARCHAR(50) | NULL | 카드 승인번호 | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: payment_id +- UK: order_id +- IDX: user_id (사용자별 결제 이력 조회용) + +--- + +### 2.4 Points 도메인 + +#### point_accounts (포인트 계좌) + +| 컬럼명 | 타입 | 제약 | 설명 | +|------------------|---------------|--------------------|---------------------| +| point_account_id | BIGINT | PK, AUTO_INCREMENT | 포인트 계좌 ID | +| user_id | BIGINT | NOT NULL, UNIQUE | 사용자 ID (참조) | +| balance | DECIMAL(15,2) | NOT NULL | 포인트 잔액 | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: point_account_id +- UK: user_id + +**제약:** + +- balance >= 0 (CHECK) + +--- + +#### point_histories (포인트 이력) + +| 컬럼명 | 타입 | 제약 | 설명 | +|---------------|---------------|--------------------|--------------------------| +| history_id | BIGINT | PK, AUTO_INCREMENT | 포인트 이력 ID | +| user_id | BIGINT | NOT NULL | 사용자 ID (참조) | +| reference_id | VARCHAR(100) | NULL | 참조 ID (충전ID, 결제ID 등) | +| type | VARCHAR(20) | NOT NULL | 포인트 타입 (CHARGE, PAYMENT) | +| amount | DECIMAL(15,2) | NOT NULL | 거래 금액 | +| balance_after | DECIMAL(15,2) | NOT NULL | 거래 후 잔액 | +| created_at | TIMESTAMP | NOT NULL | 생성 시각 | +| updated_at | TIMESTAMP | NOT NULL | 수정 시각 | +| deleted_at | TIMESTAMP | NULL | 삭제 시각 (Soft Delete) | + +**인덱스:** + +- PK: history_id +- IDX: user_id,created_at (사용자별 이력 조회용) + +--- + +## 3. 설계 고려사항 + +### 3.1 동시성 제어 + +- **stocks 테이블**: 재고 차감 시 비관적 락(`SELECT ... FOR UPDATE`) 사용 + +### 3.2 데이터 정합성 + +- **order_items**: 주문 시점의 상품 정보를 스냅샷으로 저장 (가격 변동 영향 없음) +- **payments**: total_amount = used_point + paid_amount 제약으로 정합성 보장 + +### 3.3 FK 제약 전략 + +- **Order-OrderItem**: 같은 애그리게이트이므로 FK 유지 (ON DELETE CASCADE) +- **나머지**: 애그리게이트 간 느슨한 결합을 위해 FK 제거, 애플리케이션 레벨에서 참조 무결성 관리 + +### 3.4 Soft Delete + +- 모든 테이블에 deleted_at 컬럼 적용 +- 실제 데이터 삭제 대신 deleted_at에 타임스탬프 기록 +- 조회 시 deleted_at IS NULL 조건 추가 + +### 3.5 성능 최적화 + +- **product_like_counts**: 좋아요 수 집계 테이블로 조회 성능 향상 +- **인덱스**: 자주 조회되는 컬럼에 인덱스 설정 (created_at, user_id) + +### 3.6 확장성 + +- 모든 ID는 BIGINT 사용으로 대용량 데이터 대비 +- DECIMAL(15,2)로 최대 9,999,999,999,999.99까지 금액 처리 가능