Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions .docs/design/01-requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# **요구사항 명세서**

## **1. 상품 목록 조회**

### 1.1. 유저 시나리오
> "사용자가 상품을 둘러보기 위해 사이트에 접속했다. 그는 어떤 제품이 인기가 많은지 보기 위해 상품 목록을 조회한다."

### 1.2. 핵심 기능 정의
- **F-1:** 상품 목록을 조회할 수 있다.
- **F-2:** 상품 목록을 '좋아요순'으로 정렬할 수 있다.
- **F-3:** 상품 목록을 '최신순', '가격순' 등 다른 기준으로도 정렬할 수 있다.
- **F-4:** 상품 목록을 페이지 단위로 나누어 볼 수 있다.

### 1.3. 유스케이스 흐름
* **Main Flow (기본 조회):**
1. 사용자가 상품 목록 페이지에 진입한다.
2. 최신순으로 첫 번째 페이지의 상품 목록을 반환한다.
3. 화면에 상품 목록이 표시된다.
* **Alternate Flow (정렬 변경):**
1. 사용자가 '인기순' 정렬 버튼을 클릭한다.
2. 인기순으로 상품 목록을 다시 조회하여 반환한다.
3. 화면에 인기순으로 정렬된 목록이 표시된다.
* **Alternate Flow (페이징):**
1. 사용자가 다른 페이지를 클릭한다.
2. 다른 페이지의 상품 목록을 조회하여 반환한다.
3. 화면에 다음 상품 목록이 이어서 표시된다.
* **Exception Flow (결과 없음):**
1. 사용자가 특정 필터를 적용했으나, 해당하는 상품이 하나도 없다.
2. "조회된 상품이 없습니다."라는 메시지를 반환한다.

---

## **2. 상품 상세 조회**

### 2.1. 유저 시나리오
> "사용자가 마음에 드는 상품이 있어서 해당 상품을 클릭했다. 그는 해당 상품의 상세 정보를 확인하고 싶어 한다."

### 2.2. 핵심 기능 정의
- **F-1:** 상품 상세정보를 조회할 수 있다.
- **F-2:** 상품의 총 좋아요 수와 나의 좋아요 여부를 조회할 수 있다.
- **F-3:** 구매할 상품의 수량을 선택할 수 있다.

### 2.3. 유스케이스 흐름
* **Main Flow (상세 조회):**
1. 사용자가 상품 목록에서 특정 상품을 클릭한다.
2. 해당 상품의 상세 정보(이름, 가격, 설명, 총 좋아요 수, 나의 좋아요 여부 등)를 조회하여 반환한다.
3. 화면에 상품 상세 정보가 표시되고, 수량은 '1'로 기본 설정된다.
* **Alternate Flow (수량 변경):**
1. 사용자가 '+' 버튼을 눌러 수량을 '3'으로 변경한다.
2. 화면에 수량이 '3'으로 갱신된다.
* **Exception Flow (상품 없음):**
1. 사용자가 존재하지 않는 상품 ID의 URL로 직접 접근한다.
2. 상품을 찾을 수 없으므로, "상품을 찾을 수 없습니다."라는 오류를 반환한다.

---

## **3. 브랜드별 상품 조회**

### 3.1. 유저 시나리오
> "사용자가 마음에 드는 특정 브랜드를 찾고 있다. 그는 해당 브랜드에서 나오는 상품만 모아서 조회하고 싶어 한다."

### 3.2. 핵심 기능 정의
- **F-1:** 특정 브랜드에 속한 상품만 필터링하여 조회할 수 있다.
- **F-2:** 상품 목록을 '인기순'으로 정렬할 수 있다.
- **F-3:** 상품 목록을 '최신순', '가격순' 등 다른 기준으로도 정렬할 수 있다.
- **F-4:** 상품 목록을 페이지 단위로 나누어 볼 수 있다.

### 3.3. 유스케이스 흐름
* **Main Flow (브랜드별 조회):**
1. 사용자가 'A 브랜드'를 조회한다.
2. 해당 브랜드 상품 목록을 반환한다.
3. 화면에 'A 브랜드'의 상품 목록만 표시된다.
* **Exception Flow (브랜드 없음):**
1. 사용자가 존재하지 않는 브랜드 ID의 URL로 직접 접근한다.
2. "브랜드를 찾을 수 없습니다."라는 오류(404 Not Found)를 반환한다.

---

## **4. 상품 좋아요 (등록/취소)**

### 4.1. 유저 시나리오
> (등록) "사용자가 마음에 드는 상품을 발견했다. 그는 나중에 마음에 드는 상품을 모아보기 위해 '좋아요'를 누른다."
> (취소) "사용자가 이전에 '좋아요' 했던 상품이 더 이상 마음에 들지 않아 '좋아요'를 취소한다."

### 4.2. 핵심 기능 정의
- **F-1:** 상품에 대해 '좋아요'를 등록할 수 있다.
- **F-2:** 이미 '좋아요'가 등록된 상품의 '좋아요'를 취소할 수 있다.

### 4.3. 유스케이스 흐름
* **Main Flow (좋아요 등록):**
1. 사용자가 좋아요 아이콘을 클릭한다.
2. 좋아요 여부를 판단한다.
3. 해당 상품이 좋아요가 안되어있으면 좋아요 등록이 된다.
* **Alternate Flow (좋아요 취소):**
1. 사용자가 좋아요 아이콘을 클릭한다.
2. 좋아요 여부를 판단한다.
3. 해당 상품이 좋아요가 되어있으면 좋아요가 취소된다.
* **Exception Flow (비회원):**
1. 비로그인 사용자가 좋아요 아이콘을 클릭한다.
2. "로그인이 필요한 기능입니다."라는 오류 메시지를 반환한다.

---

## **5. 주문 생성 (결제)**

### 5.1. 유저 시나리오
> "사용자가 특정 상품을 구매하고 싶어, 해당 상품을 결제한다."

### 5.2. 핵심 기능 정의
- **F-1:** 사용자가 선택한 상품을 구매할 수 있다.
- **F-2:** 사용자의 포인트가 충분할 경우 결제할 수 있으며, 결제 시 포인트가 차감된다.
- **F-3:** 상품의 재고가 충분할 경우 결제할 수 있으며, 결제 시 재고가 차감된다.
- **F-4:** 결제 성공 시 주문 정보가 외부 시스템으로 전송된다.

### 5.3. 유스케이스 흐름
* **Main Flow (주문 성공):**
1. 로그인한 사용자가 상품과 수량을 선택하고 '결제하기'를 요청한다.
2. 상품 재고가 1개 이상인지 확인한다.
3. 사용자 포인트가 총 결제 금액 이상인지 확인한다.
4. 상품 재고를 1 차감한다.
5. 사용자 포인트를 차감한다.
6. '주문' 및 '주문 항목'을 저장한다.
7. 주문 정보를 외부 시스템으로 전송한다.
8. 사용자에게 "주문 완료" 응답을 반환한다.
* **Alternate Flow (여러 수량 구매):**
1. 사용자가 수량을 '3'으로 선택하고 '결제하기'를 요청한다.
2. 재고 및 포인트를 수량 기준으로 검증, 차감, 저장한다.
3. 주문에 성공한다.
* **Exception Flow (재고 부족):**
1. 시스템이 재고를 확인했으나, 요청 수량보다 재고가 부족하다.
2. "재고가 부족하여 주문할 수 없습니다."라는 오류 메시지를 반환한다.
* **Exception Flow (포인트 부족):**
1. 시스템이 사용자의 포인트를 확인했으나, 총 결제 금액보다 포인트가 부족하다.
2. "포인트가 부족합니다."라는 오류 메시지를 반환한다.
* **Exception Flow (비회원):**
1. 비로그인 사용자가 '결제하기'를 요청한다.
2. "로그인이 필요한 기능입니다."라는 오류 메시지를 반환한다.
67 changes: 67 additions & 0 deletions .docs/design/02-sequence-diagrams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

### 1. 상품 목록/상세 조회 및 브랜드 조회
```mermaid
sequenceDiagram
participant User
participant ProductController
participant ProductReader

%% 상품 목록 조회 %%
User->>ProductController: GET /api/v1/products?sort=likes_desc
ProductController->>ProductReader: getProducts(sort)
ProductReader-->>ProductController: productList
ProductController-->>User: productList

%% 상품 상세 조회 %%
User->>ProductController: GET /api/v1/products/{productId}
ProductController->>ProductReader: getProduct(productId)
ProductReader-->>ProductController: product
ProductController-->>User: product

participant BrandController
participant BrandReader

%% 브랜드 조회 %%
User->>BrandController: GET /api/v1/brands/{brandId}
BrandController->>BrandReader: getBrand({brandId})
BrandReader-->>BrandController: brand
BrandController-->>User: brand
```
### 2. 주문 생성

```mermaid
sequenceDiagram
participant User
participant OrderController
participant OrderService
participant ProductReader
participant PointReader
participant ProductService
participant PointService
participant OrderRepository

User->>OrderController: POST /api/v1/orders (body: {productId, quantity})
OrderController->>OrderService: createOrder(userId, {productId, quantity})

%% 조회 및 검증 (서버가 가격/포인트 확인) %%
OrderService->>ProductReader: getProduct({productId})
ProductReader -->>OrderService: product(현재가격, 재고)

OrderService->>PointReader: getPoint(userId)
PointReader-->>OrderService: point (현재 잔여 포인트)

%% 서버가 totalPrice를 직접 계산하는 로직 명시 %%
Note right of OrderService: 3. 서버가 totalPrice를 직접 계산<br/>(product.getPrice() * quantity)

%% 재고 및 포인트 차감 (계산된 totalPrice 사용) %%
critical Transaction Block
OrderService ->>ProductService: decreaseStock(productId, quantity)
OrderService ->>PointService: deductPoint(calculatedTotalPrice)
OrderService->> OrderRepository: save(new Order(..., calculatedTotalPrice))
OrderRepository-->>OrderService: orderInfo
end

%% 응답 %%
OrderService -->> OrderController:orderInfo
OrderController-->> User:orderInfo
```
50 changes: 50 additions & 0 deletions .docs/design/03-class-diagram.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
classDiagram
class User {
Long id
String name
int point
}
class Product {
Long id
String name
Price price %% int -> Price (VO)
Quantity quantity %% int -> Quantity (VO)
}
class Brand {
Long id
String name
}
class Like {
User user
Product product
%% boolean liked 제거
}
class Order {
Long id
User user
int totalPrice
Timestamp created_at
}
class OrderItem {
Order order
Product product
int quantity
int orderPrice
}

%% --- VO 정의 ---
class Price { <<VO>> }
class Quantity { <<VO>> }

%% --- 관계 정의 ---
Product --> Brand : (상품은 브랜드를 가짐)
Product --> Price
Product --> Quantity

Order --> User : (주문은 유저를 가짐)

OrderItem --> Order : (주문 항목은 주문에 속함)
OrderItem --> Product : (주문 항목은 상품을 가짐)

Like --> User : (좋아요는 유저를 가짐)
Like --> Product : (좋아요는 상품을 가짐)
47 changes: 47 additions & 0 deletions .docs/design/04-erd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
erDiagram
users {
%% BaseEntity가 id, created_at 등을 제공합니다.
varchar name
}
points {
bigint id PK
bigint user_id FK
int point
}
products {
%% BaseEntity가 id, created_at 등을 제공합니다.
varchar name
int price
int stock_quantity
bigint brand_id FK
}
brands {
%% BaseEntity가 id, created_at 등을 제공합니다.
varchar name
}
likes {
bigint user_id PK, FK
bigint product_id PK, FK
}
orders {
%% BaseEntity가 id, created_at 등을 제공합니다.
bigint user_id FK
int total_price
%% Timestamp created_at 제거
}
orderitems {
%% BaseEntity가 id, created_at 등을 제공합니다.
bigint order_id FK
bigint product_id FK
int quantity
int order_price
}

%% --- 관계 정의 (1:N) ---
users ||--o{ likes : "likes"
users ||--o{ orders : "places"
users ||--o{ points : "has"
products ||--o{ likes : "is_liked"
products ||--o{ orderitems : "is_in"
brands ||--o{ products : "has"
orders ||--o{ orderitems : "contains"
Loading