From 06d6bd1692357e29caf28d2d5ebe37b8989b7867 Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Thu, 13 Nov 2025 20:17:45 +0900
Subject: [PATCH 01/11] feat: domain modeling

---
 .../application/brand/BrandFacade.java        |  18 +++
 .../loopers/application/brand/BrandInfo.java  |  16 ++
 .../loopers/application/like/LikeInfo.java    |  10 ++
 .../application/order/CreateOrderCommand.java |  21 +++
 .../application/order/OrderFacade.java        |  70 ++++++++
 .../loopers/application/order/OrderInfo.java  |  28 ++++
 .../application/order/OrderItemInfo.java      |  24 +++
 .../product/ProductDetailInfo.java            |  24 +++
 .../application/product/ProductFacade.java    |  29 ++++
 .../java/com/loopers/domain/brand/Brand.java  |  32 ++++
 .../loopers/domain/brand/BrandRepository.java |  10 ++
 .../loopers/domain/brand/BrandService.java    |  27 ++++
 .../java/com/loopers/domain/like/Like.java    |  43 +++++
 .../loopers/domain/like/LikeRepository.java   |  21 +++
 .../com/loopers/domain/like/LikeService.java  |  74 +++++++++
 .../java/com/loopers/domain/order/Order.java  |  83 ++++++++++
 .../com/loopers/domain/order/OrderItem.java   |  42 +++++
 .../loopers/domain/order/OrderRepository.java |  15 ++
 .../loopers/domain/order/OrderService.java    |  66 ++++++++
 .../com/loopers/domain/order/OrderStatus.java |  19 +++
 .../loopers/domain/point/PointService.java    |  11 ++
 .../com/loopers/domain/product/Product.java   |  60 +++++++
 .../domain/product/ProductRepository.java     |  23 +++
 .../domain/product/ProductService.java        |  92 +++++++++++
 .../domain/product/ProductStockService.java   |  25 +++
 .../loopers/domain/user/UserRepository.java   |   2 +-
 .../com/loopers/domain/user/UserService.java  |   6 +-
 .../brand/BrandJpaRepository.java             |  12 ++
 .../brand/BrandRepositoryImpl.java            |  24 +++
 .../like/LikeJpaRepository.java               |  29 ++++
 .../like/LikeRepositoryImpl.java              |  47 ++++++
 .../order/OrderJpaRepository.java             |  15 ++
 .../order/OrderRepositoryImpl.java            |  32 ++++
 .../product/ProductJpaRepository.java         |  33 ++++
 .../product/ProductRepositoryImpl.java        |  50 ++++++
 .../user/UserJpaRepository.java               |   3 +-
 .../user/UserRepositoryImpl.java              |   4 +-
 .../api/order/OrderCreateV1Dto.java           |  37 +++++
 .../interfaces/api/order/OrderV1ApiSpec.java  |  55 +++++++
 .../api/order/OrderV1Controller.java          |  63 ++++++++
 .../product/ProductFacadeIntegrationTest.java | 122 ++++++++++++++
 .../loopers/domain/brand/BrandAssertions.java |  15 ++
 .../loopers/domain/brand/BrandModelTest.java  |  49 ++++++
 .../brand/BrandServiceIntegrationTest.java    |  76 +++++++++
 .../domain/like/FakeLikeRepository.java       |  93 +++++++++++
 .../loopers/domain/like/LikeAssertions.java   |  16 ++
 .../loopers/domain/like/LikeModelTest.java    |  69 ++++++++
 .../like/LikeServiceIntegrationTest.java      | 153 ++++++++++++++++++
 .../loopers/domain/like/LikeServiceTest.java  | 150 +++++++++++++++++
 .../loopers/domain/order/OrderAssertions.java |  16 ++
 .../loopers/domain/order/OrderModelTest.java  | 103 ++++++++++++
 .../order/OrderServiceIntegrationTest.java    | 120 ++++++++++++++
 .../domain/product/ProductAssertions.java     |  18 +++
 .../domain/product/ProductModelTest.java      |  93 +++++++++++
 .../ProductServiceIntegrationTest.java        | 118 ++++++++++++++
 55 files changed, 2499 insertions(+), 7 deletions(-)
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/like/LikeInfo.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/order/OrderItemInfo.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandAssertions.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/like/LikeAssertions.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/like/LikeModelTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/product/ProductAssertions.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java

diff --git a/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
new file mode 100644
index 000000000..c0e4d5da9
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
@@ -0,0 +1,18 @@
+package com.loopers.application.brand;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class BrandFacade {
+  private final BrandService brandService;
+
+  public BrandInfo getBrandDetail(long id) {
+    Brand brand = brandService.getBrand(id);
+    return BrandInfo.from(brand);
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
new file mode 100644
index 000000000..5a2ae251c
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
@@ -0,0 +1,16 @@
+package com.loopers.application.brand;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+
+public record BrandInfo(Long id, String name, String story) {
+  public static BrandInfo from(Brand model) {
+    if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "유저정보를 찾을수 없습니다.");
+    return new BrandInfo(
+        model.getId(),
+        model.getName(),
+        model.getStory()
+    );
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/like/LikeInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/like/LikeInfo.java
new file mode 100644
index 000000000..1685623a2
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/like/LikeInfo.java
@@ -0,0 +1,10 @@
+package com.loopers.application.like;
+
+public record LikeInfo(long likeCount, boolean isLiked) {
+  public static LikeInfo from(long likeCount, boolean isLiked) {
+    return new LikeInfo(
+        likeCount,
+        isLiked
+    );
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java b/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java
new file mode 100644
index 000000000..76f9c2c74
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java
@@ -0,0 +1,21 @@
+package com.loopers.application.order;
+
+import com.loopers.domain.order.Order;
+import com.loopers.interfaces.api.order.OrderCreateV1Dto;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+
+import java.math.BigDecimal;
+import java.time.ZonedDateTime;
+import java.util.List;
+
+public record CreateOrderCommand(Long userId, List<ItemCommand> orderItemInfo) {
+  public record ItemCommand(Long productId, Long quantity) {
+  }
+
+  public static CreateOrderCommand from(Long userId, OrderCreateV1Dto.OrderRequest request) {
+    return new CreateOrderCommand(
+        userId,
+        request.items().stream().map(i -> new ItemCommand(i.productId(), i.quantity())).toList());
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
new file mode 100644
index 000000000..9aa8f0570
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
@@ -0,0 +1,70 @@
+package com.loopers.application.order;
+
+import com.loopers.domain.order.Order;
+import com.loopers.domain.order.OrderItem;
+import com.loopers.domain.order.OrderService;
+import com.loopers.domain.order.OrderStatus;
+import com.loopers.domain.point.PointService;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.product.ProductStockService;
+import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.UserService;
+import jakarta.transaction.Transactional;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.time.ZonedDateTime;
+import java.util.List;
+import java.util.Map;
+
+@RequiredArgsConstructor
+@Component
+public class OrderFacade {
+  private final UserService userService;
+  private final PointService pointService;
+  private final ProductService productService;
+  private final OrderService orderService;
+
+  private final ProductStockService productStockService;
+
+  public Page<Order> getOrderList(Long userId,
+                                  String sortType,
+                                  int page,
+                                  int size) {
+    return orderService.getOrders(userId, sortType, page, size);
+  }
+
+  public OrderInfo getOrderDetail(Long orderId) {
+    Order order = orderService.getOrder(orderId);
+    return OrderInfo.from(order);
+  }
+
+  @Transactional
+  public OrderInfo createOrder(Long userId, Map<Long, Long> productQuantityMap) {
+    //사용자 존재 확인
+    UserModel user = userService.getUser(userId);
+
+    //상품 존재 확인
+    List<Product> productList = productService.getExistingProducts(productQuantityMap.keySet());
+
+    //재고확인및 차감
+    List<Product> deductedProducts = productStockService.deductStock(productList, productQuantityMap);
+    productService.save(deductedProducts);
+
+    //포인트 확인 및 차감
+    BigDecimal totalPrice = deductedProducts.stream()
+        .map(Product::getPrice)
+        .reduce(BigDecimal.ZERO, BigDecimal::add);
+    pointService.use(user, totalPrice);
+
+    //주문 생성
+    Order order = Order.create(userId, OrderStatus.PAID, totalPrice, totalPrice, ZonedDateTime.now());
+    order.setOrderItems(deductedProducts.stream().map(i -> OrderItem.create(i.getId(), productQuantityMap.get(i.getId()), i.getPrice())).toList());
+    Order savedOrder = orderService.save(order);
+
+    return OrderInfo.from(savedOrder);
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
new file mode 100644
index 000000000..d83bb2494
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
@@ -0,0 +1,28 @@
+package com.loopers.application.order;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.loopers.application.brand.BrandInfo;
+import com.loopers.domain.order.Order;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+
+import java.math.BigDecimal;
+import java.time.ZonedDateTime;
+import java.util.List;
+
+public record OrderInfo(long id, String status, BigDecimal paymentPrice, BigDecimal totalPrice
+    , ZonedDateTime orderAt, List<OrderItemInfo> orderItemInfo) {
+  public static OrderInfo from(Order model) {
+    if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "주문정보를 찾을수 없습니다.");
+    return new OrderInfo(
+        model.getId(),
+        model.getStatus().name(),
+        model.getPaymentPrice(),
+        model.getTotalPrice(),
+        model.getOrderAt(),
+        OrderItemInfo.from(model.getOrderItems())
+    );
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderItemInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderItemInfo.java
new file mode 100644
index 000000000..92a66aa2f
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderItemInfo.java
@@ -0,0 +1,24 @@
+package com.loopers.application.order;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.loopers.domain.order.OrderItem;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public record OrderItemInfo(Long id, long quantity
+    , @JsonSerialize(using = ToStringSerializer.class) BigDecimal unitPrice,
+                            @JsonSerialize(using = ToStringSerializer.class) BigDecimal totalPrice) {
+  public static List<OrderItemInfo> from(List<OrderItem> model) {
+    if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "주문상세정보를 찾을수 없습니다.");
+    return model.stream().map(item -> new OrderItemInfo(
+        item.getId(),
+        item.getQuantity(),
+        item.getUnitPrice(),
+        item.getTotalPrice()
+    )).toList();
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
new file mode 100644
index 000000000..fadaa5cc8
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
@@ -0,0 +1,24 @@
+package com.loopers.application.product;
+
+import com.loopers.application.brand.BrandInfo;
+import com.loopers.application.like.LikeInfo;
+import com.loopers.domain.product.Product;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+
+import java.math.BigDecimal;
+
+public record ProductDetailInfo(Long id, String name, BigDecimal price, long stock
+    , BrandInfo brandInfo, LikeInfo likeInfo) {
+  public static ProductDetailInfo from(Product model, boolean isLiked) {
+    if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "상품정보를 찾을수 없습니다.");
+    return new ProductDetailInfo(
+        model.getId(),
+        model.getName(),
+        model.getPrice(),
+        model.getStock(),
+        BrandInfo.from(model.getBrand()),
+        LikeInfo.from(model.getLikeCount(), isLiked)
+    );
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
new file mode 100644
index 000000000..b19585419
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
@@ -0,0 +1,29 @@
+package com.loopers.application.product;
+
+import com.loopers.domain.like.LikeService;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class ProductFacade {
+  private final ProductService productService;
+  private final LikeService likeService;
+
+  public Page<Product> getProductList(Long brandId,
+                                      String sortType,
+                                      int page,
+                                      int size) {
+    return productService.getProducts(brandId, sortType, page, size);
+  }
+
+  public ProductDetailInfo getProductDetail(long userId, long productId) {
+    Product product = productService.getProduct(productId);
+    boolean isLiked = likeService.isLiked(userId, productId);
+    return ProductDetailInfo.from(product, isLiked);
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java
new file mode 100644
index 000000000..39067ec5a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java
@@ -0,0 +1,32 @@
+package com.loopers.domain.brand;
+
+import com.loopers.domain.BaseEntity;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Getter;
+
+@Entity
+@Table(name = "brand")
+@Getter
+public class Brand extends BaseEntity {
+  private String name;
+  private String story;
+
+  protected Brand() {
+    this.name = "";
+    this.story = "";
+  }
+
+  private Brand(String name, String story) {
+    this.name = name;
+    this.story = story;
+  }
+
+  public static Brand create(String name, String story) {
+    if (name.isBlank()) {
+      throw new IllegalArgumentException("브랜드명은 비어있을 수 없습니다.");
+    }
+    return new Brand(name, story);
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java
new file mode 100644
index 000000000..ceb5791f4
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java
@@ -0,0 +1,10 @@
+package com.loopers.domain.brand;
+
+import java.util.Optional;
+
+public interface BrandRepository {
+  Optional<Brand> findById(Long id);
+
+  Brand save(Brand brand);
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
new file mode 100644
index 000000000..0e05dead6
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
@@ -0,0 +1,27 @@
+package com.loopers.domain.brand;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@RequiredArgsConstructor
+@Component
+public class BrandService {
+
+  private final BrandRepository brandRepository;
+
+  @Transactional(readOnly = true)
+  public Brand getBrand(Long id) {
+    if (id == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
+    }
+    return brandRepository.findById(id).orElse(null);
+  }
+
+  public Brand save(Brand brand) {
+    return brandRepository.save(brand);
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
new file mode 100644
index 000000000..425316747
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
@@ -0,0 +1,43 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.UserModel;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.*;
+import lombok.Getter;
+
+@Entity
+@Table(name = "likes", uniqueConstraints = {
+    @UniqueConstraint(columnNames = {"ref_user_id", "ref_product_id"})
+})
+@Getter
+public class Like extends BaseEntity {
+  @ManyToOne(fetch = FetchType.LAZY)
+  @JoinColumn(name = "ref_user_Id", nullable = false)
+  private UserModel user;
+
+  @ManyToOne(fetch = FetchType.LAZY)
+  @JoinColumn(name = "ref_product_Id", nullable = false)
+  private Product product;
+
+  protected Like() {
+  }
+
+  private Like(UserModel user, Product product) {
+    this.user = user;
+    this.product = product;
+  }
+
+  public static Like create(UserModel user, Product product) {
+    if (user == null || user.getId() == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "사용자ID는 비어있을 수 없습니다.");
+    }
+    if (product == null || product.getId() == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "상품ID는 비어있을 수 없습니다.");
+    }
+    return new Like(user, product);
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
new file mode 100644
index 000000000..b0a87fb3a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
@@ -0,0 +1,21 @@
+package com.loopers.domain.like;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.Optional;
+
+public interface LikeRepository {
+
+  Optional<Like> findById(Long userId, Long productid);
+
+  Like save(Like like);
+
+  long remove(Long userId, Long productId);
+
+  boolean isLiked(Long userId, Long productId);
+
+  long getLikeCount(Long productId);
+
+  Page<Like> getLikedProducts(Long userId, Pageable pageable);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
new file mode 100644
index 000000000..01610a251
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
@@ -0,0 +1,74 @@
+
+package com.loopers.domain.like;
+
+import com.loopers.domain.product.Product;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.*;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Component
+public class LikeService {
+
+  private final LikeRepository likeRepository;
+
+  @Transactional
+  public Like save(Like like) {
+    Long userId = like.getUser().getId();
+    Long productId = like.getProduct().getId();
+    Optional<Like> existingLike = likeRepository.findById(userId, productId);
+
+    if (existingLike.isPresent()) {
+      return existingLike.get();
+    }
+    return likeRepository.save(like);
+  }
+
+
+  @Transactional
+  public long remove(Long userId, Long productId) {
+    return likeRepository.remove(userId, productId);
+  }
+
+  @Transactional
+  public boolean isLiked(Long userId, Long productId) {
+    return likeRepository.isLiked(userId, productId);
+  }
+
+  public Page<Product> getLikedProducts(
+      Long userId,
+      String sortType,
+      int page,
+      int size
+  ) {
+    if (userId == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
+    }
+    Sort sort = this.getSortBySortType(sortType);
+    Pageable pageable = PageRequest.of(page, size, sort);
+    Page<Like> likePage = likeRepository.getLikedProducts(userId, pageable);
+    List<Product> likedProducts = likePage.getContent().stream().map(Like::getProduct).toList();
+    return new PageImpl<>(likedProducts, pageable, likePage.getTotalElements());
+  }
+
+  private Sort getSortBySortType(String sortType) {
+    if (sortType == null) sortType = "latest";
+    Sort latestSort = Sort.by("createdAt").descending();
+    switch (sortType.toLowerCase()) {
+      case "latest":
+        return latestSort;
+      case "price_asc":
+        return Sort.by("price").ascending().and(latestSort);
+      case "likes_desc":
+        return Sort.by("likesCount").descending().and(latestSort);
+      default:
+        return latestSort;
+    }
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
new file mode 100644
index 000000000..3b285f582
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
@@ -0,0 +1,83 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.*;
+import lombok.Getter;
+
+import java.math.BigDecimal;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name = "order")
+@Getter
+public class Order extends BaseEntity {
+
+  private Long refUserId;
+
+  private OrderStatus status;
+
+  BigDecimal paymentPrice;
+
+  BigDecimal totalPrice;
+
+  ZonedDateTime orderAt;
+
+  @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
+  @JoinColumn(name = "ref_order_id")
+  private List<OrderItem> orderItems = new ArrayList<>();
+
+  public void setOrderItems(List<OrderItem> orderItems) {
+    if (orderItems == null || orderItems.isEmpty()) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "주문 상세내역이 없습니다.");
+    }
+    this.orderItems = orderItems;
+  }
+
+  protected Order() {
+  }
+
+  private Order(long refUserId, OrderStatus status, BigDecimal paymentPrice, BigDecimal totalPrice, ZonedDateTime orderAt) {
+    this.refUserId = refUserId;
+    this.status = status;
+    this.paymentPrice = paymentPrice;
+    this.totalPrice = totalPrice;
+    this.orderAt = orderAt;
+  }
+
+  public static Order create(long refUserId, OrderStatus status, BigDecimal paymentPrice, BigDecimal totalPrice, ZonedDateTime orderAt) {
+    if (status == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "상태 정보는 비어있을 수 없습니다.");
+    }
+    if (paymentPrice.compareTo(BigDecimal.ZERO) < 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "지불금액은 음수일수 없습니다.");
+    }
+    if (totalPrice.compareTo(BigDecimal.ZERO) < 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "총가격은 음수일수 없습니다.");
+    }
+    return new Order(refUserId, status, paymentPrice, totalPrice, orderAt);
+  }
+
+  public void cancel() {
+    if (status == OrderStatus.CANCELLED) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "이미 취소된 주문입니다.");
+    }
+    this.status = OrderStatus.CANCELLED;
+  }
+
+  public void preparing() {
+    if (status == OrderStatus.PENDING) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "결재대기중 주문입니다.");
+    }
+    if (status == OrderStatus.PREPARING) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "이미 준비중인 주문입니다.");
+    }
+    if (OrderStatus.PAID.getSequence() < status.getSequence()) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "상품준비 완료된 주문입니다.");
+    }
+    this.status = OrderStatus.PREPARING;
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
new file mode 100644
index 000000000..07e51557e
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
@@ -0,0 +1,42 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Getter;
+
+import java.math.BigDecimal;
+
+@Entity
+@Table(name = "orderItem")
+@Getter
+public class OrderItem extends BaseEntity {
+
+  private long refProductId;
+
+  private long quantity;
+
+  private BigDecimal unitPrice;
+
+  private BigDecimal totalPrice;
+
+  protected OrderItem() {
+  }
+
+  private OrderItem(long refProductId, long quantity, BigDecimal unitPrice, BigDecimal totalPrice) {
+    this.refProductId = refProductId;
+    this.quantity = quantity;
+    this.unitPrice = unitPrice;
+    this.totalPrice = totalPrice;
+  }
+
+  public static OrderItem create(long refProductId, long quantity, BigDecimal unitPrice) {
+    if (unitPrice.compareTo(BigDecimal.ZERO) < 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "가격은 음수일수 없습니다.");
+    }
+    BigDecimal totalPrice = unitPrice.multiply(new BigDecimal(quantity));
+    return new OrderItem(refProductId, quantity, unitPrice, totalPrice);
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java
new file mode 100644
index 000000000..c03bd66a8
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java
@@ -0,0 +1,15 @@
+package com.loopers.domain.order;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.Optional;
+
+public interface OrderRepository {
+  Optional<Order> findById(Long id);
+
+  Page<Order> findByUserId(Long userId, Pageable pageable);
+
+  Order save(Order product);
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
new file mode 100644
index 000000000..e02487b7b
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
@@ -0,0 +1,66 @@
+
+package com.loopers.domain.order;
+
+import com.loopers.application.order.CreateOrderCommand;
+import com.loopers.domain.product.ProductRepository;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@RequiredArgsConstructor
+@Component
+public class OrderService {
+  private final ProductRepository productRepository;
+  private final OrderRepository orderRepository;
+
+  public Page<Order> getOrders(
+      Long userId,
+      String sortType,
+      int page,
+      int size
+  ) {
+    Sort sort = this.getSortBySortType(sortType);
+    Pageable pageable = PageRequest.of(page, size, sort);
+    Page<Order> orders = null;
+    if (userId == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "유저 정보가 없습니다.");
+    }
+    orders = orderRepository.findByUserId(userId, pageable);
+    return orders;
+  }
+
+  @Transactional(readOnly = true)
+  public Order getOrder(Long id) {
+    if (id == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
+    }
+    return orderRepository.findById(id).orElse(null);
+  }
+
+  public Order save(Order order) {
+    return orderRepository.save(order);
+  }
+
+  private Sort getSortBySortType(String sortType) {
+    if (sortType == null) sortType = "latest";
+    Sort latestSort = Sort.by("createdAt").descending();
+    switch (sortType.toLowerCase()) {
+      case "latest":
+        return latestSort;
+      case "price":
+        return Sort.by("status").descending();
+      default:
+        return latestSort;
+    }
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
new file mode 100644
index 000000000..b93673616
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
@@ -0,0 +1,19 @@
+package com.loopers.domain.order;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum OrderStatus {
+  PENDING("결제 대기", 10),
+  PAID("결제 완료", 20),
+  PREPARING("상품 준비 중", 30),
+  SHIPPED("배송 중", 40),
+  DELIVERED("배송 완료", 50),
+  CANCELLED("주문 취소", 90),
+  REFUNDED("환불 완료", 100);
+
+  private final String description;
+  private final int sequence;
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
index 9554dcac0..9b953581d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
@@ -34,4 +34,15 @@ public BigDecimal charge(UserModel user, BigDecimal chargeAmt) {
     pointRepository.save(pointOpt.get());
     return getAmount(user.getUserId());
   }
+
+  @Transactional
+  public BigDecimal use(UserModel user, BigDecimal useAmt) {
+    Optional<PointModel> pointOpt = pointRepository.findByUserIdForUpdate(user.getUserId());
+    if (pointOpt.isEmpty()) {
+      throw new CoreException(ErrorType.NOT_FOUND, "현재 포인트 정보를 찾을수 없습니다.");
+    }
+    pointOpt.get().use(useAmt);
+    pointRepository.save(pointOpt.get());
+    return getAmount(user.getUserId());
+  }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
new file mode 100644
index 000000000..f5df0758f
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
@@ -0,0 +1,60 @@
+package com.loopers.domain.product;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.domain.brand.Brand;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.*;
+import lombok.Getter;
+
+import java.math.BigDecimal;
+
+@Entity
+@Table(name = "product")
+@Getter
+public class Product extends BaseEntity {
+  private String name;
+
+  @ManyToOne(fetch = FetchType.LAZY) // 지연 로딩 설정 (성능상 권장)
+  @JoinColumn(name = "ref_brand_Id")
+  private Brand brand;
+
+  private BigDecimal price;
+  private long stock;
+
+  @Transient
+  private long likeCount;
+
+  public void setBrand(Brand brand) {
+    this.brand = brand;
+  }
+
+  public void setLikeCount(long likeCount) {
+    this.likeCount = likeCount;
+  }
+
+  protected Product() {
+    this.stock = 0;
+  }
+
+  private Product(Brand brand, String name, BigDecimal price, long stock) {
+    this.setBrand(brand);
+    this.name = name;
+    this.price = price;
+    this.stock = stock;
+  }
+
+  public static Product create(Brand brand, String name, BigDecimal price, long stock) {
+    return new Product(brand, name, price, stock);
+  }
+
+  public void deductStock(long quantity) {
+    if (quantity <= 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "수량은 0보다 커야 합니다.");
+    }
+    if (this.stock < quantity) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "재고가 부족합니다.");
+    }
+    this.stock -= quantity;
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
new file mode 100644
index 000000000..ae1031751
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
@@ -0,0 +1,23 @@
+package com.loopers.domain.product;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+public interface ProductRepository {
+  Optional<Product> findById(Long id);
+
+  List<Product> findAllById(Set<Long> id);
+
+  Page<Product> findByBrandId(Long brandId, Pageable pageable);
+
+  Page<Product> findAll(Pageable pageable);
+
+  Product save(Product product);
+
+  List<Product> save(List<Product> products);
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
new file mode 100644
index 000000000..9819ad90b
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
@@ -0,0 +1,92 @@
+
+package com.loopers.domain.product;
+
+import com.loopers.application.order.CreateOrderCommand;
+import com.loopers.domain.like.LikeRepository;
+import com.loopers.domain.order.Order;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+@RequiredArgsConstructor
+@Component
+public class ProductService {
+
+  private final ProductRepository productRepository;
+  private final LikeRepository likeRepository;
+
+  public Page<Product> getProducts(
+      Long brandId,
+      String sortType,
+      int page,
+      int size
+  ) {
+    Sort sort = this.getSortBySortType(sortType);
+    Pageable pageable = PageRequest.of(page, size, sort);
+    Page<Product> products = null;
+    if (brandId != null) {
+      products = productRepository.findByBrandId(brandId, pageable);
+    } else {
+      products = productRepository.findAll(pageable);
+    }
+    return products;
+  }
+
+  @Transactional(readOnly = true)
+  public Product getProduct(Long id) {
+    if (id == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
+    }
+    Optional<Product> product = productRepository.findById(id);
+    return product.orElse(null);
+  }
+
+  @Transactional(readOnly = true)
+  public List<Product> getExistingProducts(Set<Long> productIds) {
+
+    if (productIds == null || productIds.isEmpty()) {
+      return Collections.emptyList();
+    }
+    List<Product> products = productRepository.findAllById(productIds);
+
+    if (products.size() != productIds.size()) {
+      throw new CoreException(
+          ErrorType.NOT_FOUND,
+          "다음 상품 ID들은 찾을 수 없습니다: "
+      );
+    }
+    return products;
+  }
+
+  public Product save(Product product) {
+    return productRepository.save(product);
+  }
+
+  public List<Product> save(List<Product> product) {
+    return productRepository.save(product);
+  }
+
+  private Sort getSortBySortType(String sortType) {
+    if (sortType == null) sortType = "latest";
+    Sort latestSort = Sort.by("createdAt").descending();
+    switch (sortType.toLowerCase()) {
+      case "latest":
+        return latestSort;
+      case "price_asc":
+        return Sort.by("price").ascending().and(latestSort);
+      case "likes_desc":
+        return Sort.by("likesCount").descending().and(latestSort);
+      default:
+        return latestSort;
+    }
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
new file mode 100644
index 000000000..b3f359d5e
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
@@ -0,0 +1,25 @@
+
+package com.loopers.domain.product;
+
+import com.loopers.domain.like.LikeRepository;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+@RequiredArgsConstructor
+@Component
+public class ProductStockService {
+
+  public List<Product> deductStock(List<Product> products, Map<Long, Long> quantityMap) {
+    products.forEach(i -> i.deductStock(quantityMap.get(i.getId())));
+    return products;
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java
index b7f64a18f..a6fbad11d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java
@@ -7,5 +7,5 @@ public interface UserRepository {
 
   boolean existsByUserId(String userId);
 
-  Optional<UserModel> findByUserId(String userId);
+  Optional<UserModel> findById(Long userId);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
index dbb459db6..89e1d73ff 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
@@ -24,11 +24,11 @@ public UserModel join(UserModel user) {
   }
 
   @Transactional(readOnly = true)
-  public UserModel getUser(String userId) {
+  public UserModel getUser(Long userId) {
     //Ask: userId도 UserModel 안에 넣어서 왔으면, userId Model에서 검증 가능
-    if (userId == null || userId.isBlank()) {
+    if (userId == null) {
       throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
     }
-    return userRepository.findByUserId(userId).orElse(null);
+    return userRepository.findById(userId).orElse(null);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
new file mode 100644
index 000000000..5ca589a5a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
@@ -0,0 +1,12 @@
+package com.loopers.infrastructure.brand;
+
+import com.loopers.domain.brand.Brand;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface BrandJpaRepository extends JpaRepository<Brand, Long> {
+  Optional<Brand> findById(Long id);
+
+  Brand save(Brand brand);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java
new file mode 100644
index 000000000..60f26343a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java
@@ -0,0 +1,24 @@
+package com.loopers.infrastructure.brand;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Component
+public class BrandRepositoryImpl implements BrandRepository {
+  private final BrandJpaRepository jpaRepository;
+
+  @Override
+  public Optional<Brand> findById(Long id) {
+    return jpaRepository.findById(id);
+  }
+
+  @Override
+  public Brand save(Brand brand) {
+    return jpaRepository.save(brand);
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
new file mode 100644
index 000000000..033408bbb
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
@@ -0,0 +1,29 @@
+package com.loopers.infrastructure.like;
+
+import com.loopers.domain.like.Like;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+import java.util.Optional;
+
+public interface LikeJpaRepository extends JpaRepository<Like, Long> {
+  Optional<Like> findByUserIdAndProductId(Long userId, Long productId);
+
+  Like save(Like like);
+
+  long deleteByUserIdAndProductId(Long userId, Long productId);
+
+  boolean existsByUserIdAndProductId(Long userId, Long productId);
+
+  long countByProductId(Long productId);
+
+//  @Query(
+//      value = "SELECT l FROM Like l JOIN FETCH l.product p WHERE l.user.id = :userId",
+//      countQuery = "SELECT COUNT(l) FROM Like l WHERE l.user.id = :userId"
+//  )
+//  Page<Like> getLikedProducts(@Param("userId") Long userId, Pageable pageable);
+
+  Page<Like> getLikedProducts(@Param("userId") Long userId, Pageable pageable);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
new file mode 100644
index 000000000..bc6aa1f6e
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
@@ -0,0 +1,47 @@
+package com.loopers.infrastructure.like;
+
+import com.loopers.domain.like.Like;
+import com.loopers.domain.like.LikeRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Component
+public class LikeRepositoryImpl implements LikeRepository {
+  private final LikeJpaRepository jpaRepository;
+
+  @Override
+  public Optional<Like> findById(Long userId, Long productId) {
+    return jpaRepository.findByUserIdAndProductId(userId, productId);
+  }
+
+  @Override
+  public Like save(Like like) {
+    return jpaRepository.save(like);
+  }
+
+  @Override
+  public long remove(Long userId, Long productId) {
+    return jpaRepository.deleteByUserIdAndProductId(userId, productId);
+  }
+
+  @Override
+  public boolean isLiked(Long userId, Long productId) {
+    return jpaRepository.existsByUserIdAndProductId(userId, productId);
+  }
+
+  @Override
+  public long getLikeCount(Long productId) {
+    return jpaRepository.countByProductId(productId);
+  }
+
+  @Override
+  public Page<Like> getLikedProducts(Long userId, Pageable pageable) {
+    return jpaRepository.getLikedProducts(userId, pageable);
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java
new file mode 100644
index 000000000..2d9c05f07
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java
@@ -0,0 +1,15 @@
+package com.loopers.infrastructure.order;
+
+import com.loopers.domain.order.Order;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface OrderJpaRepository extends JpaRepository<Order, Long> {
+  Optional<Order> findById(Long id);
+
+  Page<Order> findByRefUserId(Long userId, Pageable pageable);
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java
new file mode 100644
index 000000000..3e29c2a90
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java
@@ -0,0 +1,32 @@
+package com.loopers.infrastructure.order;
+
+import com.loopers.domain.order.Order;
+import com.loopers.domain.order.OrderRepository;
+import com.loopers.domain.product.ProductRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Component
+public class OrderRepositoryImpl implements OrderRepository {
+  private final OrderJpaRepository jpaRepository;
+
+  @Override
+  public Optional<Order> findById(Long id) {
+    return jpaRepository.findById(id);
+  }
+
+  @Override
+  public Page<Order> findByUserId(Long userId, Pageable pageable) {
+    return jpaRepository.findByRefUserId(userId, pageable);
+  }
+
+  @Override
+  public Order save(Order order) {
+    return jpaRepository.save(order);
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
new file mode 100644
index 000000000..b595f6115
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
@@ -0,0 +1,33 @@
+package com.loopers.infrastructure.product;
+
+import com.loopers.domain.product.Product;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+public interface ProductJpaRepository extends JpaRepository<Product, Long> {
+  Optional<Product> findById(Long id);
+
+  List<Product> findAllById(Set<Long> id);
+
+  Page<Product> findByBrandId(Long brandId, Pageable pageable);
+
+  List<Product> save(List<Product> product);
+
+//  @Query(value = """
+//          SELECT new com.loopers.api.product.dto.Product(
+//              p,
+//              COUNT(l.id)
+//          )
+//          FROM Product p
+//          LEFT JOIN Like l ON l.product = p
+//          GROUP BY p
+//          ORDER BY p.id DESC
+//      """,
+//      countQuery = "SELECT COUNT(p.id) FROM Product p")
+  // Page<Product> findAllWithLikeCount(Pageable pageable);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
new file mode 100644
index 000000000..b14e4326a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
@@ -0,0 +1,50 @@
+package com.loopers.infrastructure.product;
+
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductRepository;
+import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.UserRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+@RequiredArgsConstructor
+@Component
+public class ProductRepositoryImpl implements ProductRepository {
+  private final ProductJpaRepository jpaRepository;
+
+  @Override
+  public Optional<Product> findById(Long id) {
+    return jpaRepository.findById(id);
+  }
+
+  @Override
+  public List<Product> findAllById(Set<Long> id) {
+    return jpaRepository.findAllById(id);
+  }
+
+  @Override
+  public Page<Product> findByBrandId(Long brandId, Pageable pageable) {
+    return jpaRepository.findByBrandId(brandId, pageable);
+  }
+
+  @Override
+  public Page<Product> findAll(Pageable pageable) {
+    return jpaRepository.findAll(pageable);
+  }
+
+  @Override
+  public Product save(Product product) {
+    return jpaRepository.save(product);
+  }
+
+  @Override
+  public List<Product> save(List<Product> products) {
+    return jpaRepository.save(products);
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java
index bd867ae25..c4af76308 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java
@@ -7,5 +7,6 @@
 
 public interface UserJpaRepository extends JpaRepository<UserModel, Long> {
   boolean existsByUserId(String userId);
-  Optional<UserModel> findByUserId(String userId);
+
+  Optional<UserModel> findById(Long id);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
index 3e33c3ff7..f1b0ec1a6 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
@@ -23,7 +23,7 @@ public boolean existsByUserId(String userId) {
   }
 
   @Override
-  public Optional<UserModel> findByUserId(String userId) {
-    return jpaRepository.findByUserId(userId);
+  public Optional<UserModel> findById(Long userId) {
+    return jpaRepository.findById(userId);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java
new file mode 100644
index 000000000..bbae88628
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java
@@ -0,0 +1,37 @@
+package com.loopers.interfaces.api.order;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.loopers.application.order.OrderInfo;
+import com.loopers.application.order.OrderItemInfo;
+
+import java.math.BigDecimal;
+import java.time.ZonedDateTime;
+import java.util.List;
+
+public class OrderCreateV1Dto {
+  public record OrderItemRequest(long productId, long quantity) {
+  }
+
+  public record OrderRequest(List<OrderItemRequest> items) {
+  }
+
+  public record OrderResponse(long id, String status,
+                              @JsonSerialize(using = ToStringSerializer.class) BigDecimal paymentPrice
+      , @JsonSerialize(using = ToStringSerializer.class) BigDecimal totalPrice
+      , @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") ZonedDateTime orderAt
+      , List<OrderItemInfo> orderItemInfo) {
+    public static OrderResponse from(OrderInfo info) {
+      if (info == null) return null;
+      return new OrderResponse(
+          info.id(),
+          info.status(),
+          info.paymentPrice(),
+          info.totalPrice(),
+          info.orderAt(),
+          info.orderItemInfo()
+      );
+    }
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
new file mode 100644
index 000000000..ddaf2bc04
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
@@ -0,0 +1,55 @@
+package com.loopers.interfaces.api.order;
+
+import com.loopers.domain.order.Order;
+import com.loopers.interfaces.api.ApiResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Tag(name = "Order V1 API", description = "Loopers 예시 API 입니다.")
+public interface OrderV1ApiSpec {
+  @Operation(
+      summary = "주문 목록 조회",
+      description = "사용자 ID로 주문 목록 조회합니다."
+  )
+  @Valid
+  ApiResponse<Page<Order>> getOrderList(
+      @Schema(name = "사용자 ID", description = "조회할 사용자의 ID")
+      @RequestHeader(value = "X-USER-ID", required = false) Long userId,
+      @RequestParam(name = "sortType", defaultValue = "latest") String sortType,
+      @RequestParam(name = "page", defaultValue = "0") int page,
+      @RequestParam(name = "size", defaultValue = "20") int size
+  );
+
+  @Operation(
+      summary = "주문 요청",
+      description = "ID로 주문를 충전합니다."
+  )
+  @Valid
+  ApiResponse<OrderCreateV1Dto.OrderResponse> createOrder(
+      @Schema(name = "사용자 ID", description = "충전할 사용자의 ID")
+      @RequestHeader(value = "X-USER-ID", required = false) Long userId
+      , @RequestBody OrderCreateV1Dto.OrderRequest request
+  );
+
+
+  @Operation(
+      summary = "주문 상세조회",
+      description = "주문 ID로 주문 상세조회합니다."
+  )
+  @Valid
+  ApiResponse<OrderCreateV1Dto.OrderResponse> getOrderDetail(
+      @Schema(name = "사용자 ID", description = "조회할 사용자의 ID")
+      @RequestHeader(value = "X-USER-ID", required = false) Long userId
+      , Long orderId
+  );
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
new file mode 100644
index 000000000..8f4f4a144
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
@@ -0,0 +1,63 @@
+package com.loopers.interfaces.api.order;
+
+import com.loopers.application.example.ExampleInfo;
+import com.loopers.application.order.CreateOrderCommand;
+import com.loopers.application.order.OrderFacade;
+import com.loopers.application.order.OrderInfo;
+import com.loopers.domain.order.Order;
+import com.loopers.interfaces.api.ApiResponse;
+import com.loopers.interfaces.api.example.ExampleV1Dto;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/api/v1/orders")
+public class OrderV1Controller implements OrderV1ApiSpec {
+
+  private final OrderFacade orderFacade;
+
+  @GetMapping("")
+  @Override
+  public ApiResponse<Page<Order>> getOrderList(
+      @RequestHeader(value = "X-USER-ID", required = false) Long userId,
+      @RequestParam(name = "sortType", defaultValue = "latest") String sortType,
+      @RequestParam(name = "page", defaultValue = "0") int page,
+      @RequestParam(name = "size", defaultValue = "20") int size
+  ) {
+    Page<Order> info = orderFacade.getOrderList(userId, sortType, page, size);
+    return ApiResponse.success(info);
+  }
+
+  @PostMapping("")
+  @Override
+  public ApiResponse<OrderCreateV1Dto.OrderResponse> createOrder(@RequestHeader(value = "X-USER-ID", required = false) Long userId
+      , @RequestBody OrderCreateV1Dto.OrderRequest request
+  ) {
+    Map<Long, Long> orderQuantityMap = request.items().stream()
+        .collect(Collectors.toMap(
+            OrderCreateV1Dto.OrderItemRequest::productId,
+            OrderCreateV1Dto.OrderItemRequest::quantity,
+            Long::sum
+        ));
+    OrderInfo info = orderFacade.createOrder(userId, orderQuantityMap);
+    OrderCreateV1Dto.OrderResponse response = OrderCreateV1Dto.OrderResponse.from(info);
+    return ApiResponse.success(response);
+  }
+
+  @GetMapping("/{orderId}")
+  @Override
+  public ApiResponse<OrderCreateV1Dto.OrderResponse> getOrderDetail(@RequestHeader(value = "X-USER-ID", required = false) Long userId
+      , @PathVariable(value = "orderId") Long orderId
+  ) {
+    OrderInfo info = orderFacade.getOrderDetail(orderId);
+    OrderCreateV1Dto.OrderResponse response = OrderCreateV1Dto.OrderResponse.from(info);
+    return ApiResponse.success(response);
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
new file mode 100644
index 000000000..4edc54cb4
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
@@ -0,0 +1,122 @@
+package com.loopers.application.product;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.UserModel;
+import com.loopers.infrastructure.brand.BrandJpaRepository;
+import com.loopers.infrastructure.product.ProductJpaRepository;
+import com.loopers.infrastructure.user.UserJpaRepository;
+import com.loopers.support.error.CoreException;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Page;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static com.loopers.domain.product.ProductAssertions.assertProduct;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertThrows;
+
+
+@SpringBootTest
+@Transactional
+class ProductFacadeIntegrationTest {
+  @Autowired
+  private ProductFacade productFacade;
+  @MockitoSpyBean
+  private UserJpaRepository userJpaRepository;
+  @MockitoSpyBean
+  private BrandJpaRepository brandJpaRepository;
+  @MockitoSpyBean
+  private ProductJpaRepository productJpaRepository;
+
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+
+  UserModel savedUser;
+  List<Product> savedProducts;
+
+  @BeforeEach
+  void setup() {
+    // arrange
+    UserModel user = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    savedUser = userJpaRepository.save(user);
+    List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
+        , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
+    List<Brand> savedBrandList = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
+
+    List<Product> productList = List.of(Product.create(savedBrandList.get(0), "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10)
+        , Product.create(savedBrandList.get(0), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+        , Product.create(savedBrandList.get(1), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+    );
+    savedProducts = productList.stream().map((product) -> productJpaRepository.save(product)).toList();
+
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("상품목록을 조회할 때,")
+  @Nested
+  class GetList {
+    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @Test
+    void 성공_상품목록조회() {
+      // act
+      Page<Product> productsPage = productFacade.getProductList(null, "latest", 0, 20);
+      List<Product> products = productsPage.getContent();
+      // assert
+      assertThat(products).isNotEmpty().hasSize(3);
+    }
+
+    @DisplayName("브랜드ID 검색조건 포함시, 해당 브랜드의 상품 목록이 조회된다.")
+    @Test
+    void 성공_상품목록조회_브랜드ID() {
+      // arrange
+      // act
+      Page<Product> productsPage = productFacade.getProductList(savedProducts.get(0).getId(), null, 0, 20);
+      List<Product> productList = productsPage.getContent();
+
+      // assert
+      assertThat(productList).isNotEmpty().hasSize(2);
+
+      assertProduct(productList.get(0), savedProducts.get(1));
+      assertProduct(productList.get(1), savedProducts.get(0));
+    }
+  }
+
+  @DisplayName("상품을 조회할 때,")
+  @Nested
+  class Get {
+    @DisplayName("존재하는 상품 ID를 주면, 해당 상품 정보를 반환한다.")
+    @Test
+    void 성공_존재하는_상품ID() {
+      // arrange
+      // act
+      ProductDetailInfo result = productFacade.getProductDetail(savedUser.getId(), savedProducts.get(0).getId());
+
+      // assert
+      assertThat(result.brandInfo().name()).isEqualTo(savedProducts.get(0).getBrand().getName());
+    }
+
+    @DisplayName("존재하지 않는 상품 ID를 주면, 예외가 반환된다.")
+    @Test
+    void 실패_존재하지_않는_상품ID() {
+      // arrange
+      // act
+      // assert
+      assertThrows(CoreException.class, () -> {
+        productFacade.getProductDetail(savedUser.getId(), (long) 100);
+      });
+    }
+  }
+
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandAssertions.java
new file mode 100644
index 000000000..bdf45dac1
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandAssertions.java
@@ -0,0 +1,15 @@
+package com.loopers.domain.brand;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BrandAssertions {
+  public static void assertBrand(Brand actual, Brand expected) {
+    assertThat(actual).isNotNull();
+    assertThat(expected).isNotNull();
+    assertThat(actual.getId()).isEqualTo(expected.getId());
+    assertThat(actual.getName()).isEqualTo(expected.getName());
+    assertThat(actual.getStory()).isEqualTo(expected.getStory());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
new file mode 100644
index 000000000..083acdf16
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
@@ -0,0 +1,49 @@
+package com.loopers.domain.brand;
+
+import com.loopers.domain.product.Product;
+import com.loopers.support.error.CoreException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class BrandModelTest {
+  Brand brand;
+  String validMsg = "";
+
+  @DisplayName("브랜드 모델을 생성할 때, ")
+  @Nested
+  class Create_Brand {
+    @DisplayName("브랜드명과 스토리가 모두 주어지면, 정상적으로 생성된다.")
+    @Test
+    void 성공_Brand_객체생성() {
+      //given
+      brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
+
+      //assert
+      assertThat(brand).isNotNull();
+      assertThat(brand.getName()).isEqualTo("레이브");
+      assertThat(brand.getStory()).isEqualTo("레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
+    }
+  }
+
+  @Nested
+  class Valid_Brand {
+    @BeforeEach
+    void setup() {
+      validMsg = "브랜드명은 비어있을 수 없습니다.";
+    }
+
+    @Test
+    void 실패_이름_오류() {
+      assertThatThrownBy(() -> {
+        brand = Brand.create("", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
+      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
+    }
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java
new file mode 100644
index 000000000..1a7346003
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java
@@ -0,0 +1,76 @@
+package com.loopers.domain.brand;
+
+import com.loopers.infrastructure.brand.BrandJpaRepository;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+@SpringBootTest
+class BrandServiceIntegrationTest {
+  @Autowired
+  private BrandService brandService;
+
+  @MockitoSpyBean
+  private BrandJpaRepository brandJpaRepository;
+
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+
+  Brand brand;
+
+  @BeforeEach
+  void setup() {
+
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("브랜드를 조회할 때,")
+  @Nested
+  class Get {
+    @DisplayName("존재하는 브랜드 ID를 주면, 해당 브랜드 정보를 반환한다.")
+    @Test
+    void 성공_존재하는_브랜드ID() {
+      // arrange
+      List<Brand> brands = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
+          , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
+      List<Brand> savedBrands = brands.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
+
+      // act
+      Brand result = brandService.getBrand(savedBrands.get(0).getId());
+
+      // assert
+      assertBrand(result, savedBrands.get(0));
+    }
+
+    @DisplayName("존재하지 않는 브랜드 ID를 주면, NOT_FOUND 예외가 발생한다.")
+    @Test
+    void 실패_존재하지_않는_브랜드ID() {
+      // arrange
+
+      // act
+      Brand result = brandService.getBrand((long) 1);
+
+      // assert
+      assertThat(result).isNull();
+    }
+  }
+
+  public static void assertBrand(Brand actual, Brand expected) {
+    assertThat(actual).isNotNull();
+    assertThat(expected).isNotNull();
+    assertThat(actual.getId()).isEqualTo(expected.getId());
+    assertThat(actual.getName()).isEqualTo(expected.getName());
+    assertThat(actual.getStory()).isEqualTo(expected.getStory());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
new file mode 100644
index 000000000..e6e6a40ec
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
@@ -0,0 +1,93 @@
+package com.loopers.domain.like;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@RequiredArgsConstructor
+@Component
+public class FakeLikeRepository implements LikeRepository {
+  private final Map<Long, Like> list = new HashMap<>();
+
+  @Override
+  public Optional<Like> findById(Long userId, Long productId) {
+    Long id = (long) 0;
+    for (Long key : list.keySet()) {
+      id = key;
+      Like like = list.get(key);
+      if (like.getUser().getId() == userId
+          && like.getProduct().getId() == productId) {
+        return Optional.of(like);
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public Like save(Like entity) {
+    Long id = (long) 0;
+    for (Long key : list.keySet()) {
+      id = key;
+      Like like = list.get(key);
+      if (like.getUser().getId() == entity.getUser().getId()
+          && like.getProduct().getId() == entity.getUser().getId()) {
+        throw new CoreException(ErrorType.BAD_REQUEST, "중복 좋아요 요청입니다.");
+      }
+    }
+    id = id == (long) 0 ? 0 : id + 1;
+    list.put(id, entity);
+    return entity;
+  }
+
+  @Override
+  public long remove(Long userId, Long productId) {
+    long cnt = 0;
+    for (Long key : list.keySet()) {
+      Like like = list.get(key);
+      if (like.getUser().getId() == userId
+          && like.getProduct().getId() == productId) {
+        list.remove(key);
+        cnt++;
+      }
+    }
+    return cnt;
+  }
+
+  @Override
+  public boolean isLiked(Long userId, Long productId) {
+    for (Long key : list.keySet()) {
+      Like like = list.get(key);
+      if (like.getUser().getId() == userId
+          && like.getProduct().getId() == productId) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public long getLikeCount(Long productId) {
+    long cnt = 0;
+    for (Long key : list.keySet()) {
+      Like like = list.get(key);
+      if (like.getProduct().getId() == productId) {
+        cnt++;
+      }
+    }
+    return cnt;
+  }
+
+  @Override
+  public Page<Like> getLikedProducts(Long userId, Pageable pageable) {
+    return new PageImpl<>(list.values().stream().filter(i -> i.getUser().getId() == userId)
+        .collect(Collectors.toList()), pageable, list.size());
+  }
+
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeAssertions.java
new file mode 100644
index 000000000..4f8201b0c
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeAssertions.java
@@ -0,0 +1,16 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.user.UserModel;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LikeAssertions {
+  public static void assertLike(Like actual, Like expected) {
+    assertThat(actual).isNotNull();
+    assertThat(expected).isNotNull();
+    assertThat(actual.getId()).isEqualTo(expected.getId());
+    assertThat(actual.getUser().getId()).isEqualTo(expected.getUser().getId());
+    assertThat(actual.getProduct().getId()).isEqualTo(expected.getProduct().getId());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeModelTest.java
new file mode 100644
index 000000000..a26061747
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeModelTest.java
@@ -0,0 +1,69 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.UserModel;
+import com.loopers.support.error.CoreException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class LikeModelTest {
+  Like like;
+  UserModel mockUser = mock(UserModel.class);
+  Product mockProduct = mock(Product.class);
+
+  @DisplayName("좋아요 모델을 생성할 때, ")
+  @Nested
+  class Create_Like {
+    @DisplayName("사용자id와 상품id가 모두 주어지면, 정상적으로 생성된다.")
+    @Test
+    void 성공_Like_객체생성() {
+      //given
+      mockUser = mock(UserModel.class);
+      when(mockUser.getId()).thenReturn(1L);
+      mockProduct = mock(Product.class);
+      when(mockProduct.getId()).thenReturn(10L);
+      //act
+      like = Like.create(mockUser, mockProduct);
+      //assert
+      assertThat(like).isNotNull();
+      assertThat(like.getUser().getId()).isEqualTo(mockUser.getId());
+      assertThat(like.getProduct().getId()).isEqualTo(mockProduct.getId());
+    }
+  }
+
+  @Nested
+  class Valid_Like {
+    @BeforeEach
+    void setup() {
+      mockUser = mock(UserModel.class);
+      when(mockUser.getId()).thenReturn(1L);
+      mockProduct = mock(Product.class);
+      when(mockProduct.getId()).thenReturn(10L);
+    }
+
+    @Test
+    void 실패_사용자ID_오류() {
+      //given
+      when(mockUser.getId()).thenReturn(null);
+      assertThatThrownBy(() -> {
+        like = Like.create(mockUser, mockProduct);
+      }).isInstanceOf(CoreException.class).hasMessageContaining("사용자ID는 비어있을 수 없습니다.");
+    }
+
+    @Test
+    void 실패_상품ID_오류() {
+      //given
+      when(mockProduct.getId()).thenReturn(null);
+      assertThatThrownBy(() -> {
+        like = Like.create(mockUser, mockProduct);
+      }).isInstanceOf(CoreException.class).hasMessageContaining("상품ID는 비어있을 수 없습니다.");
+    }
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
new file mode 100644
index 000000000..4fbe583f7
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
@@ -0,0 +1,153 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.UserModel;
+import com.loopers.infrastructure.brand.BrandJpaRepository;
+import com.loopers.infrastructure.like.LikeJpaRepository;
+import com.loopers.infrastructure.product.ProductJpaRepository;
+import com.loopers.infrastructure.user.UserJpaRepository;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.domain.Page;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static com.loopers.domain.like.LikeAssertions.assertLike;
+import static com.loopers.domain.product.ProductAssertions.assertProduct;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+@SpringBootTest
+class LikeServiceIntegrationTest {
+  @Autowired
+  private LikeService likeService;
+
+  @MockitoSpyBean
+  private LikeJpaRepository likeJpaRepository;
+  @MockitoSpyBean
+  private UserJpaRepository userJpaRepository;
+  @MockitoSpyBean
+  private BrandJpaRepository brandJpaRepository;
+  @MockitoSpyBean
+  private ProductJpaRepository productJpaRepository;
+
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+
+  UserModel savedUser;
+  Brand savedBrand;
+  Product savedProduct;
+
+  @TestConfiguration
+  static class FakeRepositoryConfig {
+    @Bean
+    public LikeRepository likeRepository() {
+      return new FakeLikeRepository();
+    }
+  }
+
+  @BeforeEach
+  void setup() {
+    UserModel user = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    savedUser = userJpaRepository.save(user);
+    Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
+    savedBrand = brandJpaRepository.save(brand);
+    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10);
+    savedProduct = productJpaRepository.save(product);
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("좋아요를 조회할 때,")
+  @Nested
+  class Create {
+    @DisplayName("존재하는 좋아요 ID를 주면, 해당 좋아요 정보를 반환한다.")
+    @Test
+    void 성공_존재하는_좋아요ID() {
+      // arrange
+
+      // act
+      Like result = likeService.save(Like.create(savedUser, savedProduct));
+
+      // assert
+      assertLike(result, Like.create(savedUser, savedProduct));
+    }
+
+    @DisplayName("존재하지 않는 좋아요 ID를 주면, 예외가 발생하지 않는다.")
+    @Test
+    void 성공_이미_존재하는_좋아요ID() {
+      // arrange
+      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+
+      // act
+      Like result2 = likeService.save(Like.create(savedUser, savedProduct));
+
+      // assert
+      assertLike(result1, result2);
+    }
+  }
+
+  @DisplayName("좋아요를 삭제할 때,")
+  @Nested
+  class Delete {
+    @DisplayName("존재하는 좋아요를 삭제한다.")
+    @Test
+    void 성공_존재하는_좋아요ID() {
+      // arrange
+      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+
+      // act
+      likeService.remove(savedUser.getId(), savedProduct.getId());
+
+      // assert
+      verify(likeJpaRepository, times(1)).deleteByUserIdAndProductId(savedUser.getId(), savedProduct.getId());
+      //assertThrows(NotFoundException.class, () -> service.read(id));
+    }
+
+    @DisplayName("존재하지 않는 좋아요 ID를 삭제하면, 예외가 발생하지 않는다.")
+    @Test
+    void 성공_이미_삭제된_좋아요() {
+      // arrange
+      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+
+      // act
+      likeService.remove(savedUser.getId(), savedProduct.getId());
+      likeService.remove(savedUser.getId(), savedProduct.getId());
+
+      // assert
+      verify(likeJpaRepository, times(1)).deleteByUserIdAndProductId(savedUser.getId(), savedProduct.getId());
+
+    }
+  }
+
+  @DisplayName("좋아요 상품목록을 조회할 때,")
+  @Nested
+  class GetList {
+    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @Test
+    void 성공_좋아요_상품목록조회() {
+      // arrange
+      Like savedLike = likeService.save(Like.create(savedUser, savedProduct));
+
+      // act
+      Page<Product> productsPage = likeService.getLikedProducts(savedUser.getId(), "latest", 0, 20);
+      List<Product> products = productsPage.getContent();
+
+      // assert
+      assertThat(products).isNotEmpty().hasSize(1);
+    }
+
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
new file mode 100644
index 000000000..5ebaef8db
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
@@ -0,0 +1,150 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.UserModel;
+import com.loopers.infrastructure.brand.BrandJpaRepository;
+import com.loopers.infrastructure.like.LikeJpaRepository;
+import com.loopers.infrastructure.product.ProductJpaRepository;
+import com.loopers.infrastructure.user.UserJpaRepository;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.domain.Page;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static com.loopers.domain.like.LikeAssertions.assertLike;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+@SpringBootTest
+class LikeServiceTest {
+  @Autowired
+  private LikeService likeService;
+
+  @TestConfiguration
+  static class FakeRepositoryConfig {
+
+    @Primary
+    @Bean
+    public LikeRepository likeRepository() {
+      return new FakeLikeRepository();
+    }
+  }
+
+  @MockitoSpyBean
+  private UserJpaRepository userJpaRepository;
+  @MockitoSpyBean
+  private BrandJpaRepository brandJpaRepository;
+  @MockitoSpyBean
+  private ProductJpaRepository productJpaRepository;
+
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+
+  UserModel savedUser;
+  Brand savedBrand;
+  Product savedProduct;
+
+  @BeforeEach
+  void setup() {
+    UserModel user = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    savedUser = userJpaRepository.save(user);
+    Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
+    savedBrand = brandJpaRepository.save(brand);
+    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10);
+    savedProduct = productJpaRepository.save(product);
+
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("좋아요를 조회할 때,")
+  @Nested
+  class Create {
+    @DisplayName("존재하는 좋아요 ID를 주면, 해당 좋아요 정보를 반환한다.")
+    @Test
+    void 성공_존재하는_좋아요ID() {
+      // arrange
+
+      // act
+      Like result = likeService.save(Like.create(savedUser, savedProduct));
+
+      // assert
+      assertLike(result, Like.create(savedUser, savedProduct));
+    }
+
+    @DisplayName("존재하지 않는 좋아요 ID를 주면, 예외가 발생하지 않는다.")
+    @Test
+    void 성공_이미_존재하는_좋아요ID() {
+      // arrange
+      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+
+      // act
+      Like result2 = likeService.save(Like.create(savedUser, savedProduct));
+
+      // assert
+      assertLike(result1, result2);
+    }
+  }
+
+  @DisplayName("좋아요를 삭제할 때,")
+  @Nested
+  class Delete {
+    @DisplayName("존재하는 좋아요를 삭제한다.")
+    @Test
+    void 성공_존재하는_좋아요ID() {
+      // arrange
+      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+
+      // act
+      likeService.remove(savedUser.getId(), savedProduct.getId());
+      //assert
+      
+    }
+
+    @DisplayName("존재하지 않는 좋아요 ID를 삭제하면, 예외가 발생하지 않는다.")
+    @Test
+    void 성공_이미_삭제된_좋아요() {
+      // arrange
+      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+
+      // act
+      likeService.remove(savedUser.getId(), savedProduct.getId());
+      likeService.remove(savedUser.getId(), savedProduct.getId());
+
+      // assert
+    }
+  }
+
+  @DisplayName("좋아요 상품목록을 조회할 때,")
+  @Nested
+  class GetList {
+    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @Test
+    void 성공_좋아요_상품목록조회() {
+      // arrange
+      Like savedLike = likeService.save(Like.create(savedUser, savedProduct));
+
+      // act
+      Page<Product> productsPage = likeService.getLikedProducts(savedUser.getId(), "latest", 0, 20);
+      List<Product> products = productsPage.getContent();
+
+      // assert
+      assertThat(products).isNotEmpty().hasSize(1);
+    }
+
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java
new file mode 100644
index 000000000..dedfe2fe2
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java
@@ -0,0 +1,16 @@
+package com.loopers.domain.order;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OrderAssertions {
+  public static void assertOrder(Order actual, Order expected) {
+    assertThat(actual).isNotNull();
+    assertThat(expected).isNotNull();
+    assertThat(actual.getId()).isEqualTo(expected.getId());
+    assertThat(actual.getRefUserId()).isEqualTo(expected.getRefUserId());
+    assertThat(actual.getStatus()).isEqualTo(expected.getStatus());
+    assertThat(actual.getPaymentPrice()).isEqualByComparingTo(expected.getPaymentPrice());
+    assertThat(actual.getTotalPrice()).isEqualByComparingTo(expected.getTotalPrice());
+    assertThat(actual.getOrderAt()).isEqualTo(expected.getOrderAt());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
new file mode 100644
index 000000000..ace14126f
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
@@ -0,0 +1,103 @@
+package com.loopers.domain.order;
+
+import com.loopers.support.error.CoreException;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.time.ZonedDateTime;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertThrows;
+
+class OrderModelTest {
+  Order order;
+
+  @DisplayName("주문 모델을 생성할 때, ")
+  @Nested
+  class Create_Order {
+    @DisplayName("사용자ID, 상태, 지불금액,총금액, 주문일자가 모두 주어지면, 정상적으로 생성된다.")
+    @Test
+    void 성공_Order_객체생성() {
+      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      assertThat(order.getRefUserId()).isEqualTo(1);
+      assertThat(order.getStatus()).isEqualTo(OrderStatus.PENDING);
+
+    }
+  }
+
+  @DisplayName("주문 모델을 생성할 때, 검증")
+  @Nested
+  class Valid_Order {
+
+    @Test
+    void 실패_지불가격_음수오류() {
+      assertThrows(CoreException.class, () -> {
+        order = Order.create(1, OrderStatus.PENDING, new BigDecimal(-10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      });
+    }
+
+    @Test
+    void 실패_총가격_음수오류() {
+      assertThrows(CoreException.class, () -> {
+        order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), new BigDecimal(-10_000), ZonedDateTime.now());
+      });
+    }
+  }
+
+  @DisplayName("주문 확인후, 상품준비중 상태로 변경")
+  @Nested
+  class Valid_상품준비중 {
+    @Test
+    void 실패_주문확인_결재전오류() {
+      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), new BigDecimal(-10_000), ZonedDateTime.now());
+      assertThatThrownBy(() -> {
+        order.preparing();
+      }).isInstanceOf(CoreException.class).hasMessageContaining("결재대기중");
+    }
+
+    @Test
+    void 실패_주문확인_상품준비중오류() {
+      order = Order.create(1, OrderStatus.PREPARING, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      assertThatThrownBy(() -> {
+        order.preparing();
+      }).isInstanceOf(CoreException.class).hasMessageContaining("이미 준비중");
+    }
+
+    @Test
+    void 실패_주문확인_상품준비완료오류() {
+      order = Order.create(1, OrderStatus.SHIPPED, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      assertThatThrownBy(() -> {
+        order.preparing();
+      }).isInstanceOf(CoreException.class).hasMessageContaining("상품준비 완료");
+    }
+
+    @Test
+    void 성공_상품준비중() {
+      order = Order.create(1, OrderStatus.PAID, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      order.preparing();
+      assertThat(order.getStatus()).isEqualTo(OrderStatus.PREPARING);
+    }
+  }
+
+  @DisplayName("상품 모델을 생성후, 주문취소")
+  @Nested
+  class Valid_주문취소 {
+    @Test
+    void 실패_주문취소_이미취소된주문오류() {
+      order = Order.create(1, OrderStatus.CANCELLED, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      assertThatThrownBy(() -> {
+        order.preparing();
+      }).isInstanceOf(CoreException.class).hasMessageContaining("이미 취소");
+    }
+
+    @Test
+    void 성공_주문취소() {
+      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      order.cancel();
+      assertThat(order.getStatus()).isEqualTo(OrderStatus.CANCELLED);
+    }
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
new file mode 100644
index 000000000..a3e21b571
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
@@ -0,0 +1,120 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductService;
+import com.loopers.infrastructure.brand.BrandJpaRepository;
+import com.loopers.infrastructure.product.ProductJpaRepository;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Page;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static com.loopers.domain.brand.BrandAssertions.assertBrand;
+import static com.loopers.domain.product.ProductAssertions.assertProduct;
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+@SpringBootTest
+@Transactional
+class OrderServiceIntegrationTest {
+  @Autowired
+  private ProductService productService;
+
+  @MockitoSpyBean
+  private BrandJpaRepository brandJpaRepository;
+  @MockitoSpyBean
+  private ProductJpaRepository productJpaRepository;
+
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+
+  List<Brand> savedBrands;
+  List<Product> savedProducts;
+
+  @BeforeEach
+  void setup() {
+    List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
+        , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
+    savedBrands = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
+
+    List<Product> productList = List.of(Product.create(savedBrands.get(0), "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10)
+        , Product.create(savedBrands.get(0), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+        , Product.create(savedBrands.get(1), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+    );
+    savedProducts = productList.stream().map((product) -> productJpaRepository.save(product)).toList();
+
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("상품목록을 조회할 때,")
+  @Nested
+  class GetList {
+    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @Test
+    void 성공_상품목록조회() {
+      // arrange
+
+      // act
+      Page<Product> productsPage = productService.getProducts(null, "latest", 0, 20);
+      List<Product> products = productsPage.getContent();
+      // assert
+      assertThat(products).isNotEmpty().hasSize(3);
+    }
+
+    @DisplayName("브랜드ID 검색조건 포함시, 해당 브랜드의 상품 목록이 조회된다.")
+    @Test
+    void 성공_상품목록조회_브랜드ID() {
+      // arrange
+
+      // act
+      Page<Product> productsPage = productService.getProducts(savedProducts.get(0).getId(), null, 0, 20);
+      List<Product> productList = productsPage.getContent();
+
+      // assert
+      assertThat(productList).isNotEmpty().hasSize(2);
+
+      assertProduct(productList.get(0), savedProducts.get(1));
+      assertProduct(productList.get(1), savedProducts.get(0));
+    }
+  }
+
+  @DisplayName("상품을 조회할 때,")
+  @Nested
+  class Get {
+    @DisplayName("존재하는 상품 ID를 주면, 해당 상품 정보를 반환한다.")
+    @Test
+    void 성공_존재하는_상품ID() {
+      // arrange
+      // act
+      Product result = productService.getProduct(savedProducts.get(0).getId());
+
+      // assert
+      assertProduct(result, savedProducts.get(0));
+      assertBrand(result.getBrand(), savedBrands.get(0));
+    }
+
+    @DisplayName("존재하지 않는 상품 ID를 주면, null이 반환된다.")
+    @Test
+    void 실패_존재하지_않는_상품ID() {
+      // arrange
+
+      // act
+      Product result = productService.getProduct((long) 1000);
+
+      // assert
+      assertThat(result).isNull();
+    }
+  }
+
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductAssertions.java
new file mode 100644
index 000000000..2c437632d
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductAssertions.java
@@ -0,0 +1,18 @@
+package com.loopers.domain.product;
+
+import com.loopers.domain.brand.Brand;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProductAssertions {
+  public static void assertProduct(Product actual, Product expected) {
+    assertThat(actual).isNotNull();
+    assertThat(expected).isNotNull();
+    assertThat(actual.getId()).isEqualTo(expected.getId());
+    assertThat(actual.getName()).isEqualTo(expected.getName());
+    assertThat(actual.getPrice()).isEqualByComparingTo(expected.getPrice());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
new file mode 100644
index 000000000..91cf461b0
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
@@ -0,0 +1,93 @@
+package com.loopers.domain.product;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.support.error.CoreException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertThrows;
+
+class ProductModelTest {
+  Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
+  Product product;
+  String validMsg = "";
+
+  @DisplayName("상품 모델을 생성할 때, ")
+  @Nested
+  class Create_Product {
+    @DisplayName("브랜드모델, 상품명, 가격, 재고, 예약재고가 모두 주어지면, 정상적으로 생성된다.")
+    @Test
+    void 성공_Product_객체생성() {
+      product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10);
+      assertThat(product).isNotNull();
+      assertThat(product.getName()).isEqualTo("Wild Faith Rose Sweatshirt");
+      assertThat(product.getPrice()).isEqualTo(new BigDecimal(80_000));
+      assertThat(product.getStock()).isEqualTo(10);
+    }
+  }
+
+  @DisplayName("상품 모델을 생성할 때, 검증")
+  @Nested
+  class Valid_Product {
+    @BeforeEach
+    void setup() {
+      validMsg = "형식이 유효하지 않습니다.";
+    }
+
+    @Test
+    void 실패_가격_음수오류() {
+      assertThrows(CoreException.class, () -> {
+        product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(-1000), 0);
+      });
+    }
+
+    @Test
+    void 실패_재고_음수오류() {
+      assertThrows(CoreException.class, () -> {
+        product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), -1);
+      });
+    }
+
+    @Test
+    void 실패_예약재고_음수오류() {
+      assertThrows(CoreException.class, () -> {
+        product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10);
+      });
+    }
+  }
+
+  @DisplayName("상품 모델을 생성후, 재고 예약")
+  @Nested
+  class Valid_재고차감 {
+    @BeforeEach
+    void setup() {
+      product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(1000), 10);
+    }
+
+    @Test
+    void 실패_예약재고0_차감오류() {
+      assertThatThrownBy(() -> {
+        product.deductStock(0);
+      }).isInstanceOf(CoreException.class).hasMessageContaining("재고차감 이상");
+    }
+
+    @Test
+    void 실패_예약재고20_차감오류() {
+      assertThatThrownBy(() -> {
+        product.deductStock(20);
+      }).isInstanceOf(CoreException.class).hasMessageContaining("재고차감 이상");
+    }
+
+    @Test
+    void 성공_재고예약() {
+      product.deductStock(2);
+      assertThat(product.getStock()).isEqualTo(8);
+    }
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
new file mode 100644
index 000000000..b1693262e
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
@@ -0,0 +1,118 @@
+package com.loopers.domain.product;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.infrastructure.brand.BrandJpaRepository;
+import com.loopers.infrastructure.product.ProductJpaRepository;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Page;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static com.loopers.domain.brand.BrandAssertions.assertBrand;
+import static com.loopers.domain.product.ProductAssertions.assertProduct;
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+@SpringBootTest
+@Transactional
+class ProductServiceIntegrationTest {
+  @Autowired
+  private ProductService productService;
+
+  @MockitoSpyBean
+  private BrandJpaRepository brandJpaRepository;
+  @MockitoSpyBean
+  private ProductJpaRepository productJpaRepository;
+
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+
+  List<Brand> savedBrands;
+  List<Product> savedProducts;
+
+  @BeforeEach
+  void setup() {
+    List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
+        , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
+    savedBrands = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
+
+    List<Product> productList = List.of(Product.create(savedBrands.get(0), "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10)
+        , Product.create(savedBrands.get(0), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+        , Product.create(savedBrands.get(1), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+    );
+    savedProducts = productList.stream().map((product) -> productJpaRepository.save(product)).toList();
+
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("상품목록을 조회할 때,")
+  @Nested
+  class GetList {
+    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @Test
+    void 성공_상품목록조회() {
+      // arrange
+
+      // act
+      Page<Product> productsPage = productService.getProducts(null, "latest", 0, 20);
+      List<Product> products = productsPage.getContent();
+      // assert
+      assertThat(products).isNotEmpty().hasSize(3);
+    }
+
+    @DisplayName("브랜드ID 검색조건 포함시, 해당 브랜드의 상품 목록이 조회된다.")
+    @Test
+    void 성공_상품목록조회_브랜드ID() {
+      // arrange
+
+      // act
+      Page<Product> productsPage = productService.getProducts(savedProducts.get(0).getId(), null, 0, 20);
+      List<Product> productList = productsPage.getContent();
+
+      // assert
+      assertThat(productList).isNotEmpty().hasSize(2);
+
+      assertProduct(productList.get(0), savedProducts.get(1));
+      assertProduct(productList.get(1), savedProducts.get(0));
+    }
+  }
+
+  @DisplayName("상품을 조회할 때,")
+  @Nested
+  class Get {
+    @DisplayName("존재하는 상품 ID를 주면, 해당 상품 정보를 반환한다.")
+    @Test
+    void 성공_존재하는_상품ID() {
+      // arrange
+      // act
+      Product result = productService.getProduct(savedProducts.get(0).getId());
+
+      // assert
+      assertProduct(result, savedProducts.get(0));
+      assertBrand(result.getBrand(), savedBrands.get(0));
+    }
+
+    @DisplayName("존재하지 않는 상품 ID를 주면, null이 반환된다.")
+    @Test
+    void 실패_존재하지_않는_상품ID() {
+      // arrange
+
+      // act
+      Product result = productService.getProduct((long) 1000);
+
+      // assert
+      assertThat(result).isNull();
+    }
+  }
+
+}

From e4b871106dc00fbea5013b6e1a5a52397db2a46d Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Fri, 14 Nov 2025 12:18:28 +0900
Subject: [PATCH 02/11] =?UTF-8?q?feat:=20OrderFacade=20=EC=A3=BC=EB=AC=B8?=
 =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/order/CreateOrderCommand.java |  19 +-
 .../application/order/OrderFacade.java        |  38 ++--
 .../loopers/application/order/OrderInfo.java  |   7 +-
 .../application/point/PointFacade.java        |  15 +-
 .../loopers/application/point/PointInfo.java  |   4 +-
 .../loopers/application/user/UserFacade.java  |   6 +-
 .../loopers/application/user/UserInfo.java    |   4 +-
 .../java/com/loopers/domain/like/Like.java    |   8 +-
 .../domain/order/CreateOrderService.java      |  40 +++++
 .../java/com/loopers/domain/order/Order.java  |  19 +-
 .../com/loopers/domain/order/OrderItem.java   |   2 +-
 .../loopers/domain/order/OrderPreparer.java   |  35 ++++
 .../loopers/domain/order/OrderRepository.java |   2 +-
 .../com/loopers/domain/order/OrderStatus.java |   8 +
 .../point/{PointModel.java => Point.java}     |  22 +--
 .../loopers/domain/point/PointRepository.java |   7 +-
 .../loopers/domain/point/PointService.java    |  28 +--
 .../com/loopers/domain/product/Product.java   |  15 +-
 .../domain/product/ProductService.java        |  13 +-
 .../domain/product/ProductStockService.java   |  25 +--
 .../domain/user/{UserModel.java => User.java} |  18 +-
 .../loopers/domain/user/UserPointService.java |  27 +++
 .../loopers/domain/user/UserRepository.java   |   7 +-
 .../com/loopers/domain/user/UserService.java  |  26 ++-
 .../like/LikeJpaRepository.java               |  13 +-
 .../like/LikeRepositoryImpl.java              |   2 +
 .../point/PointJpaRepository.java             |  13 +-
 .../point/PointRepositoryImpl.java            |  16 +-
 .../product/ProductJpaRepository.java         |   3 +-
 .../product/ProductRepositoryImpl.java        |   4 +-
 .../user/UserJpaRepository.java               |   8 +-
 .../user/UserRepositoryImpl.java              |  11 +-
 .../api/order/OrderCreateV1Dto.java           |   4 +-
 .../api/order/OrderV1Controller.java          |  16 +-
 .../interfaces/api/point/PointV1ApiSpec.java  |   4 +-
 .../api/point/PointV1Controller.java          |   4 +-
 .../com/loopers/support/error/ErrorType.java  |  20 ++-
 .../order/OrderFacadeIntegrationTest.java     | 168 ++++++++++++++++++
 .../product/ProductFacadeIntegrationTest.java |   6 +-
 .../loopers/domain/brand/BrandModelTest.java  |   2 +-
 .../loopers/domain/like/LikeAssertions.java   |   3 -
 .../loopers/domain/like/LikeModelTest.java    |   8 +-
 .../like/LikeServiceIntegrationTest.java      |  18 +-
 .../loopers/domain/like/LikeServiceTest.java  |  21 ++-
 .../loopers/domain/order/OrderAssertions.java |   1 -
 .../loopers/domain/order/OrderModelTest.java  |  32 ++--
 .../point/PointServiceIntegrationTest.java    |  24 +--
 .../{PointModelTest.java => PointTest.java}   |  30 ++--
 .../domain/product/ProductModelTest.java      |  14 +-
 .../user/UserServiceIntegrationTest.java      |  36 ++--
 .../{UserModelTest.java => UserTest.java}     |  26 +--
 .../interfaces/api/PointV1ApiE2ETest.java     |  23 +--
 .../interfaces/api/UserV1ApiE2ETest.java      |  11 +-
 53 files changed, 613 insertions(+), 323 deletions(-)
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java
 rename apps/commerce-api/src/main/java/com/loopers/domain/point/{PointModel.java => Point.java} (67%)
 rename apps/commerce-api/src/main/java/com/loopers/domain/user/{UserModel.java => User.java} (78%)
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
 rename apps/commerce-api/src/test/java/com/loopers/domain/point/{PointModelTest.java => PointTest.java} (62%)
 rename apps/commerce-api/src/test/java/com/loopers/domain/user/{UserModelTest.java => UserTest.java} (72%)

diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java b/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java
index 76f9c2c74..61aa9f7e3 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java
@@ -1,21 +1,22 @@
 package com.loopers.application.order;
 
-import com.loopers.domain.order.Order;
 import com.loopers.interfaces.api.order.OrderCreateV1Dto;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
 
-import java.math.BigDecimal;
-import java.time.ZonedDateTime;
-import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
-public record CreateOrderCommand(Long userId, List<ItemCommand> orderItemInfo) {
-  public record ItemCommand(Long productId, Long quantity) {
+public record CreateOrderCommand(Long userId, Map<Long, Long> orderItemInfo) {
+  public record ItemCommand(Long productId, Map<Long, Long> quantity) {
   }
 
   public static CreateOrderCommand from(Long userId, OrderCreateV1Dto.OrderRequest request) {
     return new CreateOrderCommand(
         userId,
-        request.items().stream().map(i -> new ItemCommand(i.productId(), i.quantity())).toList());
+        request.items().stream()
+            .collect(Collectors.toMap(
+                OrderCreateV1Dto.OrderItemRequest::productId,
+                OrderCreateV1Dto.OrderItemRequest::quantity,
+                Long::sum
+            )));
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
index 9aa8f0570..63fcc98bc 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
@@ -1,22 +1,20 @@
 package com.loopers.application.order;
 
+import com.loopers.domain.order.CreateOrderService;
 import com.loopers.domain.order.Order;
-import com.loopers.domain.order.OrderItem;
 import com.loopers.domain.order.OrderService;
-import com.loopers.domain.order.OrderStatus;
 import com.loopers.domain.point.PointService;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
 import com.loopers.domain.product.ProductStockService;
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserPointService;
 import com.loopers.domain.user.UserService;
 import jakarta.transaction.Transactional;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Component;
 
-import java.math.BigDecimal;
-import java.time.ZonedDateTime;
 import java.util.List;
 import java.util.Map;
 
@@ -27,8 +25,10 @@ public class OrderFacade {
   private final PointService pointService;
   private final ProductService productService;
   private final OrderService orderService;
+  private final CreateOrderService createOrderService;
 
   private final ProductStockService productStockService;
+  private final UserPointService userPointService;
 
   public Page<Order> getOrderList(Long userId,
                                   String sortType,
@@ -43,28 +43,16 @@ public OrderInfo getOrderDetail(Long orderId) {
   }
 
   @Transactional
-  public OrderInfo createOrder(Long userId, Map<Long, Long> productQuantityMap) {
-    //사용자 존재 확인
-    UserModel user = userService.getUser(userId);
+  public OrderInfo createOrder(CreateOrderCommand command) {
 
-    //상품 존재 확인
-    List<Product> productList = productService.getExistingProducts(productQuantityMap.keySet());
-
-    //재고확인및 차감
-    List<Product> deductedProducts = productStockService.deductStock(productList, productQuantityMap);
-    productService.save(deductedProducts);
-
-    //포인트 확인 및 차감
-    BigDecimal totalPrice = deductedProducts.stream()
-        .map(Product::getPrice)
-        .reduce(BigDecimal.ZERO, BigDecimal::add);
-    pointService.use(user, totalPrice);
-
-    //주문 생성
-    Order order = Order.create(userId, OrderStatus.PAID, totalPrice, totalPrice, ZonedDateTime.now());
-    order.setOrderItems(deductedProducts.stream().map(i -> OrderItem.create(i.getId(), productQuantityMap.get(i.getId()), i.getPrice())).toList());
-    Order savedOrder = orderService.save(order);
+    Map<Long, Long> quantityMap = command.orderItemInfo();
+    User user = userService.getActiveUser(command.userId());
+    List<Product> productList = productService.getExistingProducts(quantityMap.keySet());
+    Order savedOrder = null;
+    productStockService.deduct(productList, quantityMap);
+    userPointService.use(user, productList, quantityMap);
 
+    savedOrder = createOrderService.save(user, productList, quantityMap);
     return OrderInfo.from(savedOrder);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
index d83bb2494..95d20d92b 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
@@ -1,9 +1,5 @@
 package com.loopers.application.order;
 
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
-import com.loopers.application.brand.BrandInfo;
 import com.loopers.domain.order.Order;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
@@ -12,14 +8,13 @@
 import java.time.ZonedDateTime;
 import java.util.List;
 
-public record OrderInfo(long id, String status, BigDecimal paymentPrice, BigDecimal totalPrice
+public record OrderInfo(long id, String status, BigDecimal totalPrice
     , ZonedDateTime orderAt, List<OrderItemInfo> orderItemInfo) {
   public static OrderInfo from(Order model) {
     if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "주문정보를 찾을수 없습니다.");
     return new OrderInfo(
         model.getId(),
         model.getStatus().name(),
-        model.getPaymentPrice(),
         model.getTotalPrice(),
         model.getOrderAt(),
         OrderItemInfo.from(model.getOrderItems())
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/point/PointFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/point/PointFacade.java
index 92b3efd79..8a85275ff 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/point/PointFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/point/PointFacade.java
@@ -1,10 +1,8 @@
 package com.loopers.application.point;
 
 import com.loopers.domain.point.PointService;
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserService;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
 
@@ -16,14 +14,13 @@ public class PointFacade {
   private final UserService userService;
   private final PointService pointService;
 
-  public BigDecimal getPoint(String userId) {
-    UserModel user = userService.getUser(userId);
-    return pointService.getAmount(user.getUserId());
+  public BigDecimal getPoint(Long userId) {
+    User user = userService.getActiveUser(userId);
+    return user.getPoint().getAmount();
   }
 
-  public BigDecimal charge(String userId, BigDecimal chargeAmt) {
-    UserModel user = userService.getUser(userId);
-    if (user == null) throw new CoreException(ErrorType.NOT_FOUND, "유저정보를 찾을수 없습니다.");
+  public BigDecimal charge(Long userId, BigDecimal chargeAmt) {
+    User user = userService.getActiveUser(userId);
     return pointService.charge(user, chargeAmt);
   }
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/point/PointInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/point/PointInfo.java
index ff51218d9..ca76d1644 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/point/PointInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/point/PointInfo.java
@@ -1,11 +1,11 @@
 package com.loopers.application.point;
 
-import com.loopers.domain.point.PointModel;
+import com.loopers.domain.point.Point;
 
 import java.math.BigDecimal;
 
 public record PointInfo(String userId, BigDecimal amount) {
-  public static PointInfo from(PointModel model) {
+  public static PointInfo from(Point model) {
     if (model == null) return null;
     return new PointInfo(
         model.getUser().getUserId(),
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java
index f4e3e79c3..9e7703061 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java
@@ -1,6 +1,6 @@
 package com.loopers.application.user;
 
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserService;
 import com.loopers.interfaces.api.user.UserCreateV1Dto;
 import lombok.RequiredArgsConstructor;
@@ -12,12 +12,12 @@ public class UserFacade {
   private final UserService userService;
 
   public UserInfo join(UserCreateV1Dto.UserRequest requestDto) {
-    UserModel user = userService.join(UserModel.create(requestDto.userId(), requestDto.email(), requestDto.birthday(), requestDto.gender()));
+    User user = userService.join(User.create(requestDto.userId(), requestDto.email(), requestDto.birthday(), requestDto.gender()));
     return UserInfo.from(user);
   }
 
   public UserInfo getUser(String userId) {
-    UserModel user = userService.getUser(userId);
+    User user = userService.getUser(userId);
     return UserInfo.from(user);
   }
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/user/UserInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/user/UserInfo.java
index df777bb86..16b11be7d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/user/UserInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/user/UserInfo.java
@@ -1,11 +1,11 @@
 package com.loopers.application.user;
 
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 
 public record UserInfo(String userId, String email, String birthday, String gender) {
-  public static UserInfo from(UserModel model) {
+  public static UserInfo from(User model) {
     if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "유저정보를 찾을수 없습니다.");
     return new UserInfo(
         model.getUserId(),
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
index 425316747..7015ee53d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
@@ -2,7 +2,7 @@
 
 import com.loopers.domain.BaseEntity;
 import com.loopers.domain.product.Product;
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 import jakarta.persistence.*;
@@ -16,7 +16,7 @@
 public class Like extends BaseEntity {
   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "ref_user_Id", nullable = false)
-  private UserModel user;
+  private User user;
 
   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "ref_product_Id", nullable = false)
@@ -25,12 +25,12 @@ public class Like extends BaseEntity {
   protected Like() {
   }
 
-  private Like(UserModel user, Product product) {
+  private Like(User user, Product product) {
     this.user = user;
     this.product = product;
   }
 
-  public static Like create(UserModel user, Product product) {
+  public static Like create(User user, Product product) {
     if (user == null || user.getId() == null) {
       throw new CoreException(ErrorType.BAD_REQUEST, "사용자ID는 비어있을 수 없습니다.");
     }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
new file mode 100644
index 000000000..78cd49740
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
@@ -0,0 +1,40 @@
+
+package com.loopers.domain.order;
+
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductRepository;
+import com.loopers.domain.user.User;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@RequiredArgsConstructor
+@Component
+public class CreateOrderService {
+  private final ProductRepository productRepository;
+  private final OrderRepository orderRepository;
+
+  public Order save(User user, List<Product> productList, Map<Long, Long> quantityMap) {
+
+    List<OrderItem> orderItems = new ArrayList<>();
+
+    BigDecimal totalPrice = productList.stream().map(product -> {
+      long quantity = quantityMap.get(product.getId());
+      BigDecimal unitPrice = product.getPrice();
+      BigDecimal itemTotalPrice = unitPrice.multiply(new BigDecimal(quantity));
+
+      OrderItem orderItem = OrderItem.create(product.getId(), quantity, unitPrice);
+      orderItems.add(orderItem);
+
+      return itemTotalPrice;
+    }).reduce(BigDecimal.ZERO, BigDecimal::add);
+
+    Order order = Order.create(user.getId(), OrderStatus.PAID, totalPrice, orderItems);
+    return orderRepository.save(order);
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
index 3b285f582..157f231b7 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
@@ -12,7 +12,7 @@
 import java.util.List;
 
 @Entity
-@Table(name = "order")
+@Table(name = "orders")
 @Getter
 public class Order extends BaseEntity {
 
@@ -20,8 +20,6 @@ public class Order extends BaseEntity {
 
   private OrderStatus status;
 
-  BigDecimal paymentPrice;
-
   BigDecimal totalPrice;
 
   ZonedDateTime orderAt;
@@ -40,25 +38,22 @@ public void setOrderItems(List<OrderItem> orderItems) {
   protected Order() {
   }
 
-  private Order(long refUserId, OrderStatus status, BigDecimal paymentPrice, BigDecimal totalPrice, ZonedDateTime orderAt) {
+  private Order(long refUserId, OrderStatus status, BigDecimal totalPrice, List<OrderItem> orderItems) {
     this.refUserId = refUserId;
     this.status = status;
-    this.paymentPrice = paymentPrice;
     this.totalPrice = totalPrice;
-    this.orderAt = orderAt;
+    this.orderAt = ZonedDateTime.now();
+    this.orderItems = orderItems;
   }
 
-  public static Order create(long refUserId, OrderStatus status, BigDecimal paymentPrice, BigDecimal totalPrice, ZonedDateTime orderAt) {
+  public static Order create(long refUserId, OrderStatus status, BigDecimal totalPrice, List<OrderItem> orderItems) {
     if (status == null) {
       throw new CoreException(ErrorType.BAD_REQUEST, "상태 정보는 비어있을 수 없습니다.");
     }
-    if (paymentPrice.compareTo(BigDecimal.ZERO) < 0) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "지불금액은 음수일수 없습니다.");
-    }
     if (totalPrice.compareTo(BigDecimal.ZERO) < 0) {
       throw new CoreException(ErrorType.BAD_REQUEST, "총가격은 음수일수 없습니다.");
     }
-    return new Order(refUserId, status, paymentPrice, totalPrice, orderAt);
+    return new Order(refUserId, status, totalPrice, orderItems);
   }
 
   public void cancel() {
@@ -75,7 +70,7 @@ public void preparing() {
     if (status == OrderStatus.PREPARING) {
       throw new CoreException(ErrorType.BAD_REQUEST, "이미 준비중인 주문입니다.");
     }
-    if (OrderStatus.PAID.getSequence() < status.getSequence()) {
+    if (status.compare(OrderStatus.PREPARING) > 0) {
       throw new CoreException(ErrorType.BAD_REQUEST, "상품준비 완료된 주문입니다.");
     }
     this.status = OrderStatus.PREPARING;
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
index 07e51557e..17c324510 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
@@ -10,7 +10,7 @@
 import java.math.BigDecimal;
 
 @Entity
-@Table(name = "orderItem")
+@Table(name = "order_item")
 @Getter
 public class OrderItem extends BaseEntity {
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java
new file mode 100644
index 000000000..6a4a29d5a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java
@@ -0,0 +1,35 @@
+
+package com.loopers.domain.order;
+
+import com.loopers.domain.point.Point;
+import com.loopers.domain.product.Product;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+@RequiredArgsConstructor
+@Component
+public class OrderPreparer {
+  public static List<Product> verifyProductStockDeductable(List<Product> products, Map<Long, Long> quantityMap) {
+    products.forEach(i -> i.deductStock(quantityMap.get(i.getId())));
+    return products;
+  }
+  public static List<Product> verifyProductStockAddable(List<Product> products, Map<Long, Long> quantityMap) {
+    products.forEach(i -> i.addStock(quantityMap.get(i.getId())));
+    return products;
+  }
+
+  public static BigDecimal getTotalAmt(Point point, List<Product> products, Map<Long, Long> quantityMap) {
+    return products.stream()
+        .map(product -> {
+          Long productId = product.getId();
+          Long quantity = quantityMap.getOrDefault(productId, 0L);
+          return product.getPrice().multiply(BigDecimal.valueOf(quantity));
+        })
+        .reduce(BigDecimal.ZERO, BigDecimal::add);
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java
index c03bd66a8..4ba06bc61 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java
@@ -10,6 +10,6 @@ public interface OrderRepository {
 
   Page<Order> findByUserId(Long userId, Pageable pageable);
 
-  Order save(Order product);
+  Order save(Order order);
 
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
index b93673616..8dd95ef7e 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
@@ -1,5 +1,8 @@
 package com.loopers.domain.order;
 
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 
@@ -15,5 +18,10 @@ public enum OrderStatus {
   REFUNDED("환불 완료", 100);
 
   private final String description;
+  @Getter(AccessLevel.NONE)
   private final int sequence;
+
+  public Integer compare(OrderStatus other) {
+    return Integer.compare(this.sequence, other.sequence);
+  }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointModel.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
similarity index 67%
rename from apps/commerce-api/src/main/java/com/loopers/domain/point/PointModel.java
rename to apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
index db3321685..0264af717 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointModel.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
@@ -1,7 +1,7 @@
 package com.loopers.domain.point;
 
 import com.loopers.domain.BaseEntity;
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 import jakarta.persistence.*;
@@ -13,33 +13,33 @@
 @Entity
 @Table(name = "point")
 @Getter
-public class PointModel extends BaseEntity {
+public class Point extends BaseEntity {
   private BigDecimal amount;
 
   @OneToOne
-  @JoinColumn(name = "userId", unique = true, nullable = false)
-  private UserModel user;
+  @JoinColumn(name = "ref_user_Id", unique = true, nullable = false)
+  private User user;
 
-  public void setUser(UserModel user) {
+  public void setUser(User user) {
     this.user = user;
     if (user.getPoint() != this) {
       user.setPoint(this);
     }
   }
 
-  protected PointModel() {
+  protected Point() {
     this.amount = BigDecimal.ZERO;
   }
 
-  private PointModel(UserModel user, BigDecimal amount) {
-    Objects.requireNonNull(user, "UserModel must not be null for PointModel creation.");
+  private Point(User user, BigDecimal amount) {
+    Objects.requireNonNull(user, "유저 정보가 없습니다.");
 
     this.setUser(user);
     this.amount = amount;
   }
 
-  public static PointModel create(UserModel user, BigDecimal amount) {
-    return new PointModel(user, amount);
+  public static Point create(User user, BigDecimal amount) {
+    return new Point(user, amount);
   }
 
   public void charge(BigDecimal amountToChange) {
@@ -54,7 +54,7 @@ public void use(BigDecimal amountToChange) {
       throw new CoreException(ErrorType.BAD_REQUEST, "차감 금액은 0보다 커야 합니다.");
     }
     if (this.amount.compareTo(amountToChange) < 0) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "잔액이 부족합니다.");
+      throw new CoreException(ErrorType.INSUFFICIENT_POINT, "포인트이가 부족합니다.");
     }
     this.amount = this.amount.subtract(amountToChange);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
index 35d2dc95c..0524bc7ac 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
@@ -6,12 +6,13 @@
 import java.util.Optional;
 
 public interface PointRepository {
+  Optional<Point> findByUserId(Long userId);
 
-  Optional<PointModel> findByUserId(String userId);
+  Optional<Point> findByUserId(String userId);
 
   @Lock(LockModeType.PESSIMISTIC_WRITE)
-  Optional<PointModel> findByUserIdForUpdate(String userId);
+  Optional<Point> findByUserIdForUpdate(Long userId);
 
-  PointModel save(PointModel point);
+  Point save(Point point);
 
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
index 9b953581d..9482ade25 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
@@ -1,7 +1,7 @@
 
 package com.loopers.domain.point;
 
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 import lombok.RequiredArgsConstructor;
@@ -18,31 +18,37 @@ public class PointService {
   private final PointRepository pointRepository;
 
   @Transactional(readOnly = true)
-  public BigDecimal getAmount(String userId) {
+  public Point getAvailablePoints(Long userId) {
     return pointRepository.findByUserId(userId)
-        .map(PointModel::getAmount)
+        .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "포인트 정보를 찾을 수 없습니다."));
+  }
+
+  @Transactional(readOnly = true)
+  public BigDecimal getAmount(Long userId) {
+    return pointRepository.findByUserId(userId)
+        .map(Point::getAmount)
         .orElse(null);
   }
 
   @Transactional
-  public BigDecimal charge(UserModel user, BigDecimal chargeAmt) {
-    Optional<PointModel> pointOpt = pointRepository.findByUserIdForUpdate(user.getUserId());
-    if (pointOpt.isEmpty()) {
+  public BigDecimal charge(User user, BigDecimal chargeAmt) {
+    Optional<Point> pointOpt = pointRepository.findByUserIdForUpdate(user.getId());
+    if (!pointOpt.isPresent()) {
       throw new CoreException(ErrorType.NOT_FOUND, "현재 포인트 정보를 찾을수 없습니다.");
     }
     pointOpt.get().charge(chargeAmt);
     pointRepository.save(pointOpt.get());
-    return getAmount(user.getUserId());
+    return getAmount(user.getId());
   }
 
   @Transactional
-  public BigDecimal use(UserModel user, BigDecimal useAmt) {
-    Optional<PointModel> pointOpt = pointRepository.findByUserIdForUpdate(user.getUserId());
-    if (pointOpt.isEmpty()) {
+  public BigDecimal use(User user, BigDecimal useAmt) {
+    Optional<Point> pointOpt = pointRepository.findByUserIdForUpdate(user.getId());
+    if (!pointOpt.isPresent()) {
       throw new CoreException(ErrorType.NOT_FOUND, "현재 포인트 정보를 찾을수 없습니다.");
     }
     pointOpt.get().use(useAmt);
     pointRepository.save(pointOpt.get());
-    return getAmount(user.getUserId());
+    return getAmount(user.getId());
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
index f5df0758f..0a536113f 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
@@ -45,6 +45,12 @@ private Product(Brand brand, String name, BigDecimal price, long stock) {
   }
 
   public static Product create(Brand brand, String name, BigDecimal price, long stock) {
+    if (price.compareTo(BigDecimal.ZERO) < 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "가격은 음수 일수 없습니다.");
+    }
+    if (stock < 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "재고는 0보다 커야 합니다.");
+    }
     return new Product(brand, name, price, stock);
   }
 
@@ -53,8 +59,15 @@ public void deductStock(long quantity) {
       throw new CoreException(ErrorType.BAD_REQUEST, "수량은 0보다 커야 합니다.");
     }
     if (this.stock < quantity) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "재고가 부족합니다.");
+      throw new CoreException(ErrorType.INSUFFICIENT_STOCK, "재고가 부족합니다.");
     }
     this.stock -= quantity;
   }
+
+  public void addStock(long quantity) {
+    if (quantity <= 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "수량은 0보다 커야 합니다.");
+    }
+    this.stock += quantity;
+  }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
index 9819ad90b..ef8fb2cd7 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
@@ -1,9 +1,7 @@
 
 package com.loopers.domain.product;
 
-import com.loopers.application.order.CreateOrderCommand;
 import com.loopers.domain.like.LikeRepository;
-import com.loopers.domain.order.Order;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 import lombok.RequiredArgsConstructor;
@@ -15,6 +13,7 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
+import java.util.stream.Collectors;
 
 @RequiredArgsConstructor
 @Component
@@ -55,12 +54,18 @@ public List<Product> getExistingProducts(Set<Long> productIds) {
     if (productIds == null || productIds.isEmpty()) {
       return Collections.emptyList();
     }
+    Set<Long> uniqueIds = new HashSet<>(productIds);
     List<Product> products = productRepository.findAllById(productIds);
 
-    if (products.size() != productIds.size()) {
+    if (products.size() != uniqueIds.size()) {
+      Set<Long> foundIds = products.stream()
+          .map(Product::getId)
+          .collect(Collectors.toSet());
+
+      uniqueIds.removeAll(foundIds);
       throw new CoreException(
           ErrorType.NOT_FOUND,
-          "다음 상품 ID들은 찾을 수 없습니다: "
+          "다음 상품 ID들은 찾을 수 없습니다: " + uniqueIds.toString()
       );
     }
     return products;
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
index b3f359d5e..075b25d6e 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
@@ -1,25 +1,26 @@
 
 package com.loopers.domain.product;
 
-import com.loopers.domain.like.LikeRepository;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
+import com.loopers.domain.order.OrderPreparer;
 import lombok.RequiredArgsConstructor;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
 import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
 
-import java.util.*;
+import java.util.List;
+import java.util.Map;
 
 @RequiredArgsConstructor
 @Component
 public class ProductStockService {
+  private final ProductRepository productService;
 
-  public List<Product> deductStock(List<Product> products, Map<Long, Long> quantityMap) {
-    products.forEach(i -> i.deductStock(quantityMap.get(i.getId())));
-    return products;
+  public void deduct(List<Product> products, Map<Long, Long> quantityMap) {
+    List<Product> deductedProducts = OrderPreparer.verifyProductStockDeductable(products, quantityMap);
+    productService.save(deductedProducts);
   }
+
+  public void add(List<Product> products, Map<Long, Long> quantityMap) {
+    List<Product> addedProducts = OrderPreparer.verifyProductStockAddable(products, quantityMap);
+    productService.save(addedProducts);
+  }
+
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserModel.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
similarity index 78%
rename from apps/commerce-api/src/main/java/com/loopers/domain/user/UserModel.java
rename to apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
index 500eae5ef..61c8167c0 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserModel.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
@@ -1,7 +1,7 @@
 package com.loopers.domain.user;
 
 import com.loopers.domain.BaseEntity;
-import com.loopers.domain.point.PointModel;
+import com.loopers.domain.point.Point;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 import jakarta.persistence.*;
@@ -15,26 +15,26 @@
 @Entity
 @Table(name = "user")
 @Getter
-public class UserModel extends BaseEntity {
+public class User extends BaseEntity {
   private String userId;
   private String email;
   private LocalDate birthday;
   private String gender;
 
   @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
-  private PointModel point;
+  private Point point;
 
-  public void setPoint(PointModel point) {
+  public void setPoint(Point point) {
     this.point = point;
     if (point.getUser() != this) {
       point.setUser(this);
     }
   }
 
-  protected UserModel() {
+  protected User() {
   }
 
-  private UserModel(String userId, String email, String birthday, String gender) {
+  private User(String userId, String email, String birthday, String gender) {
 
     if (!Pattern.compile("^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]{1,10}$").matcher(userId).matches()) {
       throw new CoreException(
@@ -67,11 +67,11 @@ private UserModel(String userId, String email, String birthday, String gender) {
     this.email = email;
     this.birthday = LocalDate.parse(birthday);
     this.gender = gender;
-    this.point = PointModel.create(this, BigDecimal.TEN);
+    this.point = Point.create(this, BigDecimal.TEN);
   }
 
-  public static UserModel create(String userId, String email, String birthday, String gender) {
-    return new UserModel(userId, email, birthday, gender);
+  public static User create(String userId, String email, String birthday, String gender) {
+    return new User(userId, email, birthday, gender);
   }
 
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
new file mode 100644
index 000000000..467bbcd63
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
@@ -0,0 +1,27 @@
+
+package com.loopers.domain.user;
+
+import com.loopers.domain.order.OrderPreparer;
+import com.loopers.domain.point.PointService;
+import com.loopers.domain.product.Product;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+@RequiredArgsConstructor
+@Component
+public class UserPointService {
+
+  private final PointService pointService;
+
+  @Transactional
+  public void use(User user, List<Product> productList, Map<Long, Long> quantityMap) {
+
+    BigDecimal totalAmt = OrderPreparer.getTotalAmt(user.getPoint(), productList, quantityMap);
+    pointService.use(user, totalAmt);
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java
index a6fbad11d..2ac424125 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java
@@ -3,9 +3,12 @@
 import java.util.Optional;
 
 public interface UserRepository {
-  UserModel save(UserModel user);
+  User save(User user);
 
   boolean existsByUserId(String userId);
 
-  Optional<UserModel> findById(Long userId);
+  Optional<User> findById(Long id);
+
+  Optional<User> findByUserId(String userId);
+
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
index 89e1d73ff..7481cf1cb 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
@@ -7,8 +7,6 @@
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.Optional;
-
 @RequiredArgsConstructor
 @Component
 public class UserService {
@@ -16,7 +14,7 @@ public class UserService {
   private final UserRepository userRepository;
 
   @Transactional
-  public UserModel join(UserModel user) {
+  public User join(User user) {
     if (userRepository.existsByUserId(user.getUserId())) {
       throw new CoreException(ErrorType.BAD_REQUEST, "이미 가입된 ID 입니다.");
     }
@@ -24,11 +22,27 @@ public UserModel join(UserModel user) {
   }
 
   @Transactional(readOnly = true)
-  public UserModel getUser(Long userId) {
-    //Ask: userId도 UserModel 안에 넣어서 왔으면, userId Model에서 검증 가능
+  public User getUser(Long userId) {
     if (userId == null) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
+      throw new CoreException(ErrorType.NOT_FOUND, "ID가 없습니다.");
     }
     return userRepository.findById(userId).orElse(null);
   }
+
+  @Transactional(readOnly = true)
+  public User getUser(String userId) {
+    if (userId == null) {
+      throw new CoreException(ErrorType.NOT_FOUND, "ID가 없습니다.");
+    }
+    return userRepository.findByUserId(userId).orElse(null);
+  }
+
+  @Transactional(readOnly = true)
+  public User getActiveUser(Long userId) {
+    if (userId == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
+    }
+    return userRepository.findById(userId)
+        .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "사용자 정보를 찾을 수 없습니다."));
+  }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
index 033408bbb..1008e0adb 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
@@ -4,6 +4,7 @@
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
 
 import java.util.Optional;
@@ -19,11 +20,11 @@ public interface LikeJpaRepository extends JpaRepository<Like, Long> {
 
   long countByProductId(Long productId);
 
-//  @Query(
-//      value = "SELECT l FROM Like l JOIN FETCH l.product p WHERE l.user.id = :userId",
-//      countQuery = "SELECT COUNT(l) FROM Like l WHERE l.user.id = :userId"
-//  )
-//  Page<Like> getLikedProducts(@Param("userId") Long userId, Pageable pageable);
-
+  @Query(
+      value = "SELECT l FROM Like l JOIN FETCH l.product p WHERE l.user.id = :userId",
+      countQuery = "SELECT COUNT(l) FROM Like l WHERE l.user.id = :userId"
+  )
   Page<Like> getLikedProducts(@Param("userId") Long userId, Pageable pageable);
+
+  //Page<Like> getLikedProducts(@Param("userId") Long userId, Pageable pageable);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
index bc6aa1f6e..74507560e 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
@@ -3,6 +3,7 @@
 import com.loopers.domain.like.Like;
 import com.loopers.domain.like.LikeRepository;
 import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Primary;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Component;
@@ -11,6 +12,7 @@
 
 @RequiredArgsConstructor
 @Component
+@Primary
 public class LikeRepositoryImpl implements LikeRepository {
   private final LikeJpaRepository jpaRepository;
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
index 2c436109d..a5a85c432 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
@@ -1,18 +1,21 @@
 package com.loopers.infrastructure.point;
 
-import com.loopers.domain.point.PointModel;
+import com.loopers.domain.point.Point;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
 
 import java.util.Optional;
 
-public interface PointJpaRepository extends JpaRepository<PointModel, Long> {
+public interface PointJpaRepository extends JpaRepository<Point, Long> {
   boolean existsByUserUserId(String userId);
 
-  Optional<PointModel> findByUserUserId(String userId);
+  Optional<Point> findByUserUserId(String userId);
+
+  Optional<Point> findByUserId(Long userId);
+
+  @Query("SELECT p FROM Point p WHERE p.user.id = :userId")
+  Optional<Point> findByUserIdForUpdate(@Param("userId") Long userId);
 
-  @Query("SELECT p FROM PointModel p WHERE p.user.userId = :userId")
-  Optional<PointModel> findByUserUserIdForUpdate(@Param("userId") String userId);
 
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
index cce08040d..b9ef6bf0e 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
@@ -1,6 +1,6 @@
 package com.loopers.infrastructure.point;
 
-import com.loopers.domain.point.PointModel;
+import com.loopers.domain.point.Point;
 import com.loopers.domain.point.PointRepository;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
@@ -13,18 +13,22 @@ public class PointRepositoryImpl implements PointRepository {
   private final PointJpaRepository jpaRepository;
 
   @Override
-  public PointModel save(PointModel point) {
+  public Point save(Point point) {
     return jpaRepository.save(point);
   }
 
   @Override
-  public Optional<PointModel> findByUserId(String userId) {
-    return jpaRepository.findByUserUserId(userId);
+  public Optional<Point> findByUserId(Long userId) {
+    return jpaRepository.findByUserId(userId);
   }
 
   @Override
-  public Optional<PointModel> findByUserIdForUpdate(String userId) {
-    return jpaRepository.findByUserUserIdForUpdate(userId);
+  public Optional<Point> findByUserId(String userId) {
+    return jpaRepository.findByUserUserId(userId);
   }
 
+  @Override
+  public Optional<Point> findByUserIdForUpdate(Long userId) {
+    return jpaRepository.findByUserIdForUpdate(userId);
+  }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
index b595f6115..e2119e400 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
@@ -7,12 +7,11 @@
 
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 
 public interface ProductJpaRepository extends JpaRepository<Product, Long> {
   Optional<Product> findById(Long id);
 
-  List<Product> findAllById(Set<Long> id);
+  List<Product> findAllById(Iterable<Long> id);
 
   Page<Product> findByBrandId(Long brandId, Pageable pageable);
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
index b14e4326a..584ac2a5c 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
@@ -2,8 +2,6 @@
 
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductRepository;
-import com.loopers.domain.user.UserModel;
-import com.loopers.domain.user.UserRepository;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -45,6 +43,6 @@ public Product save(Product product) {
 
   @Override
   public List<Product> save(List<Product> products) {
-    return jpaRepository.save(products);
+    return jpaRepository.saveAll(products);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java
index c4af76308..015978561 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java
@@ -1,12 +1,14 @@
 package com.loopers.infrastructure.user;
 
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import org.springframework.data.jpa.repository.JpaRepository;
 
 import java.util.Optional;
 
-public interface UserJpaRepository extends JpaRepository<UserModel, Long> {
+public interface UserJpaRepository extends JpaRepository<User, Long> {
   boolean existsByUserId(String userId);
 
-  Optional<UserModel> findById(Long id);
+  Optional<User> findById(Long id);
+
+  Optional<User> findByUserId(String id);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
index f1b0ec1a6..e4e85f02d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
@@ -1,6 +1,6 @@
 package com.loopers.infrastructure.user;
 
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserRepository;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
@@ -13,7 +13,7 @@ public class UserRepositoryImpl implements UserRepository {
   private final UserJpaRepository jpaRepository;
 
   @Override
-  public UserModel save(UserModel user) {
+  public User save(User user) {
     return jpaRepository.save(user);
   }
 
@@ -23,7 +23,12 @@ public boolean existsByUserId(String userId) {
   }
 
   @Override
-  public Optional<UserModel> findById(Long userId) {
+  public Optional<User> findById(Long userId) {
     return jpaRepository.findById(userId);
   }
+
+  @Override
+  public Optional<User> findByUserId(String userId) {
+    return jpaRepository.findByUserId(userId);
+  }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java
index bbae88628..0ef09e8b2 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java
@@ -17,8 +17,7 @@ public record OrderItemRequest(long productId, long quantity) {
   public record OrderRequest(List<OrderItemRequest> items) {
   }
 
-  public record OrderResponse(long id, String status,
-                              @JsonSerialize(using = ToStringSerializer.class) BigDecimal paymentPrice
+  public record OrderResponse(long id, String status
       , @JsonSerialize(using = ToStringSerializer.class) BigDecimal totalPrice
       , @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") ZonedDateTime orderAt
       , List<OrderItemInfo> orderItemInfo) {
@@ -27,7 +26,6 @@ public static OrderResponse from(OrderInfo info) {
       return new OrderResponse(
           info.id(),
           info.status(),
-          info.paymentPrice(),
           info.totalPrice(),
           info.orderAt(),
           info.orderItemInfo()
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
index 8f4f4a144..b42521578 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
@@ -1,21 +1,14 @@
 package com.loopers.interfaces.api.order;
 
-import com.loopers.application.example.ExampleInfo;
 import com.loopers.application.order.CreateOrderCommand;
 import com.loopers.application.order.OrderFacade;
 import com.loopers.application.order.OrderInfo;
 import com.loopers.domain.order.Order;
 import com.loopers.interfaces.api.ApiResponse;
-import com.loopers.interfaces.api.example.ExampleV1Dto;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.web.bind.annotation.*;
 
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
 @RequiredArgsConstructor
 @RestController
 @RequestMapping("/api/v1/orders")
@@ -40,13 +33,8 @@ public ApiResponse<Page<Order>> getOrderList(
   public ApiResponse<OrderCreateV1Dto.OrderResponse> createOrder(@RequestHeader(value = "X-USER-ID", required = false) Long userId
       , @RequestBody OrderCreateV1Dto.OrderRequest request
   ) {
-    Map<Long, Long> orderQuantityMap = request.items().stream()
-        .collect(Collectors.toMap(
-            OrderCreateV1Dto.OrderItemRequest::productId,
-            OrderCreateV1Dto.OrderItemRequest::quantity,
-            Long::sum
-        ));
-    OrderInfo info = orderFacade.createOrder(userId, orderQuantityMap);
+    CreateOrderCommand command = CreateOrderCommand.from(userId, request);
+    OrderInfo info = orderFacade.createOrder(command);
     OrderCreateV1Dto.OrderResponse response = OrderCreateV1Dto.OrderResponse.from(info);
     return ApiResponse.success(response);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1ApiSpec.java
index 7e86ed59c..47b7660c9 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1ApiSpec.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1ApiSpec.java
@@ -19,7 +19,7 @@ public interface PointV1ApiSpec {
   @Valid
   ApiResponse<BigDecimal> getPoint(
       @Schema(name = "사용자 ID", description = "조회할 사용자의 ID")
-      @RequestHeader(value = "X-USER-ID", required = false) String userId
+      @RequestHeader(value = "X-USER-ID", required = false) Long userId
   );
 
   @Operation(
@@ -29,7 +29,7 @@ ApiResponse<BigDecimal> getPoint(
   @Valid
   ApiResponse<BigDecimal> charge(
       @Schema(name = "사용자 ID", description = "충전할 사용자의 ID")
-      @RequestHeader(value = "X-USER-ID", required = false) String userId
+      @RequestHeader(value = "X-USER-ID", required = false) Long userId
       , @RequestBody BigDecimal amount
   );
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java
index 211efbe2a..5adeadb43 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java
@@ -16,14 +16,14 @@ public class PointV1Controller implements PointV1ApiSpec {
 
   @GetMapping("")
   @Override
-  public ApiResponse<BigDecimal> getPoint(@RequestHeader(value = "X-USER-ID", required = false) String userId
+  public ApiResponse<BigDecimal> getPoint(@RequestHeader(value = "X-USER-ID", required = false) Long userId
   ) {
     return ApiResponse.success(pointFacade.getPoint(userId));
   }
 
   @PostMapping("/charge")
   @Override
-  public ApiResponse<BigDecimal> charge(@RequestHeader(value = "X-USER-ID", required = false) String userId
+  public ApiResponse<BigDecimal> charge(@RequestHeader(value = "X-USER-ID", required = false) Long userId
       , @RequestBody BigDecimal amount
   ) {
     return ApiResponse.success(pointFacade.charge(userId, amount));
diff --git a/apps/commerce-api/src/main/java/com/loopers/support/error/ErrorType.java b/apps/commerce-api/src/main/java/com/loopers/support/error/ErrorType.java
index 5d142efbf..8bb2324d6 100644
--- a/apps/commerce-api/src/main/java/com/loopers/support/error/ErrorType.java
+++ b/apps/commerce-api/src/main/java/com/loopers/support/error/ErrorType.java
@@ -7,13 +7,17 @@
 @Getter
 @RequiredArgsConstructor
 public enum ErrorType {
-    /** 범용 에러 */
-    INTERNAL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), "일시적인 오류가 발생했습니다."),
-    BAD_REQUEST(HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST.getReasonPhrase(), "잘못된 요청입니다."),
-    NOT_FOUND(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.getReasonPhrase(), "존재하지 않는 요청입니다."),
-    CONFLICT(HttpStatus.CONFLICT, HttpStatus.CONFLICT.getReasonPhrase(), "이미 존재하는 리소스입니다.");
+  /**
+   * 범용 에러
+   */
+  INTERNAL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), "일시적인 오류가 발생했습니다."),
+  BAD_REQUEST(HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST.getReasonPhrase(), "잘못된 요청입니다."),
+  NOT_FOUND(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.getReasonPhrase(), "존재하지 않는 요청입니다."),
+  CONFLICT(HttpStatus.CONFLICT, HttpStatus.CONFLICT.getReasonPhrase(), "이미 존재하는 리소스입니다."),
 
-    private final HttpStatus status;
-    private final String code;
-    private final String message;
+  INSUFFICIENT_STOCK(HttpStatus.BAD_REQUEST, "INSUFFICIENT_STOCK", "요청하신 상품의 재고가 부족합니다."),
+  INSUFFICIENT_POINT(HttpStatus.BAD_REQUEST, "INSUFFICIENT_POINT", "사용 가능한 포인트가 부족합니다.");
+  private final HttpStatus status;
+  private final String code;
+  private final String message;
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
new file mode 100644
index 000000000..cf9123d78
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
@@ -0,0 +1,168 @@
+package com.loopers.application.order;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.order.Order;
+import com.loopers.domain.order.OrderItem;
+import com.loopers.domain.order.OrderStatus;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.user.User;
+import com.loopers.infrastructure.brand.BrandJpaRepository;
+import com.loopers.infrastructure.order.OrderJpaRepository;
+import com.loopers.infrastructure.product.ProductJpaRepository;
+import com.loopers.infrastructure.user.UserJpaRepository;
+import com.loopers.interfaces.api.order.OrderCreateV1Dto;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Page;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertThrows;
+
+
+@SpringBootTest
+@Transactional
+class OrderFacadeIntegrationTest {
+  @Autowired
+  private OrderFacade orderFacade;
+  @MockitoSpyBean
+  private UserJpaRepository userJpaRepository;
+  @MockitoSpyBean
+  private BrandJpaRepository brandJpaRepository;
+  @MockitoSpyBean
+  private ProductService productService;
+
+  @MockitoSpyBean
+  private OrderJpaRepository orderJpaRepository;
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+
+  User savedUser;
+  List<Product> savedProducts;
+  Order savedOrder;
+
+  @BeforeEach
+  void setup() {
+    // arrange
+    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    savedUser = userJpaRepository.save(user);
+    List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
+        , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
+    List<Brand> savedBrandList = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
+
+    List<Product> productList = List.of(Product.create(savedBrandList.get(0), "Wild Faith Rose Sweatshirt", new BigDecimal(8), 10)
+        , Product.create(savedBrandList.get(0), "Flower Pattern Fleece Jacket", new BigDecimal(4), 10)
+        , Product.create(savedBrandList.get(1), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+    );
+    savedProducts = productService.save(productList);
+    List<OrderItem> orderItems = new ArrayList<>();
+    orderItems.add(OrderItem.create(user.getId(), 2L, new BigDecimal(5_000)));
+    Order order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), orderItems);
+    savedOrder = orderJpaRepository.save(order);
+
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("주문목록을 조회할 때,")
+  @Nested
+  class GetList {
+    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @Test
+    void 성공_주문목록조회() {
+      // act
+      Page<Order> ordersPage = orderFacade.getOrderList(savedUser.getId(), "latest", 0, 20);
+      List<Order> orders = ordersPage.getContent();
+      // assert
+      assertThat(orders).isNotEmpty().hasSize(1);
+    }
+  }
+
+  @DisplayName("주문을 조회할 때,")
+  @Nested
+  class Get {
+    @DisplayName("존재하는 상품 ID를 주면, 해당 상품 정보를 반환한다.")
+    @Test
+    void 성공_존재하는_상품ID() {
+      // arrange
+      // act
+      OrderInfo result = orderFacade.getOrderDetail(savedOrder.getId());
+
+      // assert
+      assertThat(result.id()).isEqualTo(savedOrder.getId());
+      assertThat(result.status().toString()).isEqualTo(savedOrder.getStatus().toString());
+      assertThat(result.totalPrice()).isEqualTo(savedOrder.getTotalPrice());
+    }
+
+    @DisplayName("존재하지 않는 상품 ID를 주면, 예외가 반환된다.")
+    @Test
+    void 실패_존재하지_않는_상품ID() {
+      // arrange
+      // act
+      // assert
+      assertThrows(CoreException.class, () -> {
+        orderFacade.getOrderDetail(0L);
+      });
+    }
+  }
+
+  @DisplayName("주문을 생성할 때,")
+  @Nested
+  class Post {
+    @DisplayName("10재고가 있는 8원의 상품 1건 주문시, 기본 포인트10으로 결재가 된다.")
+    @Test
+    void 성공_단건주문생성() {
+      List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
+      items.add(new OrderCreateV1Dto.OrderItemRequest(savedProducts.get(0).getId(), 1));
+      OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
+      CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUser.getId(), request);
+      // act
+      OrderInfo savedOrder = orderFacade.createOrder(orderCommand);
+      // assert
+      assertThat(savedOrder).isNotNull();
+      assertThat(savedOrder.totalPrice()).isEqualTo(savedProducts.get(0).getPrice());
+      assertThat(savedOrder.orderItemInfo()).hasSize(1);
+    }
+
+    @DisplayName("10재고가 있는 8원의 상품 20건 주문시, 재고 없음 오류가 발생한다.")
+    @Test
+    void 실패_재고없음오류() {
+      List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
+      items.add(new OrderCreateV1Dto.OrderItemRequest(savedProducts.get(0).getId(), 20));
+      OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
+      CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUser.getId(), request);
+      // act
+      // assert
+      assertThrows(CoreException.class, () -> orderFacade.createOrder(orderCommand)).getErrorType().equals(ErrorType.INSUFFICIENT_STOCK);
+    }
+
+    @DisplayName("10재고가 있는 4원의 상품 3건 주문시, 포인트 부족 오류가 발생한다.")
+    @Test
+    void 실패_포인트부족오류() {
+      long productId = savedProducts.get(1).getId();
+      long quantity = 3L;
+      List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
+      items.add(new OrderCreateV1Dto.OrderItemRequest(productId, quantity));
+      OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
+      CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUser.getId(), request);
+      // act
+      // assert
+      assertThrows(CoreException.class, () -> orderFacade.createOrder(orderCommand)).getErrorType().equals(ErrorType.INSUFFICIENT_POINT);
+    }
+  }
+
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
index 4edc54cb4..d958c1b6d 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
@@ -2,7 +2,7 @@
 
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.product.Product;
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
@@ -39,13 +39,13 @@ class ProductFacadeIntegrationTest {
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
 
-  UserModel savedUser;
+  User savedUser;
   List<Product> savedProducts;
 
   @BeforeEach
   void setup() {
     // arrange
-    UserModel user = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
     savedUser = userJpaRepository.save(user);
     List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
         , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
index 083acdf16..57e57f3da 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
@@ -43,7 +43,7 @@ void setup() {
     void 실패_이름_오류() {
       assertThatThrownBy(() -> {
         brand = Brand.create("", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
-      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
+      }).isInstanceOf(IllegalArgumentException.class).hasMessageContaining(validMsg);
     }
   }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeAssertions.java
index 4f8201b0c..0c62874c5 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeAssertions.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeAssertions.java
@@ -1,8 +1,5 @@
 package com.loopers.domain.like;
 
-import com.loopers.domain.brand.Brand;
-import com.loopers.domain.user.UserModel;
-
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class LikeAssertions {
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeModelTest.java
index a26061747..a977dc616 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeModelTest.java
@@ -1,7 +1,7 @@
 package com.loopers.domain.like;
 
 import com.loopers.domain.product.Product;
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.support.error.CoreException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
@@ -15,7 +15,7 @@
 
 class LikeModelTest {
   Like like;
-  UserModel mockUser = mock(UserModel.class);
+  User mockUser = mock(User.class);
   Product mockProduct = mock(Product.class);
 
   @DisplayName("좋아요 모델을 생성할 때, ")
@@ -25,7 +25,7 @@ class Create_Like {
     @Test
     void 성공_Like_객체생성() {
       //given
-      mockUser = mock(UserModel.class);
+      mockUser = mock(User.class);
       when(mockUser.getId()).thenReturn(1L);
       mockProduct = mock(Product.class);
       when(mockProduct.getId()).thenReturn(10L);
@@ -42,7 +42,7 @@ class Create_Like {
   class Valid_Like {
     @BeforeEach
     void setup() {
-      mockUser = mock(UserModel.class);
+      mockUser = mock(User.class);
       when(mockUser.getId()).thenReturn(1L);
       mockProduct = mock(Product.class);
       when(mockProduct.getId()).thenReturn(10L);
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
index 4fbe583f7..ccb7fbeab 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
@@ -2,7 +2,7 @@
 
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.product.Product;
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.like.LikeJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
@@ -18,9 +18,9 @@
 
 import java.math.BigDecimal;
 import java.util.List;
+import java.util.Optional;
 
 import static com.loopers.domain.like.LikeAssertions.assertLike;
-import static com.loopers.domain.product.ProductAssertions.assertProduct;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -43,7 +43,7 @@ class LikeServiceIntegrationTest {
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
 
-  UserModel savedUser;
+  User savedUser;
   Brand savedBrand;
   Product savedProduct;
 
@@ -57,7 +57,7 @@ public LikeRepository likeRepository() {
 
   @BeforeEach
   void setup() {
-    UserModel user = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
     savedUser = userJpaRepository.save(user);
     Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
     savedBrand = brandJpaRepository.save(brand);
@@ -80,9 +80,9 @@ class Create {
 
       // act
       Like result = likeService.save(Like.create(savedUser, savedProduct));
-
+      Optional<Like> savedLike = likeJpaRepository.findById(result.getId());
       // assert
-      assertLike(result, Like.create(savedUser, savedProduct));
+      assertLike(result, savedLike.get());
     }
 
     @DisplayName("존재하지 않는 좋아요 ID를 주면, 예외가 발생하지 않는다.")
@@ -120,14 +120,14 @@ class Delete {
     @Test
     void 성공_이미_삭제된_좋아요() {
       // arrange
-      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+      Like savedLike = likeService.save(Like.create(savedUser, savedProduct));
 
       // act
       likeService.remove(savedUser.getId(), savedProduct.getId());
-      likeService.remove(savedUser.getId(), savedProduct.getId());
+      Long result1 = likeService.remove(savedUser.getId(), savedProduct.getId());
 
       // assert
-      verify(likeJpaRepository, times(1)).deleteByUserIdAndProductId(savedUser.getId(), savedProduct.getId());
+      verify(likeJpaRepository, times(3)).deleteByUserIdAndProductId(savedUser.getId(), savedProduct.getId());
 
     }
   }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
index 5ebaef8db..729d1b8c3 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
@@ -2,9 +2,8 @@
 
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.product.Product;
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
-import com.loopers.infrastructure.like.LikeJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
 import com.loopers.utils.DatabaseCleanUp;
@@ -22,19 +21,22 @@
 
 import static com.loopers.domain.like.LikeAssertions.assertLike;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 
 @SpringBootTest
 class LikeServiceTest {
+
+  private final LikeService likeService;
+
   @Autowired
-  private LikeService likeService;
+  public LikeServiceTest(LikeService likeService) {
+    this.likeService = likeService;
+  }
 
   @TestConfiguration
   static class FakeRepositoryConfig {
 
-    @Primary
     @Bean
     public LikeRepository likeRepository() {
       return new FakeLikeRepository();
@@ -51,13 +53,13 @@ public LikeRepository likeRepository() {
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
 
-  UserModel savedUser;
+  User savedUser;
   Brand savedBrand;
   Product savedProduct;
 
   @BeforeEach
   void setup() {
-    UserModel user = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
     savedUser = userJpaRepository.save(user);
     Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
     savedBrand = brandJpaRepository.save(brand);
@@ -83,7 +85,8 @@ class Create {
       Like result = likeService.save(Like.create(savedUser, savedProduct));
 
       // assert
-      assertLike(result, Like.create(savedUser, savedProduct));
+      assertThat(result).isNotNull();
+      assertThat(result.getUser()).isNotNull();
     }
 
     @DisplayName("존재하지 않는 좋아요 ID를 주면, 예외가 발생하지 않는다.")
@@ -112,7 +115,7 @@ class Delete {
       // act
       likeService.remove(savedUser.getId(), savedProduct.getId());
       //assert
-      
+
     }
 
     @DisplayName("존재하지 않는 좋아요 ID를 삭제하면, 예외가 발생하지 않는다.")
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java
index dedfe2fe2..65a876a15 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java
@@ -9,7 +9,6 @@ public static void assertOrder(Order actual, Order expected) {
     assertThat(actual.getId()).isEqualTo(expected.getId());
     assertThat(actual.getRefUserId()).isEqualTo(expected.getRefUserId());
     assertThat(actual.getStatus()).isEqualTo(expected.getStatus());
-    assertThat(actual.getPaymentPrice()).isEqualByComparingTo(expected.getPaymentPrice());
     assertThat(actual.getTotalPrice()).isEqualByComparingTo(expected.getTotalPrice());
     assertThat(actual.getOrderAt()).isEqualTo(expected.getOrderAt());
   }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
index ace14126f..82d2f1d60 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
@@ -6,7 +6,8 @@
 import org.junit.jupiter.api.Test;
 
 import java.math.BigDecimal;
-import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -14,6 +15,7 @@
 
 class OrderModelTest {
   Order order;
+  List<OrderItem> orderItems;
 
   @DisplayName("주문 모델을 생성할 때, ")
   @Nested
@@ -21,7 +23,8 @@ class Create_Order {
     @DisplayName("사용자ID, 상태, 지불금액,총금액, 주문일자가 모두 주어지면, 정상적으로 생성된다.")
     @Test
     void 성공_Order_객체생성() {
-      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      orderItems = new ArrayList<>();
+      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), orderItems);
       assertThat(order.getRefUserId()).isEqualTo(1);
       assertThat(order.getStatus()).isEqualTo(OrderStatus.PENDING);
 
@@ -32,17 +35,10 @@ class Create_Order {
   @Nested
   class Valid_Order {
 
-    @Test
-    void 실패_지불가격_음수오류() {
-      assertThrows(CoreException.class, () -> {
-        order = Order.create(1, OrderStatus.PENDING, new BigDecimal(-10_000), new BigDecimal(10_000), ZonedDateTime.now());
-      });
-    }
-
     @Test
     void 실패_총가격_음수오류() {
       assertThrows(CoreException.class, () -> {
-        order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), new BigDecimal(-10_000), ZonedDateTime.now());
+        order = Order.create(1, OrderStatus.PENDING, new BigDecimal(-10_000), orderItems);
       });
     }
   }
@@ -52,7 +48,7 @@ class Valid_Order {
   class Valid_상품준비중 {
     @Test
     void 실패_주문확인_결재전오류() {
-      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), new BigDecimal(-10_000), ZonedDateTime.now());
+      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), orderItems);
       assertThatThrownBy(() -> {
         order.preparing();
       }).isInstanceOf(CoreException.class).hasMessageContaining("결재대기중");
@@ -60,7 +56,7 @@ class Valid_상품준비중 {
 
     @Test
     void 실패_주문확인_상품준비중오류() {
-      order = Order.create(1, OrderStatus.PREPARING, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      order = Order.create(1, OrderStatus.PREPARING, new BigDecimal(10_000), orderItems);
       assertThatThrownBy(() -> {
         order.preparing();
       }).isInstanceOf(CoreException.class).hasMessageContaining("이미 준비중");
@@ -68,7 +64,7 @@ class Valid_상품준비중 {
 
     @Test
     void 실패_주문확인_상품준비완료오류() {
-      order = Order.create(1, OrderStatus.SHIPPED, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      order = Order.create(1, OrderStatus.SHIPPED, new BigDecimal(10_000), orderItems);
       assertThatThrownBy(() -> {
         order.preparing();
       }).isInstanceOf(CoreException.class).hasMessageContaining("상품준비 완료");
@@ -76,7 +72,7 @@ class Valid_상품준비중 {
 
     @Test
     void 성공_상품준비중() {
-      order = Order.create(1, OrderStatus.PAID, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      order = Order.create(1, OrderStatus.PAID, new BigDecimal(10_000), orderItems);
       order.preparing();
       assertThat(order.getStatus()).isEqualTo(OrderStatus.PREPARING);
     }
@@ -87,15 +83,15 @@ class Valid_상품준비중 {
   class Valid_주문취소 {
     @Test
     void 실패_주문취소_이미취소된주문오류() {
-      order = Order.create(1, OrderStatus.CANCELLED, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
-      assertThatThrownBy(() -> {
+      order = Order.create(1, OrderStatus.CANCELLED, new BigDecimal(10_000), orderItems);
+      assertThrows(CoreException.class, () -> {
         order.preparing();
-      }).isInstanceOf(CoreException.class).hasMessageContaining("이미 취소");
+      });
     }
 
     @Test
     void 성공_주문취소() {
-      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), new BigDecimal(10_000), ZonedDateTime.now());
+      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), orderItems);
       order.cancel();
       assertThat(order.getStatus()).isEqualTo(OrderStatus.CANCELLED);
     }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
index ac000465c..c86ebaea9 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
@@ -1,6 +1,6 @@
 package com.loopers.domain.point;
 
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.infrastructure.point.PointJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
 import com.loopers.support.error.CoreException;
@@ -49,11 +49,11 @@ class Get {
     void 성공_존재하는_유저ID() {
       // arrange
       BigDecimal JOIN_POINT = BigDecimal.TEN;
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      userJpaRepository.save(userModel);
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User saved = userJpaRepository.save(user);
 
       // act
-      BigDecimal pointAmt = pointService.getAmount(userModel.getUserId());
+      BigDecimal pointAmt = pointService.getAmount(saved.getId());
 
       // assert(회원가입시, 기본포인트 10)
       assertAll(
@@ -66,10 +66,10 @@ class Get {
     @Test
     void 실패_존재하지않는_유저ID() {
       // arrange (등록된 회원 없음)
-      String userId = "userId";
+      Long id = 5L;
 
       // act
-      BigDecimal pointAmt = pointService.getAmount(userId);
+      BigDecimal pointAmt = pointService.getAmount(id);
 
       // assert
       assertThat(pointAmt).isNull();
@@ -84,11 +84,11 @@ class Charge {
     @Test
     void 실패_존재하지않는_유저ID() {
       // arrange (등록된 회원 없음)
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
 
       // act, assert
       assertThatThrownBy(() -> {
-        pointService.charge(userModel, BigDecimal.TEN);
+        pointService.charge(user, BigDecimal.TEN);
       }).isInstanceOf(CoreException.class).hasMessageContaining("현재 포인트 정보를 찾을수 없습니다.");
 
     }
@@ -98,11 +98,11 @@ class Charge {
     void 성공_존재하는_유저ID() {
       // arrange
       BigDecimal JOIN_POINT = BigDecimal.TEN;
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      userJpaRepository.save(userModel);
-      Optional<UserModel> savedUserModel = userJpaRepository.findByUserId(userModel.getUserId());
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User savedUser = userJpaRepository.save(user);
+
       // act
-      BigDecimal pointAmt = pointService.charge(savedUserModel.get(), BigDecimal.TEN);
+      BigDecimal pointAmt = pointService.charge(savedUser, BigDecimal.TEN);
 
       // assert(회원가입시, 기본포인트 10)
       assertAll(
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
similarity index 62%
rename from apps/commerce-api/src/test/java/com/loopers/domain/point/PointModelTest.java
rename to apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
index 02bcd0387..345e7f0e6 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
@@ -1,6 +1,6 @@
 package com.loopers.domain.point;
 
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.support.error.CoreException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
@@ -12,13 +12,13 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-class PointModelTest {
-  UserModel userModel;
-  PointModel pointModel;
+class PointTest {
+  User user;
+  Point point;
 
   @BeforeEach
   void setup() {
-    userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
   }
 
   @DisplayName("포인트 충전")
@@ -28,24 +28,24 @@ class Charge {
     @Test
     void 실패_포인트충전_0() {
       // arrange
-      pointModel = PointModel.create(userModel, BigDecimal.ZERO);
+      point = Point.create(user, BigDecimal.ZERO);
 
       // act, assert
       assertThatThrownBy(() -> {
-        pointModel.charge(BigDecimal.ZERO);
+        point.charge(BigDecimal.ZERO);
       }).isInstanceOf(CoreException.class).hasMessageContaining("충전 금액은 0보다 커야 합니다.");
     }
 
     @Test
     void 성공_포인트충전() {
       // arrange
-      pointModel = PointModel.create(userModel, new BigDecimal(20));
+      point = Point.create(user, new BigDecimal(20));
 
       // act
-      pointModel.charge(new BigDecimal(5));
+      point.charge(new BigDecimal(5));
 
       // assert
-      assertThat(pointModel.getAmount()).isEqualTo(new BigDecimal(25));
+      assertThat(point.getAmount()).isEqualTo(new BigDecimal(25));
     }
   }
 
@@ -55,24 +55,24 @@ class Use {
     @Test
     void 실패_포인트사용() {
       // arrange
-      pointModel = PointModel.create(userModel, BigDecimal.ZERO);
+      point = Point.create(user, BigDecimal.ZERO);
 
       // act, assert
       assertThatThrownBy(() -> {
-        pointModel.use(BigDecimal.TEN);
+        point.use(BigDecimal.TEN);
       }).isInstanceOf(CoreException.class).hasMessageContaining("잔액이 부족합니다.");
     }
 
     @Test
     void 성공_포인트사용() {
       // arrange
-      pointModel = PointModel.create(userModel, new BigDecimal(20));
+      point = Point.create(user, new BigDecimal(20));
 
       // act
-      pointModel.use(new BigDecimal(5));
+      point.use(new BigDecimal(5));
 
       // assert
-      assertThat(pointModel.getAmount()).isEqualTo(new BigDecimal(15));
+      assertThat(point.getAmount()).isEqualTo(new BigDecimal(15));
     }
   }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
index 91cf461b0..cf44ba293 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
@@ -54,12 +54,6 @@ void setup() {
       });
     }
 
-    @Test
-    void 실패_예약재고_음수오류() {
-      assertThrows(CoreException.class, () -> {
-        product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10);
-      });
-    }
   }
 
   @DisplayName("상품 모델을 생성후, 재고 예약")
@@ -72,16 +66,16 @@ void setup() {
 
     @Test
     void 실패_예약재고0_차감오류() {
-      assertThatThrownBy(() -> {
+      assertThrows(CoreException.class, () -> {
         product.deductStock(0);
-      }).isInstanceOf(CoreException.class).hasMessageContaining("재고차감 이상");
+      });
     }
 
     @Test
     void 실패_예약재고20_차감오류() {
-      assertThatThrownBy(() -> {
+      assertThrows(CoreException.class, () -> {
         product.deductStock(20);
-      }).isInstanceOf(CoreException.class).hasMessageContaining("재고차감 이상");
+      });
     }
 
     @Test
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
index a3a300d5c..7cc9e69a0 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
@@ -39,15 +39,15 @@ class Join {
     @Test
     void 성공_회원가입() {
       // arrange
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
 
       // act
-      userService.join(userModel);
+      userService.join(user);
 
       // assert
       assertAll(
-          () -> verify(userJpaRepository, times(1)).save(userModel),
-          () -> assertThrows(CoreException.class, () -> userService.join(userModel))
+          () -> verify(userJpaRepository, times(1)).save(user),
+          () -> assertThrows(CoreException.class, () -> userService.join(user))
       );
     }
 
@@ -55,15 +55,15 @@ class Join {
     @Test
     void 실패_이미_가입된ID() {
       // arrange
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      userService.join(userModel);
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      userService.join(user);
 
       // act
-      verify(userJpaRepository, times(1)).save(userModel);
+      verify(userJpaRepository, times(1)).save(user);
       assertThatThrownBy(() -> {
-        userService.join(userModel);
+        userService.join(user);
       }).isInstanceOf(CoreException.class).hasMessageContaining("이미 가입된 ID 입니다.");
-      verify(userJpaRepository, times(1)).save(userModel);
+      verify(userJpaRepository, times(1)).save(user);
     }
   }
 
@@ -74,19 +74,19 @@ class Get {
     @Test
     void 성공_존재하는_유저ID() {
       // arrange
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      userService.join(userModel);
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      userService.join(user);
 
       // act
-      UserModel result = userService.getUser(userModel.getUserId());
+      User result = userService.getUser(user.getUserId());
 
       // assert
       assertAll(
           () -> assertThat(result).isNotNull(),
-          () -> assertThat(result.getUserId()).isEqualTo(userModel.getUserId()),
-          () -> assertThat(result.getEmail()).isEqualTo(userModel.getEmail()),
-          () -> assertThat(result.getBirthday()).isEqualTo(userModel.getBirthday()),
-          () -> assertThat(result.getGender()).isEqualTo(userModel.getGender())
+          () -> assertThat(result.getUserId()).isEqualTo(user.getUserId()),
+          () -> assertThat(result.getEmail()).isEqualTo(user.getEmail()),
+          () -> assertThat(result.getBirthday()).isEqualTo(user.getBirthday()),
+          () -> assertThat(result.getGender()).isEqualTo(user.getGender())
       );
     }
 
@@ -94,10 +94,10 @@ class Get {
     @Test
     void 실패_존재하지_않는_유저ID() {
       // arrange
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
 
       // act
-      UserModel result = userService.getUser(userModel.getUserId());
+      User result = userService.getUser(user.getUserId());
 
       // assert
       assertThat(result).isNull();
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java
similarity index 72%
rename from apps/commerce-api/src/test/java/com/loopers/domain/user/UserModelTest.java
rename to apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java
index 159dbdde4..91eb7b75f 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java
@@ -9,8 +9,8 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-class UserModelTest {
-  UserModel userModel;
+class UserTest {
+  User user;
   String validMsg = "";
 
   @DisplayName("회원가입-단위테스트1")
@@ -24,28 +24,28 @@ void setup() {
     @Test
     void 실패_ID_숫자만() {
       assertThatThrownBy(() -> {
-        userModel = UserModel.create("12345", "user1@test.XXX", "1999-01-01", "F");
+        user = User.create("12345", "user1@test.XXX", "1999-01-01", "F");
       }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
 
     @Test
     void 실패_ID_문자만() {
       assertThatThrownBy(() -> {
-        userModel = UserModel.create("hello", "user1@test.XXX", "1999-01-01", "F");
+        user = User.create("hello", "user1@test.XXX", "1999-01-01", "F");
       }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
 
     @Test
     void 실패_ID_특수문자() {
       assertThatThrownBy(() -> {
-        userModel = UserModel.create("12345!", "user1@test.XXX", "1999-01-01", "F");
+        user = User.create("12345!", "user1@test.XXX", "1999-01-01", "F");
       }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
 
     @Test
     void 실패_ID_10자이상오류() {
       assertThatThrownBy(() -> {
-        userModel = UserModel.create("12345678910", "user1@test.XXX", "1999-01-01", "F");
+        user = User.create("12345678910", "user1@test.XXX", "1999-01-01", "F");
       }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
   }
@@ -61,14 +61,14 @@ void setup() {
     @Test
     void 실패_이메일_기호없음() {
       assertThatThrownBy(() -> {
-        userModel = UserModel.create("user1", "user1test.XXX", "1999-01-01", "F");
+        user = User.create("user1", "user1test.XXX", "1999-01-01", "F");
       }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
 
     @Test
     void 실패_이메일_한글포함() {
       assertThatThrownBy(() -> {
-        userModel = UserModel.create("user1", "ㄱuser1@test.XXX", "1999-01-01", "F");
+        user = User.create("user1", "ㄱuser1@test.XXX", "1999-01-01", "F");
       }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
   }
@@ -85,22 +85,22 @@ void setup() {
     @Test
     void 실패_생년월일_형식오류() {
       assertThatThrownBy(() -> {
-        userModel = UserModel.create("user1", "user1@test.XXX", "19990101", "F");
+        user = User.create("user1", "user1@test.XXX", "19990101", "F");
       }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
 
     @Test
     void 실패_생년월일_날짜오류() {
       assertThatThrownBy(() -> {
-        userModel = UserModel.create("user1", "user1@test.XXX", "1999-13-01", "F");
+        user = User.create("user1", "user1@test.XXX", "1999-13-01", "F");
       }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
   }
 
   @Test
   void 성공_User_객체생성() {
-    userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
-    assertThat(userModel).isNotNull();
-    assertThat(userModel.getUserId()).isEqualTo("user1");
+    user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    assertThat(user).isNotNull();
+    assertThat(user.getUserId()).isEqualTo("user1");
   }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
index 4914412f7..d1730a77f 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
@@ -1,6 +1,7 @@
 package com.loopers.interfaces.api;
 
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
 import com.loopers.infrastructure.user.UserJpaRepository;
 import com.loopers.utils.DatabaseCleanUp;
 import org.junit.jupiter.api.AfterEach;
@@ -22,17 +23,17 @@
 public class PointV1ApiE2ETest {
 
   private final TestRestTemplate testRestTemplate;
-  private final UserJpaRepository userJpaRepository;
+  private final UserService userService;
   private final DatabaseCleanUp databaseCleanUp;
 
   @Autowired
   public PointV1ApiE2ETest(
       TestRestTemplate testRestTemplate,
-      UserJpaRepository userJpaRepository,
+      UserService userService,
       DatabaseCleanUp databaseCleanUp
   ) {
     this.testRestTemplate = testRestTemplate;
-    this.userJpaRepository = userJpaRepository;
+    this.userService = userService;
     this.databaseCleanUp = databaseCleanUp;
   }
 
@@ -50,12 +51,12 @@ class Get {
       //given
       BigDecimal JOIN_POINT = BigDecimal.TEN;
 
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      userJpaRepository.save(userModel);
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User savedUser = userService.join(user);
 
       //when
       HttpHeaders headers = new HttpHeaders();
-      headers.set("X-USER-ID", userModel.getUserId());
+      headers.set("X-USER-ID", savedUser.getId().toString());
 
       String url = "/api/v1/user/point";
       ParameterizedTypeReference<ApiResponse<BigDecimal>> resType = new ParameterizedTypeReference<>() {
@@ -93,11 +94,11 @@ class Charge {
       //given
       BigDecimal CHARGE_POINT = BigDecimal.TEN;
 
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      userJpaRepository.save(userModel);
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User savedUser = userService.join(user);
 
       HttpHeaders headers = new HttpHeaders();
-      headers.set("X-USER-ID", userModel.getUserId());
+      headers.set("X-USER-ID", savedUser.getId().toString());
 
       //when
       String url = "/api/v1/user/point/charge";
@@ -116,7 +117,7 @@ class Charge {
     void 실패_ID없음_400() {
       //given
       HttpHeaders headers = new HttpHeaders();
-      headers.set("X-USER-ID", "user1");
+      headers.set("X-USER-ID", "2");
 
       //when
       String url = "/api/v1/user/point/charge";
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
index 6c5952457..21fcb759e 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
@@ -1,9 +1,8 @@
 package com.loopers.interfaces.api;
 
-import com.loopers.domain.user.UserModel;
+import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserService;
 import com.loopers.interfaces.api.user.UserCreateV1Dto;
-import com.loopers.support.error.CoreException;
 import com.loopers.utils.DatabaseCleanUp;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.DisplayName;
@@ -93,11 +92,11 @@ class Get {
     @Test
     void 성공_정보조회() {
       //given
-      UserModel userModel = UserModel.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      userService.join(userModel);
+      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      userService.join(user);
 
       //when
-      String url = ENDPOINT_GET.apply(userModel.getUserId());
+      String url = ENDPOINT_GET.apply(user.getUserId());
       ParameterizedTypeReference<ApiResponse<UserCreateV1Dto.UserResponse>> resType = new ParameterizedTypeReference<>() {
       };
       ResponseEntity<ApiResponse<UserCreateV1Dto.UserResponse>> res = testRestTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null), resType);
@@ -105,7 +104,7 @@ class Get {
       //then
       assertThat(res.getStatusCode()).isEqualTo(HttpStatus.OK);
       assertThat(res.getBody().data().userId()).isNotNull();
-      assertThat(res.getBody().data().userId()).isEqualTo(userModel.getUserId());
+      assertThat(res.getBody().data().userId()).isEqualTo(user.getUserId());
     }
 
     @DisplayName("E2E테스트2")

From 9bbe6841f2dd44a4d17da9d33439128ab87d7cb7 Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Fri, 14 Nov 2025 14:16:17 +0900
Subject: [PATCH 03/11] =?UTF-8?q?feat:=20OrderFacade=20=EC=A3=BC=EB=AC=B8?=
 =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20?=
 =?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../loopers/application/order/OrderFacade.java | 10 ++++++----
 .../application/product/ProductFacade.java     |  3 +++
 .../domain/order/CreateOrderService.java       |  1 +
 .../java/com/loopers/domain/point/Point.java   |  2 +-
 .../loopers/domain/product/ProductService.java |  3 +++
 .../domain/product/ProductStockService.java    |  1 +
 .../loopers/domain/user/UserPointService.java  |  5 +++--
 .../order/OrderFacadeIntegrationTest.java      | 18 +++++++++---------
 .../product/ProductFacadeIntegrationTest.java  |  8 ++++----
 .../com/loopers/domain/point/PointTest.java    |  2 +-
 10 files changed, 32 insertions(+), 21 deletions(-)

diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
index 63fcc98bc..b6e1cd950 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
@@ -10,10 +10,10 @@
 import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserPointService;
 import com.loopers.domain.user.UserService;
-import jakarta.transaction.Transactional;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 import java.util.Map;
@@ -30,13 +30,15 @@ public class OrderFacade {
   private final ProductStockService productStockService;
   private final UserPointService userPointService;
 
+  @Transactional(readOnly = true)
   public Page<Order> getOrderList(Long userId,
                                   String sortType,
                                   int page,
                                   int size) {
     return orderService.getOrders(userId, sortType, page, size);
   }
-
+  
+  @Transactional(readOnly = true)
   public OrderInfo getOrderDetail(Long orderId) {
     Order order = orderService.getOrder(orderId);
     return OrderInfo.from(order);
@@ -48,11 +50,11 @@ public OrderInfo createOrder(CreateOrderCommand command) {
     Map<Long, Long> quantityMap = command.orderItemInfo();
     User user = userService.getActiveUser(command.userId());
     List<Product> productList = productService.getExistingProducts(quantityMap.keySet());
-    Order savedOrder = null;
+
     productStockService.deduct(productList, quantityMap);
     userPointService.use(user, productList, quantityMap);
 
-    savedOrder = createOrderService.save(user, productList, quantityMap);
+    Order savedOrder = createOrderService.save(user, productList, quantityMap);
     return OrderInfo.from(savedOrder);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
index b19585419..2a9a97eff 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
@@ -6,6 +6,7 @@
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 
 @RequiredArgsConstructor
 @Component
@@ -13,6 +14,7 @@ public class ProductFacade {
   private final ProductService productService;
   private final LikeService likeService;
 
+  @Transactional(readOnly = true)
   public Page<Product> getProductList(Long brandId,
                                       String sortType,
                                       int page,
@@ -20,6 +22,7 @@ public Page<Product> getProductList(Long brandId,
     return productService.getProducts(brandId, sortType, page, size);
   }
 
+  @Transactional(readOnly = true)
   public ProductDetailInfo getProductDetail(long userId, long productId) {
     Product product = productService.getProduct(productId);
     boolean isLiked = likeService.isLiked(userId, productId);
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
index 78cd49740..4780d71b3 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
@@ -6,6 +6,7 @@
 import com.loopers.domain.user.User;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
index 0264af717..84ecc6696 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
@@ -54,7 +54,7 @@ public void use(BigDecimal amountToChange) {
       throw new CoreException(ErrorType.BAD_REQUEST, "차감 금액은 0보다 커야 합니다.");
     }
     if (this.amount.compareTo(amountToChange) < 0) {
-      throw new CoreException(ErrorType.INSUFFICIENT_POINT, "포인트이가 부족합니다.");
+      throw new CoreException(ErrorType.INSUFFICIENT_POINT, "포인트가 부족합니다.");
     }
     this.amount = this.amount.subtract(amountToChange);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
index ef8fb2cd7..15a98b75c 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
@@ -22,6 +22,7 @@ public class ProductService {
   private final ProductRepository productRepository;
   private final LikeRepository likeRepository;
 
+  @Transactional(readOnly = true)
   public Page<Product> getProducts(
       Long brandId,
       String sortType,
@@ -71,10 +72,12 @@ public List<Product> getExistingProducts(Set<Long> productIds) {
     return products;
   }
 
+  @Transactional
   public Product save(Product product) {
     return productRepository.save(product);
   }
 
+  @Transactional
   public List<Product> save(List<Product> product) {
     return productRepository.save(product);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
index 075b25d6e..800226bbc 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
@@ -4,6 +4,7 @@
 import com.loopers.domain.order.OrderPreparer;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 import java.util.Map;
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
index 467bbcd63..c29759ccf 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
@@ -2,6 +2,8 @@
 package com.loopers.domain.user;
 
 import com.loopers.domain.order.OrderPreparer;
+import com.loopers.domain.point.Point;
+import com.loopers.domain.point.PointRepository;
 import com.loopers.domain.point.PointService;
 import com.loopers.domain.product.Product;
 import lombok.RequiredArgsConstructor;
@@ -18,9 +20,8 @@ public class UserPointService {
 
   private final PointService pointService;
 
-  @Transactional
-  public void use(User user, List<Product> productList, Map<Long, Long> quantityMap) {
 
+  public void use(User user, List<Product> productList, Map<Long, Long> quantityMap) {
     BigDecimal totalAmt = OrderPreparer.getTotalAmt(user.getPoint(), productList, quantityMap);
     pointService.use(user, totalAmt);
   }
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
index cf9123d78..a2121e2ef 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
@@ -9,7 +9,6 @@
 import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.order.OrderJpaRepository;
-import com.loopers.infrastructure.product.ProductJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
 import com.loopers.interfaces.api.order.OrderCreateV1Dto;
 import com.loopers.support.error.CoreException;
@@ -20,11 +19,9 @@
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.data.domain.Page;
 import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
-import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -32,7 +29,6 @@
 
 
 @SpringBootTest
-@Transactional
 class OrderFacadeIntegrationTest {
   @Autowired
   private OrderFacade orderFacade;
@@ -67,7 +63,7 @@ void setup() {
     );
     savedProducts = productService.save(productList);
     List<OrderItem> orderItems = new ArrayList<>();
-    orderItems.add(OrderItem.create(user.getId(), 2L, new BigDecimal(5_000)));
+    orderItems.add(OrderItem.create(productList.get(0).getId(), 2L, new BigDecimal(5_000)));
     Order order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), orderItems);
     savedOrder = orderJpaRepository.save(order);
 
@@ -104,8 +100,8 @@ class Get {
 
       // assert
       assertThat(result.id()).isEqualTo(savedOrder.getId());
-      assertThat(result.status().toString()).isEqualTo(savedOrder.getStatus().toString());
-      assertThat(result.totalPrice()).isEqualTo(savedOrder.getTotalPrice());
+      assertThat(result.status()).isEqualTo(savedOrder.getStatus().toString());
+      assertThat(result.totalPrice()).isEqualByComparingTo(savedOrder.getTotalPrice());
     }
 
     @DisplayName("존재하지 않는 상품 ID를 주면, 예외가 반환된다.")
@@ -134,7 +130,7 @@ class Post {
       OrderInfo savedOrder = orderFacade.createOrder(orderCommand);
       // assert
       assertThat(savedOrder).isNotNull();
-      assertThat(savedOrder.totalPrice()).isEqualTo(savedProducts.get(0).getPrice());
+      assertThat(savedOrder.totalPrice()).isEqualByComparingTo(savedProducts.get(0).getPrice());
       assertThat(savedOrder.orderItemInfo()).hasSize(1);
     }
 
@@ -161,7 +157,11 @@ class Post {
       CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUser.getId(), request);
       // act
       // assert
-      assertThrows(CoreException.class, () -> orderFacade.createOrder(orderCommand)).getErrorType().equals(ErrorType.INSUFFICIENT_POINT);
+      CoreException actualException = assertThrows(CoreException.class,
+          () -> orderFacade.createOrder(orderCommand));
+      assertThat(actualException.getErrorType()).isEqualTo(ErrorType.INSUFFICIENT_POINT);
+      Product deductedProduct = productService.getProduct(productId);
+      assertThat(deductedProduct.getStock()).isEqualTo(10);
     }
   }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
index d958c1b6d..f31a3d8f2 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
@@ -2,6 +2,7 @@
 
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductService;
 import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
@@ -25,7 +26,6 @@
 
 
 @SpringBootTest
-@Transactional
 class ProductFacadeIntegrationTest {
   @Autowired
   private ProductFacade productFacade;
@@ -34,7 +34,7 @@ class ProductFacadeIntegrationTest {
   @MockitoSpyBean
   private BrandJpaRepository brandJpaRepository;
   @MockitoSpyBean
-  private ProductJpaRepository productJpaRepository;
+  private ProductService productService;
 
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
@@ -55,7 +55,7 @@ void setup() {
         , Product.create(savedBrandList.get(0), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
         , Product.create(savedBrandList.get(1), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
     );
-    savedProducts = productList.stream().map((product) -> productJpaRepository.save(product)).toList();
+    savedProducts = productService.save(productList);
 
   }
 
@@ -104,7 +104,7 @@ class Get {
       ProductDetailInfo result = productFacade.getProductDetail(savedUser.getId(), savedProducts.get(0).getId());
 
       // assert
-      assertThat(result.brandInfo().name()).isEqualTo(savedProducts.get(0).getBrand().getName());
+      assertThat(result.name()).isEqualTo(savedProducts.get(0).getName());
     }
 
     @DisplayName("존재하지 않는 상품 ID를 주면, 예외가 반환된다.")
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
index 345e7f0e6..29d6b647a 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
@@ -60,7 +60,7 @@ class Use {
       // act, assert
       assertThatThrownBy(() -> {
         point.use(BigDecimal.TEN);
-      }).isInstanceOf(CoreException.class).hasMessageContaining("잔액이 부족합니다.");
+      }).isInstanceOf(CoreException.class).hasMessageContaining("포인트가 부족합니다.");
     }
 
     @Test

From d2e51d306ab9f3169d1462085565003e31b5b201 Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Sun, 16 Nov 2025 01:12:25 +0900
Subject: [PATCH 04/11] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=EB=9E=98?=
 =?UTF-8?q?=EB=B9=97=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/brand/BrandFacade.java        |  2 +-
 .../loopers/application/brand/BrandInfo.java  |  2 +-
 .../application/order/OrderFacade.java        |  2 -
 .../loopers/application/order/OrderInfo.java  |  2 +-
 .../application/order/OrderItemInfo.java      |  4 +-
 .../loopers/application/point/PointInfo.java  |  2 +-
 .../product/ProductDetailInfo.java            |  2 +-
 .../application/product/ProductFacade.java    |  3 +-
 .../loopers/application/user/UserInfo.java    |  2 +-
 .../java/com/loopers/domain/brand/Brand.java  |  6 +-
 .../loopers/domain/brand/BrandService.java    |  1 +
 .../java/com/loopers/domain/like/Like.java    |  4 +-
 .../domain/order/CreateOrderService.java      | 12 +--
 .../loopers/domain/order/CurrencyCode.java    |  8 ++
 .../java/com/loopers/domain/order/Money.java  | 79 ++++++++++++++++
 .../java/com/loopers/domain/order/Order.java  | 10 +-
 .../com/loopers/domain/order/OrderItem.java   | 41 ++++----
 .../loopers/domain/order/OrderPreparer.java   | 15 ++-
 .../loopers/domain/order/OrderService.java    |  6 --
 .../com/loopers/domain/order/OrderStatus.java |  2 +-
 .../java/com/loopers/domain/point/Point.java  |  1 +
 .../loopers/domain/point/PointRepository.java |  2 +-
 .../com/loopers/domain/product/Product.java   | 12 +--
 .../domain/product/ProductRepository.java     |  2 +-
 .../domain/product/ProductService.java        | 14 ++-
 .../domain/product/ProductStockService.java   |  7 +-
 .../java/com/loopers/domain/user/User.java    |  8 +-
 .../loopers/domain/user/UserPointService.java |  5 +-
 .../loopers/domain/user/UserRepository.java   |  5 +-
 .../com/loopers/domain/user/UserService.java  | 12 +--
 .../brand/BrandJpaRepository.java             |  5 -
 .../order/OrderJpaRepository.java             |  4 -
 .../point/PointJpaRepository.java             |  3 +-
 .../point/PointRepositoryImpl.java            |  4 +-
 .../product/ProductJpaRepository.java         |  7 +-
 .../user/UserJpaRepository.java               |  5 +-
 .../user/UserRepositoryImpl.java              | 12 +--
 .../api/order/OrderCreateV1Dto.java           |  3 +-
 .../interfaces/api/order/OrderV1ApiSpec.java  |  7 +-
 .../com/loopers/support/error/ErrorType.java  |  8 +-
 .../order/OrderFacadeIntegrationTest.java     | 20 ++--
 .../product/ProductFacadeIntegrationTest.java | 11 ++-
 .../loopers/domain/brand/BrandModelTest.java  |  5 +-
 .../like/LikeServiceIntegrationTest.java      |  7 +-
 .../loopers/domain/like/LikeServiceTest.java  | 10 +-
 .../loopers/domain/order/OrderAssertions.java |  2 +-
 .../domain/order/OrderItemModelTest.java      | 51 ++++++++++
 .../loopers/domain/order/OrderModelTest.java  | 23 +++--
 .../order/OrderServiceIntegrationTest.java    | 94 ++++++++++++-------
 .../domain/product/ProductAssertions.java     |  7 +-
 .../domain/product/ProductModelTest.java      | 29 +++---
 .../ProductServiceIntegrationTest.java        | 11 ++-
 .../user/UserServiceIntegrationTest.java      |  9 +-
 .../com/loopers/domain/user/UserTest.java     |  2 +-
 .../interfaces/api/PointV1ApiE2ETest.java     |  2 +-
 .../interfaces/api/UserV1ApiE2ETest.java      |  4 +-
 56 files changed, 378 insertions(+), 240 deletions(-)
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/CurrencyCode.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/Money.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemModelTest.java

diff --git a/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
index c0e4d5da9..57fcb4d5b 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
@@ -10,7 +10,7 @@
 public class BrandFacade {
   private final BrandService brandService;
 
-  public BrandInfo getBrandDetail(long id) {
+  public BrandInfo getBrandDetail(Long id) {
     Brand brand = brandService.getBrand(id);
     return BrandInfo.from(brand);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
index 5a2ae251c..211f684a3 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
@@ -6,7 +6,7 @@
 
 public record BrandInfo(Long id, String name, String story) {
   public static BrandInfo from(Brand model) {
-    if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "유저정보를 찾을수 없습니다.");
+    if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "브랜드 정보를 찾을 수 없습니다.");
     return new BrandInfo(
         model.getId(),
         model.getName(),
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
index b6e1cd950..8b45690bf 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
@@ -3,7 +3,6 @@
 import com.loopers.domain.order.CreateOrderService;
 import com.loopers.domain.order.Order;
 import com.loopers.domain.order.OrderService;
-import com.loopers.domain.point.PointService;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
 import com.loopers.domain.product.ProductStockService;
@@ -22,7 +21,6 @@
 @Component
 public class OrderFacade {
   private final UserService userService;
-  private final PointService pointService;
   private final ProductService productService;
   private final OrderService orderService;
   private final CreateOrderService createOrderService;
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
index 95d20d92b..883d3ec00 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
@@ -15,7 +15,7 @@ public static OrderInfo from(Order model) {
     return new OrderInfo(
         model.getId(),
         model.getStatus().name(),
-        model.getTotalPrice(),
+        model.getTotalPrice().getAmount(),
         model.getOrderAt(),
         OrderItemInfo.from(model.getOrderItems())
     );
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderItemInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderItemInfo.java
index 92a66aa2f..841e6e88a 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderItemInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderItemInfo.java
@@ -17,8 +17,8 @@ public static List<OrderItemInfo> from(List<OrderItem> model) {
     return model.stream().map(item -> new OrderItemInfo(
         item.getId(),
         item.getQuantity(),
-        item.getUnitPrice(),
-        item.getTotalPrice()
+        item.getUnitPrice().getAmount(),
+        item.getTotalPrice().getAmount()
     )).toList();
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/point/PointInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/point/PointInfo.java
index ca76d1644..ed1f011e0 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/point/PointInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/point/PointInfo.java
@@ -8,7 +8,7 @@ public record PointInfo(String userId, BigDecimal amount) {
   public static PointInfo from(Point model) {
     if (model == null) return null;
     return new PointInfo(
-        model.getUser().getUserId(),
+        model.getUser().getLoginId(),
         model.getAmount()
     );
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
index fadaa5cc8..ed39408d9 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
@@ -15,7 +15,7 @@ public static ProductDetailInfo from(Product model, boolean isLiked) {
     return new ProductDetailInfo(
         model.getId(),
         model.getName(),
-        model.getPrice(),
+        model.getPrice().getAmount(),
         model.getStock(),
         BrandInfo.from(model.getBrand()),
         LikeInfo.from(model.getLikeCount(), isLiked)
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
index 2a9a97eff..a0108db28 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
@@ -24,7 +24,8 @@ public Page<Product> getProductList(Long brandId,
 
   @Transactional(readOnly = true)
   public ProductDetailInfo getProductDetail(long userId, long productId) {
-    Product product = productService.getProduct(productId);
+    Product product = productService.getExistingProduct(productId);
+
     boolean isLiked = likeService.isLiked(userId, productId);
     return ProductDetailInfo.from(product, isLiked);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/user/UserInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/user/UserInfo.java
index 16b11be7d..33997ebcd 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/user/UserInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/user/UserInfo.java
@@ -8,7 +8,7 @@ public record UserInfo(String userId, String email, String birthday, String gend
   public static UserInfo from(User model) {
     if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "유저정보를 찾을수 없습니다.");
     return new UserInfo(
-        model.getUserId(),
+        model.getLoginId(),
         model.getEmail(),
         model.getBirthday().toString(),
         model.getGender()
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java
index 39067ec5a..09a7ac785 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java
@@ -1,6 +1,8 @@
 package com.loopers.domain.brand;
 
 import com.loopers.domain.BaseEntity;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
 import jakarta.persistence.Entity;
 import jakarta.persistence.Table;
 import lombok.Getter;
@@ -23,8 +25,8 @@ private Brand(String name, String story) {
   }
 
   public static Brand create(String name, String story) {
-    if (name.isBlank()) {
-      throw new IllegalArgumentException("브랜드명은 비어있을 수 없습니다.");
+    if (name == null || name.isBlank()) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "브랜드명은 비어있을 수 없습니다.");
     }
     return new Brand(name, story);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
index 0e05dead6..6f7bc22d1 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
@@ -20,6 +20,7 @@ public Brand getBrand(Long id) {
     return brandRepository.findById(id).orElse(null);
   }
 
+  @Transactional
   public Brand save(Brand brand) {
     return brandRepository.save(brand);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
index 7015ee53d..6351c086c 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
@@ -15,11 +15,11 @@
 @Getter
 public class Like extends BaseEntity {
   @ManyToOne(fetch = FetchType.LAZY)
-  @JoinColumn(name = "ref_user_Id", nullable = false)
+  @JoinColumn(name = "ref_user_id", nullable = false)
   private User user;
 
   @ManyToOne(fetch = FetchType.LAZY)
-  @JoinColumn(name = "ref_product_Id", nullable = false)
+  @JoinColumn(name = "ref_product_id", nullable = false)
   private Product product;
 
   protected Like() {
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
index 4780d71b3..e385ef9fd 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
@@ -6,9 +6,7 @@
 import com.loopers.domain.user.User;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
 
-import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -23,16 +21,16 @@ public Order save(User user, List<Product> productList, Map<Long, Long> quantity
 
     List<OrderItem> orderItems = new ArrayList<>();
 
-    BigDecimal totalPrice = productList.stream().map(product -> {
-      long quantity = quantityMap.get(product.getId());
-      BigDecimal unitPrice = product.getPrice();
-      BigDecimal itemTotalPrice = unitPrice.multiply(new BigDecimal(quantity));
+    Money totalPrice = productList.stream().map(product -> {
+      long quantity = quantityMap.getOrDefault(product.getId(), 0L);
+      Money unitPrice = product.getPrice();
+      Money itemTotalPrice = unitPrice.multiply(quantity);
 
       OrderItem orderItem = OrderItem.create(product.getId(), quantity, unitPrice);
       orderItems.add(orderItem);
 
       return itemTotalPrice;
-    }).reduce(BigDecimal.ZERO, BigDecimal::add);
+    }).reduce(Money.wons(0), Money::add);
 
     Order order = Order.create(user.getId(), OrderStatus.PAID, totalPrice, orderItems);
     return orderRepository.save(order);
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/CurrencyCode.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/CurrencyCode.java
new file mode 100644
index 000000000..01ba637ce
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/CurrencyCode.java
@@ -0,0 +1,8 @@
+package com.loopers.domain.order;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum CurrencyCode {USD, EUR, GBP, KRW}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/Money.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/Money.java
new file mode 100644
index 000000000..0e3247ff1
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/Money.java
@@ -0,0 +1,79 @@
+package com.loopers.domain.order;
+
+import jakarta.persistence.Embeddable;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+
+@Embeddable
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Money {
+  private static final BigDecimal ZERO = BigDecimal.ZERO;
+
+  private BigDecimal amount;
+  @Enumerated(EnumType.STRING)
+  private CurrencyCode currency;
+
+  public Money(BigDecimal amount, CurrencyCode currency) {
+    // 💡 음수 검증
+    if (amount.compareTo(BigDecimal.ZERO) < 0) {
+      throw new IllegalArgumentException("금액은 음수가 될 수 없습니다.");
+    }
+
+    this.amount = amount;
+    this.currency = currency;
+  }
+
+  public Money add(Money other) {
+    if (!this.currency.equals(other.currency)) {
+      throw new IllegalArgumentException("통화가 다릅니다.");
+    }
+    return new Money(this.amount.add(other.amount), this.currency);
+  }
+
+  public Money subtract(Money other) {
+    if (!this.currency.equals(other.currency)) {
+      throw new IllegalArgumentException("통화가 다릅니다.");
+    }
+    return new Money(this.amount.subtract(other.amount), this.currency);
+  }
+
+  public Money multiply(long multiplier) {
+    BigDecimal multiplierDecimal = BigDecimal.valueOf(multiplier);
+    BigDecimal newAmount = this.amount.multiply(multiplierDecimal);
+    return new Money(newAmount, this.currency);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    Money money = (Money) o;
+    return Objects.equals(currency, money.currency) &&
+        amount.compareTo(money.amount) == 0;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(amount.stripTrailingZeros(), currency);
+  }
+
+  public boolean isZero() {
+    return this.amount.compareTo(ZERO) == 0;
+  }
+
+  public static Money wons(long amount) {
+    BigDecimal amountDecimal = BigDecimal.valueOf(amount);
+    return new Money(amountDecimal, CurrencyCode.KRW);
+  }
+
+  public static Money of(BigDecimal amount, CurrencyCode currencyCode) {
+    return new Money(amount, currencyCode);
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
index 157f231b7..8749bcc1b 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
@@ -6,7 +6,6 @@
 import jakarta.persistence.*;
 import lombok.Getter;
 
-import java.math.BigDecimal;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.List;
@@ -20,7 +19,7 @@ public class Order extends BaseEntity {
 
   private OrderStatus status;
 
-  BigDecimal totalPrice;
+  Money totalPrice;
 
   ZonedDateTime orderAt;
 
@@ -38,7 +37,7 @@ public void setOrderItems(List<OrderItem> orderItems) {
   protected Order() {
   }
 
-  private Order(long refUserId, OrderStatus status, BigDecimal totalPrice, List<OrderItem> orderItems) {
+  private Order(long refUserId, OrderStatus status, Money totalPrice, List<OrderItem> orderItems) {
     this.refUserId = refUserId;
     this.status = status;
     this.totalPrice = totalPrice;
@@ -46,13 +45,10 @@ private Order(long refUserId, OrderStatus status, BigDecimal totalPrice, List<Or
     this.orderItems = orderItems;
   }
 
-  public static Order create(long refUserId, OrderStatus status, BigDecimal totalPrice, List<OrderItem> orderItems) {
+  public static Order create(long refUserId, OrderStatus status, Money totalPrice, List<OrderItem> orderItems) {
     if (status == null) {
       throw new CoreException(ErrorType.BAD_REQUEST, "상태 정보는 비어있을 수 없습니다.");
     }
-    if (totalPrice.compareTo(BigDecimal.ZERO) < 0) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "총가격은 음수일수 없습니다.");
-    }
     return new Order(refUserId, status, totalPrice, orderItems);
   }
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
index 17c324510..ed4aa292e 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
@@ -1,14 +1,9 @@
 package com.loopers.domain.order;
 
 import com.loopers.domain.BaseEntity;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Table;
+import jakarta.persistence.*;
 import lombok.Getter;
 
-import java.math.BigDecimal;
-
 @Entity
 @Table(name = "order_item")
 @Getter
@@ -17,26 +12,40 @@ public class OrderItem extends BaseEntity {
   private long refProductId;
 
   private long quantity;
-
-  private BigDecimal unitPrice;
-
-  private BigDecimal totalPrice;
+  @Embedded
+  @AttributeOverride(
+      name = "amount",
+      column = @Column(name = "unit_price_amount")
+  )
+  @AttributeOverride(
+      name = "currency",
+      column = @Column(name = "unit_price_currency")
+  )
+  private Money unitPrice;
+
+  @Embedded
+  @AttributeOverride(
+      name = "amount",
+      column = @Column(name = "total_price_amount")
+  )
+  @AttributeOverride(
+      name = "currency",
+      column = @Column(name = "total_price_currency")
+  )
+  private Money totalPrice;
 
   protected OrderItem() {
   }
 
-  private OrderItem(long refProductId, long quantity, BigDecimal unitPrice, BigDecimal totalPrice) {
+  private OrderItem(long refProductId, long quantity, Money unitPrice, Money totalPrice) {
     this.refProductId = refProductId;
     this.quantity = quantity;
     this.unitPrice = unitPrice;
     this.totalPrice = totalPrice;
   }
 
-  public static OrderItem create(long refProductId, long quantity, BigDecimal unitPrice) {
-    if (unitPrice.compareTo(BigDecimal.ZERO) < 0) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "가격은 음수일수 없습니다.");
-    }
-    BigDecimal totalPrice = unitPrice.multiply(new BigDecimal(quantity));
+  public static OrderItem create(long refProductId, long quantity, Money unitPrice) {
+    Money totalPrice = unitPrice.multiply(quantity);
     return new OrderItem(refProductId, quantity, unitPrice, totalPrice);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java
index 6a4a29d5a..7aa9f8a7b 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java
@@ -1,33 +1,30 @@
 
 package com.loopers.domain.order;
 
-import com.loopers.domain.point.Point;
 import com.loopers.domain.product.Product;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
 
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
-@RequiredArgsConstructor
-@Component
-public class OrderPreparer {
+
+public final class OrderPreparer {
   public static List<Product> verifyProductStockDeductable(List<Product> products, Map<Long, Long> quantityMap) {
-    products.forEach(i -> i.deductStock(quantityMap.get(i.getId())));
+    products.forEach(i -> i.deductStock(quantityMap.getOrDefault(i.getId(), 0L)));
     return products;
   }
+
   public static List<Product> verifyProductStockAddable(List<Product> products, Map<Long, Long> quantityMap) {
     products.forEach(i -> i.addStock(quantityMap.get(i.getId())));
     return products;
   }
 
-  public static BigDecimal getTotalAmt(Point point, List<Product> products, Map<Long, Long> quantityMap) {
+  public static BigDecimal getTotalAmt(List<Product> products, Map<Long, Long> quantityMap) {
     return products.stream()
         .map(product -> {
           Long productId = product.getId();
           Long quantity = quantityMap.getOrDefault(productId, 0L);
-          return product.getPrice().multiply(BigDecimal.valueOf(quantity));
+          return product.getPrice().getAmount().multiply(BigDecimal.valueOf(quantity));
         })
         .reduce(BigDecimal.ZERO, BigDecimal::add);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
index e02487b7b..c832fcb8a 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
@@ -1,8 +1,6 @@
 
 package com.loopers.domain.order;
 
-import com.loopers.application.order.CreateOrderCommand;
-import com.loopers.domain.product.ProductRepository;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 import lombok.RequiredArgsConstructor;
@@ -13,13 +11,9 @@
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.math.BigDecimal;
-import java.util.List;
-
 @RequiredArgsConstructor
 @Component
 public class OrderService {
-  private final ProductRepository productRepository;
   private final OrderRepository orderRepository;
 
   public Page<Order> getOrders(
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
index 8dd95ef7e..4b7375384 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
@@ -21,7 +21,7 @@ public enum OrderStatus {
   @Getter(AccessLevel.NONE)
   private final int sequence;
 
-  public Integer compare(OrderStatus other) {
+  public int compare(OrderStatus other) {
     return Integer.compare(this.sequence, other.sequence);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
index 84ecc6696..aa683a5d0 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
@@ -21,6 +21,7 @@ public class Point extends BaseEntity {
   private User user;
 
   public void setUser(User user) {
+    Objects.requireNonNull(user, "유저 정보가 없습니다.");
     this.user = user;
     if (user.getPoint() != this) {
       user.setPoint(this);
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
index 0524bc7ac..263f963a3 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
@@ -8,7 +8,7 @@
 public interface PointRepository {
   Optional<Point> findByUserId(Long userId);
 
-  Optional<Point> findByUserId(String userId);
+  Optional<Point> findByUserLoginId(String userId);
 
   @Lock(LockModeType.PESSIMISTIC_WRITE)
   Optional<Point> findByUserIdForUpdate(Long userId);
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
index 0a536113f..3d99209b9 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
@@ -2,13 +2,12 @@
 
 import com.loopers.domain.BaseEntity;
 import com.loopers.domain.brand.Brand;
+import com.loopers.domain.order.Money;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 import jakarta.persistence.*;
 import lombok.Getter;
 
-import java.math.BigDecimal;
-
 @Entity
 @Table(name = "product")
 @Getter
@@ -19,7 +18,7 @@ public class Product extends BaseEntity {
   @JoinColumn(name = "ref_brand_Id")
   private Brand brand;
 
-  private BigDecimal price;
+  private Money price;
   private long stock;
 
   @Transient
@@ -37,17 +36,14 @@ protected Product() {
     this.stock = 0;
   }
 
-  private Product(Brand brand, String name, BigDecimal price, long stock) {
+  private Product(Brand brand, String name, Money price, long stock) {
     this.setBrand(brand);
     this.name = name;
     this.price = price;
     this.stock = stock;
   }
 
-  public static Product create(Brand brand, String name, BigDecimal price, long stock) {
-    if (price.compareTo(BigDecimal.ZERO) < 0) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "가격은 음수 일수 없습니다.");
-    }
+  public static Product create(Brand brand, String name, Money price, long stock) {
     if (stock < 0) {
       throw new CoreException(ErrorType.BAD_REQUEST, "재고는 0보다 커야 합니다.");
     }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
index ae1031751..55173cf39 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
@@ -10,7 +10,7 @@
 public interface ProductRepository {
   Optional<Product> findById(Long id);
 
-  List<Product> findAllById(Set<Long> id);
+  List<Product> findAllById(Set<Long> ids);
 
   Page<Product> findByBrandId(Long brandId, Pageable pageable);
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
index 15a98b75c..04f04a8e3 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
@@ -1,7 +1,6 @@
 
 package com.loopers.domain.product;
 
-import com.loopers.domain.like.LikeRepository;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 import lombok.RequiredArgsConstructor;
@@ -20,7 +19,6 @@
 public class ProductService {
 
   private final ProductRepository productRepository;
-  private final LikeRepository likeRepository;
 
   @Transactional(readOnly = true)
   public Page<Product> getProducts(
@@ -49,6 +47,18 @@ public Product getProduct(Long id) {
     return product.orElse(null);
   }
 
+  @Transactional(readOnly = true)
+  public Product getExistingProduct(Long id) {
+    if (id == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
+    }
+    Optional<Product> product = productRepository.findById(id);
+    if (product.isEmpty()) {
+      throw new CoreException(ErrorType.NOT_FOUND, "상품을 찾을 수 없습니다.");
+    }
+    return product.get();
+  }
+
   @Transactional(readOnly = true)
   public List<Product> getExistingProducts(Set<Long> productIds) {
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
index 800226bbc..bda2ed6e2 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
@@ -4,7 +4,6 @@
 import com.loopers.domain.order.OrderPreparer;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 import java.util.Map;
@@ -12,16 +11,16 @@
 @RequiredArgsConstructor
 @Component
 public class ProductStockService {
-  private final ProductRepository productService;
+  private final ProductRepository productRepository;
 
   public void deduct(List<Product> products, Map<Long, Long> quantityMap) {
     List<Product> deductedProducts = OrderPreparer.verifyProductStockDeductable(products, quantityMap);
-    productService.save(deductedProducts);
+    productRepository.save(deductedProducts);
   }
 
   public void add(List<Product> products, Map<Long, Long> quantityMap) {
     List<Product> addedProducts = OrderPreparer.verifyProductStockAddable(products, quantityMap);
-    productService.save(addedProducts);
+    productRepository.save(addedProducts);
   }
 
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
index 61c8167c0..55b863217 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
@@ -16,7 +16,7 @@
 @Table(name = "user")
 @Getter
 public class User extends BaseEntity {
-  private String userId;
+  private String loginId;
   private String email;
   private LocalDate birthday;
   private String gender;
@@ -34,9 +34,9 @@ public void setPoint(Point point) {
   protected User() {
   }
 
-  private User(String userId, String email, String birthday, String gender) {
+  private User(String loginId, String email, String birthday, String gender) {
 
-    if (!Pattern.compile("^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]{1,10}$").matcher(userId).matches()) {
+    if (!Pattern.compile("^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]{1,10}$").matcher(loginId).matches()) {
       throw new CoreException(
           ErrorType.BAD_REQUEST,
           "아이디 형식이 잘못되었습니다.(영문 및 숫자 1~10자 이내)"
@@ -63,7 +63,7 @@ private User(String userId, String email, String birthday, String gender) {
           "성별정보가 없습니다."
       );
     }
-    this.userId = userId;
+    this.loginId = loginId;
     this.email = email;
     this.birthday = LocalDate.parse(birthday);
     this.gender = gender;
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
index c29759ccf..7b064cb54 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
@@ -2,13 +2,10 @@
 package com.loopers.domain.user;
 
 import com.loopers.domain.order.OrderPreparer;
-import com.loopers.domain.point.Point;
-import com.loopers.domain.point.PointRepository;
 import com.loopers.domain.point.PointService;
 import com.loopers.domain.product.Product;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.util.List;
@@ -22,7 +19,7 @@ public class UserPointService {
 
 
   public void use(User user, List<Product> productList, Map<Long, Long> quantityMap) {
-    BigDecimal totalAmt = OrderPreparer.getTotalAmt(user.getPoint(), productList, quantityMap);
+    BigDecimal totalAmt = OrderPreparer.getTotalAmt(productList, quantityMap);
     pointService.use(user, totalAmt);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java
index 2ac424125..f94a61c38 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java
@@ -5,10 +5,7 @@
 public interface UserRepository {
   User save(User user);
 
-  boolean existsByUserId(String userId);
-
   Optional<User> findById(Long id);
 
-  Optional<User> findByUserId(String userId);
-
+  Optional<User> findByLoginId(String longinId);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
index 7481cf1cb..5ff80ad9c 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
@@ -15,7 +15,7 @@ public class UserService {
 
   @Transactional
   public User join(User user) {
-    if (userRepository.existsByUserId(user.getUserId())) {
+    if (userRepository.findByLoginId(user.getLoginId()).isPresent()) {
       throw new CoreException(ErrorType.BAD_REQUEST, "이미 가입된 ID 입니다.");
     }
     return userRepository.save(user);
@@ -24,17 +24,17 @@ public User join(User user) {
   @Transactional(readOnly = true)
   public User getUser(Long userId) {
     if (userId == null) {
-      throw new CoreException(ErrorType.NOT_FOUND, "ID가 없습니다.");
+      throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
     }
     return userRepository.findById(userId).orElse(null);
   }
 
   @Transactional(readOnly = true)
-  public User getUser(String userId) {
-    if (userId == null) {
-      throw new CoreException(ErrorType.NOT_FOUND, "ID가 없습니다.");
+  public User getUser(String loginId) {
+    if (loginId == null) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
     }
-    return userRepository.findByUserId(userId).orElse(null);
+    return userRepository.findByLoginId(loginId).orElse(null);
   }
 
   @Transactional(readOnly = true)
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
index 5ca589a5a..72daca8e5 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
@@ -3,10 +3,5 @@
 import com.loopers.domain.brand.Brand;
 import org.springframework.data.jpa.repository.JpaRepository;
 
-import java.util.Optional;
-
 public interface BrandJpaRepository extends JpaRepository<Brand, Long> {
-  Optional<Brand> findById(Long id);
-
-  Brand save(Brand brand);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java
index 2d9c05f07..68e85215e 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java
@@ -5,11 +5,7 @@
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 
-import java.util.Optional;
-
 public interface OrderJpaRepository extends JpaRepository<Order, Long> {
-  Optional<Order> findById(Long id);
-
   Page<Order> findByRefUserId(Long userId, Pageable pageable);
 
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
index a5a85c432..f7969fc95 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
@@ -8,9 +8,8 @@
 import java.util.Optional;
 
 public interface PointJpaRepository extends JpaRepository<Point, Long> {
-  boolean existsByUserUserId(String userId);
 
-  Optional<Point> findByUserUserId(String userId);
+  Optional<Point> findByUserLoginId(String userId);
 
   Optional<Point> findByUserId(Long userId);
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
index b9ef6bf0e..6329b5c74 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
@@ -23,8 +23,8 @@ public Optional<Point> findByUserId(Long userId) {
   }
 
   @Override
-  public Optional<Point> findByUserId(String userId) {
-    return jpaRepository.findByUserUserId(userId);
+  public Optional<Point> findByUserLoginId(String userId) {
+    return jpaRepository.findByUserLoginId(userId);
   }
 
   @Override
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
index e2119e400..2d3a34309 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
@@ -6,19 +6,14 @@
 import org.springframework.data.jpa.repository.JpaRepository;
 
 import java.util.List;
-import java.util.Optional;
 
 public interface ProductJpaRepository extends JpaRepository<Product, Long> {
-  Optional<Product> findById(Long id);
-
-  List<Product> findAllById(Iterable<Long> id);
-
   Page<Product> findByBrandId(Long brandId, Pageable pageable);
 
   List<Product> save(List<Product> product);
 
 //  @Query(value = """
-//          SELECT new com.loopers.api.product.dto.Product(
+//          SELECT new com.loopers.api.product.dto.ProductView(
 //              p,
 //              COUNT(l.id)
 //          )
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java
index 015978561..80175b640 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserJpaRepository.java
@@ -6,9 +6,6 @@
 import java.util.Optional;
 
 public interface UserJpaRepository extends JpaRepository<User, Long> {
-  boolean existsByUserId(String userId);
+  Optional<User> findByLoginId(String loginId);
 
-  Optional<User> findById(Long id);
-
-  Optional<User> findByUserId(String id);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
index e4e85f02d..326f5f3a9 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
@@ -16,19 +16,15 @@ public class UserRepositoryImpl implements UserRepository {
   public User save(User user) {
     return jpaRepository.save(user);
   }
-
-  @Override
-  public boolean existsByUserId(String userId) {
-    return jpaRepository.existsByUserId(userId);
-  }
-
+  
   @Override
   public Optional<User> findById(Long userId) {
     return jpaRepository.findById(userId);
   }
 
   @Override
-  public Optional<User> findByUserId(String userId) {
-    return jpaRepository.findByUserId(userId);
+  public Optional<User> findByLoginId(String loginId) {
+    return jpaRepository.findByLoginId(loginId);
   }
+
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java
index 0ef09e8b2..2fb536034 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderCreateV1Dto.java
@@ -5,13 +5,14 @@
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import com.loopers.application.order.OrderInfo;
 import com.loopers.application.order.OrderItemInfo;
+import jakarta.validation.constraints.Positive;
 
 import java.math.BigDecimal;
 import java.time.ZonedDateTime;
 import java.util.List;
 
 public class OrderCreateV1Dto {
-  public record OrderItemRequest(long productId, long quantity) {
+  public record OrderItemRequest(@Positive long productId, @Positive long quantity) {
   }
 
   public record OrderRequest(List<OrderItemRequest> items) {
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
index ddaf2bc04..9ec1bf2a8 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
@@ -12,9 +12,6 @@
 import org.springframework.web.bind.annotation.RequestHeader;
 import org.springframework.web.bind.annotation.RequestParam;
 
-import java.math.BigDecimal;
-import java.util.List;
-
 @Tag(name = "Order V1 API", description = "Loopers 예시 API 입니다.")
 public interface OrderV1ApiSpec {
   @Operation(
@@ -49,7 +46,7 @@ ApiResponse<OrderCreateV1Dto.OrderResponse> createOrder(
   @Valid
   ApiResponse<OrderCreateV1Dto.OrderResponse> getOrderDetail(
       @Schema(name = "사용자 ID", description = "조회할 사용자의 ID")
-      @RequestHeader(value = "X-USER-ID", required = false) Long userId
-      , Long orderId
+      @RequestHeader(value = "X-USER-ID", required = false) Long userId,
+      @PathVariable("orderId") Long orderId
   );
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/support/error/ErrorType.java b/apps/commerce-api/src/main/java/com/loopers/support/error/ErrorType.java
index 8bb2324d6..05141702b 100644
--- a/apps/commerce-api/src/main/java/com/loopers/support/error/ErrorType.java
+++ b/apps/commerce-api/src/main/java/com/loopers/support/error/ErrorType.java
@@ -10,10 +10,10 @@ public enum ErrorType {
   /**
    * 범용 에러
    */
-  INTERNAL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), "일시적인 오류가 발생했습니다."),
-  BAD_REQUEST(HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST.getReasonPhrase(), "잘못된 요청입니다."),
-  NOT_FOUND(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.getReasonPhrase(), "존재하지 않는 요청입니다."),
-  CONFLICT(HttpStatus.CONFLICT, HttpStatus.CONFLICT.getReasonPhrase(), "이미 존재하는 리소스입니다."),
+  INTERNAL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "INTERNAL_ERROR", "일시적인 오류가 발생했습니다."),
+  BAD_REQUEST(HttpStatus.BAD_REQUEST, "BAD_REQUEST", "잘못된 요청입니다."),
+  NOT_FOUND(HttpStatus.NOT_FOUND, "NOT_FOUND", "존재하지 않는 요청입니다."),
+  CONFLICT(HttpStatus.CONFLICT, "CONFLICT", "이미 존재하는 리소스입니다."),
 
   INSUFFICIENT_STOCK(HttpStatus.BAD_REQUEST, "INSUFFICIENT_STOCK", "요청하신 상품의 재고가 부족합니다."),
   INSUFFICIENT_POINT(HttpStatus.BAD_REQUEST, "INSUFFICIENT_POINT", "사용 가능한 포인트가 부족합니다.");
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
index a2121e2ef..b48df283e 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
@@ -1,6 +1,7 @@
 package com.loopers.application.order;
 
 import com.loopers.domain.brand.Brand;
+import com.loopers.domain.order.Money;
 import com.loopers.domain.order.Order;
 import com.loopers.domain.order.OrderItem;
 import com.loopers.domain.order.OrderStatus;
@@ -20,12 +21,11 @@
 import org.springframework.data.domain.Page;
 import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
 
-import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 
 @SpringBootTest
@@ -57,14 +57,14 @@ void setup() {
         , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
     List<Brand> savedBrandList = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
 
-    List<Product> productList = List.of(Product.create(savedBrandList.get(0), "Wild Faith Rose Sweatshirt", new BigDecimal(8), 10)
-        , Product.create(savedBrandList.get(0), "Flower Pattern Fleece Jacket", new BigDecimal(4), 10)
-        , Product.create(savedBrandList.get(1), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+    List<Product> productList = List.of(Product.create(savedBrandList.get(0), "Wild Faith Rose Sweatshirt", Money.wons(8), 10)
+        , Product.create(savedBrandList.get(0), "Flower Pattern Fleece Jacket", Money.wons(4), 10)
+        , Product.create(savedBrandList.get(1), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
     );
     savedProducts = productService.save(productList);
     List<OrderItem> orderItems = new ArrayList<>();
-    orderItems.add(OrderItem.create(productList.get(0).getId(), 2L, new BigDecimal(5_000)));
-    Order order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), orderItems);
+    orderItems.add(OrderItem.create(productList.get(0).getId(), 2L, Money.wons(5_000)));
+    Order order = Order.create(savedUser.getId(), OrderStatus.PENDING, Money.wons(10_000), orderItems);
     savedOrder = orderJpaRepository.save(order);
 
   }
@@ -77,7 +77,7 @@ void tearDown() {
   @DisplayName("주문목록을 조회할 때,")
   @Nested
   class GetList {
-    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @DisplayName("페이징 처리되어, 초기설정시 size=20, sort=최신순으로 목록이 조회된다.")
     @Test
     void 성공_주문목록조회() {
       // act
@@ -101,7 +101,7 @@ class Get {
       // assert
       assertThat(result.id()).isEqualTo(savedOrder.getId());
       assertThat(result.status()).isEqualTo(savedOrder.getStatus().toString());
-      assertThat(result.totalPrice()).isEqualByComparingTo(savedOrder.getTotalPrice());
+      assertThat(result.totalPrice()).isEqualByComparingTo(savedOrder.getTotalPrice().getAmount());
     }
 
     @DisplayName("존재하지 않는 상품 ID를 주면, 예외가 반환된다.")
@@ -130,7 +130,7 @@ class Post {
       OrderInfo savedOrder = orderFacade.createOrder(orderCommand);
       // assert
       assertThat(savedOrder).isNotNull();
-      assertThat(savedOrder.totalPrice()).isEqualByComparingTo(savedProducts.get(0).getPrice());
+      assertThat(savedOrder.totalPrice()).isEqualByComparingTo(savedProducts.get(0).getPrice().getAmount());
       assertThat(savedOrder.orderItemInfo()).hasSize(1);
     }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
index f31a3d8f2..f6175d493 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
@@ -1,6 +1,7 @@
 package com.loopers.application.product;
 
 import com.loopers.domain.brand.Brand;
+import com.loopers.domain.order.Money;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
 import com.loopers.domain.user.User;
@@ -22,7 +23,7 @@
 import static com.loopers.domain.product.ProductAssertions.assertProduct;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 
 @SpringBootTest
@@ -51,9 +52,9 @@ void setup() {
         , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
     List<Brand> savedBrandList = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
 
-    List<Product> productList = List.of(Product.create(savedBrandList.get(0), "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10)
-        , Product.create(savedBrandList.get(0), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
-        , Product.create(savedBrandList.get(1), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+    List<Product> productList = List.of(Product.create(savedBrandList.get(0), "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10)
+        , Product.create(savedBrandList.get(0), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
+        , Product.create(savedBrandList.get(1), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
     );
     savedProducts = productService.save(productList);
 
@@ -67,7 +68,7 @@ void tearDown() {
   @DisplayName("상품목록을 조회할 때,")
   @Nested
   class GetList {
-    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @DisplayName("페이징 처리되어, 초기설정시 size=20, sort=최신순으로 목록이 조회된다.")
     @Test
     void 성공_상품목록조회() {
       // act
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
index 57e57f3da..5974e7877 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
@@ -1,14 +1,11 @@
 package com.loopers.domain.brand;
 
-import com.loopers.domain.product.Product;
 import com.loopers.support.error.CoreException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
-import java.math.BigDecimal;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
@@ -43,7 +40,7 @@ void setup() {
     void 실패_이름_오류() {
       assertThatThrownBy(() -> {
         brand = Brand.create("", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
-      }).isInstanceOf(IllegalArgumentException.class).hasMessageContaining(validMsg);
+      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
   }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
index ccb7fbeab..d1555e8ca 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
@@ -1,6 +1,7 @@
 package com.loopers.domain.like;
 
 import com.loopers.domain.brand.Brand;
+import com.loopers.domain.order.Money;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
@@ -61,7 +62,7 @@ void setup() {
     savedUser = userJpaRepository.save(user);
     Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
     savedBrand = brandJpaRepository.save(brand);
-    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10);
+    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10);
     savedProduct = productJpaRepository.save(product);
   }
 
@@ -85,7 +86,7 @@ class Create {
       assertLike(result, savedLike.get());
     }
 
-    @DisplayName("존재하지 않는 좋아요 ID를 주면, 예외가 발생하지 않는다.")
+    @DisplayName("동일한 좋아요를 중복 저장해도 일관된 결과를 반환한다")
     @Test
     void 성공_이미_존재하는_좋아요ID() {
       // arrange
@@ -135,7 +136,7 @@ class Delete {
   @DisplayName("좋아요 상품목록을 조회할 때,")
   @Nested
   class GetList {
-    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @DisplayName("페이징 처리되어, 초기설정시 size=20, sort=최신순으로 목록이 조회된다.")
     @Test
     void 성공_좋아요_상품목록조회() {
       // arrange
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
index 729d1b8c3..2e0855b94 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
@@ -1,6 +1,7 @@
 package com.loopers.domain.like;
 
 import com.loopers.domain.brand.Brand;
+import com.loopers.domain.order.Money;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
@@ -63,7 +64,7 @@ void setup() {
     savedUser = userJpaRepository.save(user);
     Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
     savedBrand = brandJpaRepository.save(brand);
-    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10);
+    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10);
     savedProduct = productJpaRepository.save(product);
 
   }
@@ -115,7 +116,8 @@ class Delete {
       // act
       likeService.remove(savedUser.getId(), savedProduct.getId());
       //assert
-
+      boolean result = likeService.isLiked(savedUser.getId(), savedProduct.getId());
+      assertThat(result).isEqualTo(true);
     }
 
     @DisplayName("존재하지 않는 좋아요 ID를 삭제하면, 예외가 발생하지 않는다.")
@@ -129,13 +131,15 @@ class Delete {
       likeService.remove(savedUser.getId(), savedProduct.getId());
 
       // assert
+      boolean result = likeService.isLiked(savedUser.getId(), savedProduct.getId());
+      assertThat(result).isEqualTo(false);
     }
   }
 
   @DisplayName("좋아요 상품목록을 조회할 때,")
   @Nested
   class GetList {
-    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @DisplayName("페이징 처리되어, 초기설정시 size=20, sort=최신순으로 목록이 조회된다.")
     @Test
     void 성공_좋아요_상품목록조회() {
       // arrange
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java
index 65a876a15..6e38ff211 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderAssertions.java
@@ -9,7 +9,7 @@ public static void assertOrder(Order actual, Order expected) {
     assertThat(actual.getId()).isEqualTo(expected.getId());
     assertThat(actual.getRefUserId()).isEqualTo(expected.getRefUserId());
     assertThat(actual.getStatus()).isEqualTo(expected.getStatus());
-    assertThat(actual.getTotalPrice()).isEqualByComparingTo(expected.getTotalPrice());
+    assertThat(actual.getTotalPrice()).isEqualTo(expected.getTotalPrice());
     assertThat(actual.getOrderAt()).isEqualTo(expected.getOrderAt());
   }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemModelTest.java
new file mode 100644
index 000000000..7b68669a0
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemModelTest.java
@@ -0,0 +1,51 @@
+package com.loopers.domain.order;
+
+import com.loopers.support.error.CoreException;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class OrderItemModelTest {
+  OrderItem orderItems;
+
+  @DisplayName("주문상세 모델을 생성할 때, ")
+  @Nested
+  class Create_Order {
+    @DisplayName("사용자ID, 상태, 지불금액,총금액, 주문일자가 모두 주어지면, 정상적으로 생성된다.")
+    @Test
+    void 성공_Order_객체생성() {
+      orderItems = OrderItem.create(1L, 2L, Money.wons(5_000));
+      assertThat(orderItems.getRefProductId()).isEqualTo(1L);
+      assertThat(orderItems.getQuantity()).isEqualTo(2L);
+      assertThat(orderItems.getUnitPrice()).isEqualTo(Money.wons(5_000));
+
+    }
+  }
+
+  @DisplayName("주문 모델을 생성할 때, 검증")
+  @Nested
+  class Valid_Order {
+
+    @Test
+    void 실패_수량_음수오류() {
+      assertThrows(IllegalArgumentException.class, () -> {
+        orderItems = OrderItem.create(1L, -2L, Money.wons(5_000));
+      });
+    }
+
+    @Test
+    void 실패_단가_음수오류() {
+
+      assertThrows(IllegalArgumentException.class, () -> {
+        orderItems = OrderItem.create(1L, 2L, Money.wons(-5_000));
+      });
+    }
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
index 82d2f1d60..afa1239e1 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
@@ -5,17 +5,16 @@
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
-import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 class OrderModelTest {
   Order order;
-  List<OrderItem> orderItems;
+  List<OrderItem> orderItems = new ArrayList<>();
 
   @DisplayName("주문 모델을 생성할 때, ")
   @Nested
@@ -24,7 +23,7 @@ class Create_Order {
     @Test
     void 성공_Order_객체생성() {
       orderItems = new ArrayList<>();
-      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), orderItems);
+      order = Order.create(1, OrderStatus.PENDING, Money.wons(10_000), orderItems);
       assertThat(order.getRefUserId()).isEqualTo(1);
       assertThat(order.getStatus()).isEqualTo(OrderStatus.PENDING);
 
@@ -37,8 +36,8 @@ class Valid_Order {
 
     @Test
     void 실패_총가격_음수오류() {
-      assertThrows(CoreException.class, () -> {
-        order = Order.create(1, OrderStatus.PENDING, new BigDecimal(-10_000), orderItems);
+      assertThrows(IllegalArgumentException.class, () -> {
+        order = Order.create(1, OrderStatus.PENDING, Money.wons(-10_000), orderItems);
       });
     }
   }
@@ -48,7 +47,7 @@ class Valid_Order {
   class Valid_상품준비중 {
     @Test
     void 실패_주문확인_결재전오류() {
-      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), orderItems);
+      order = Order.create(1, OrderStatus.PENDING, Money.wons(10_000), orderItems);
       assertThatThrownBy(() -> {
         order.preparing();
       }).isInstanceOf(CoreException.class).hasMessageContaining("결재대기중");
@@ -56,7 +55,7 @@ class Valid_상품준비중 {
 
     @Test
     void 실패_주문확인_상품준비중오류() {
-      order = Order.create(1, OrderStatus.PREPARING, new BigDecimal(10_000), orderItems);
+      order = Order.create(1, OrderStatus.PREPARING, Money.wons(10_000), orderItems);
       assertThatThrownBy(() -> {
         order.preparing();
       }).isInstanceOf(CoreException.class).hasMessageContaining("이미 준비중");
@@ -64,7 +63,7 @@ class Valid_상품준비중 {
 
     @Test
     void 실패_주문확인_상품준비완료오류() {
-      order = Order.create(1, OrderStatus.SHIPPED, new BigDecimal(10_000), orderItems);
+      order = Order.create(1, OrderStatus.SHIPPED, Money.wons(10_000), orderItems);
       assertThatThrownBy(() -> {
         order.preparing();
       }).isInstanceOf(CoreException.class).hasMessageContaining("상품준비 완료");
@@ -72,7 +71,7 @@ class Valid_상품준비중 {
 
     @Test
     void 성공_상품준비중() {
-      order = Order.create(1, OrderStatus.PAID, new BigDecimal(10_000), orderItems);
+      order = Order.create(1, OrderStatus.PAID, Money.wons(10_000), orderItems);
       order.preparing();
       assertThat(order.getStatus()).isEqualTo(OrderStatus.PREPARING);
     }
@@ -83,7 +82,7 @@ class Valid_상품준비중 {
   class Valid_주문취소 {
     @Test
     void 실패_주문취소_이미취소된주문오류() {
-      order = Order.create(1, OrderStatus.CANCELLED, new BigDecimal(10_000), orderItems);
+      order = Order.create(1, OrderStatus.CANCELLED, Money.wons(10_000), orderItems);
       assertThrows(CoreException.class, () -> {
         order.preparing();
       });
@@ -91,7 +90,7 @@ class Valid_주문취소 {
 
     @Test
     void 성공_주문취소() {
-      order = Order.create(1, OrderStatus.PENDING, new BigDecimal(10_000), orderItems);
+      order = Order.create(1, OrderStatus.PENDING, Money.wons(10_000), orderItems);
       order.cancel();
       assertThat(order.getStatus()).isEqualTo(OrderStatus.CANCELLED);
     }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
index a3e21b571..15ce9fcb5 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
@@ -3,8 +3,12 @@
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
+import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
+import com.loopers.infrastructure.order.OrderJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
+import com.loopers.infrastructure.user.UserJpaRepository;
+import com.loopers.support.error.CoreException;
 import com.loopers.utils.DatabaseCleanUp;
 import org.junit.jupiter.api.*;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -14,42 +18,65 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Currency;
 import java.util.List;
 
 import static com.loopers.domain.brand.BrandAssertions.assertBrand;
+import static com.loopers.domain.order.OrderAssertions.assertOrder;
 import static com.loopers.domain.product.ProductAssertions.assertProduct;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 
 @SpringBootTest
 @Transactional
 class OrderServiceIntegrationTest {
   @Autowired
-  private ProductService productService;
+  private OrderService orderService;
+
+  @MockitoSpyBean
+  private UserJpaRepository userJpaRepository;
 
   @MockitoSpyBean
   private BrandJpaRepository brandJpaRepository;
   @MockitoSpyBean
   private ProductJpaRepository productJpaRepository;
+  @MockitoSpyBean
+  private OrderJpaRepository orderJpaRepository;
 
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
 
+  User savedUser;
   List<Brand> savedBrands;
   List<Product> savedProducts;
+  Order savedOrder;
 
   @BeforeEach
   void setup() {
+    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    savedUser = userJpaRepository.save(user);
+
     List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
         , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
     savedBrands = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
 
-    List<Product> productList = List.of(Product.create(savedBrands.get(0), "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10)
-        , Product.create(savedBrands.get(0), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
-        , Product.create(savedBrands.get(1), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+    List<Product> productList = List.of(Product.create(savedBrands.get(0), "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10)
+        , Product.create(savedBrands.get(0), "Flower Pattern Fleece Jacket", Money.wons(80_000), 20)
+        , Product.create(savedBrands.get(1), "Flower Pattern Fleece Jacket", Money.wons(80_000), 20)
     );
     savedProducts = productList.stream().map((product) -> productJpaRepository.save(product)).toList();
 
+    List<OrderItem> orderItems = new ArrayList<>();
+    orderItems.add(OrderItem.create(productList.get(0).getId(), 2L, Money.wons(5_000)));
+    Order order = Order.create(savedUser.getId(), OrderStatus.PENDING, Money.wons(10_000), orderItems);
+    savedOrder = orderJpaRepository.save(order);
+
   }
 
   @AfterEach
@@ -57,64 +84,67 @@ void tearDown() {
     databaseCleanUp.truncateAllTables();
   }
 
-  @DisplayName("상품목록을 조회할 때,")
+  @DisplayName("주문목록을 조회할 때,")
   @Nested
   class GetList {
-    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @DisplayName("페이징 처리되어, 초기설정시 size=20, sort=최신순으로 목록이 조회된다.")
     @Test
     void 성공_상품목록조회() {
       // arrange
 
       // act
-      Page<Product> productsPage = productService.getProducts(null, "latest", 0, 20);
-      List<Product> products = productsPage.getContent();
+      Page<Order> ordersPage = orderService.getOrders(savedUser.getId(), "latest", 0, 20);
+      List<Order> orders = ordersPage.getContent();
       // assert
-      assertThat(products).isNotEmpty().hasSize(3);
+      assertThat(orders).isNotEmpty().hasSize(1);
     }
+  }
 
-    @DisplayName("브랜드ID 검색조건 포함시, 해당 브랜드의 상품 목록이 조회된다.")
+  @DisplayName("주문을 조회할 때,")
+  @Nested
+  class Get {
+    @DisplayName("존재하는 주문 ID를 주면, 해당 주문 정보를 반환한다.")
     @Test
-    void 성공_상품목록조회_브랜드ID() {
+    void 성공_존재하는_주문ID() {
       // arrange
-
       // act
-      Page<Product> productsPage = productService.getProducts(savedProducts.get(0).getId(), null, 0, 20);
-      List<Product> productList = productsPage.getContent();
+      Order result = orderService.getOrder(savedOrder.getId());
 
       // assert
-      assertThat(productList).isNotEmpty().hasSize(2);
-
-      assertProduct(productList.get(0), savedProducts.get(1));
-      assertProduct(productList.get(1), savedProducts.get(0));
+      assertOrder(result, savedOrder);
     }
-  }
 
-  @DisplayName("상품을 조회할 때,")
-  @Nested
-  class Get {
-    @DisplayName("존재하는 상품 ID를 주면, 해당 상품 정보를 반환한다.")
+    @DisplayName("존재하지 않는 상품 ID를 주면, null이 반환된다.")
     @Test
-    void 성공_존재하는_상품ID() {
+    void 실패_존재하지_않는_주문ID() {
       // arrange
+
       // act
-      Product result = productService.getProduct(savedProducts.get(0).getId());
+      Order result = orderService.getOrder((long) 99999);
 
       // assert
-      assertProduct(result, savedProducts.get(0));
-      assertBrand(result.getBrand(), savedBrands.get(0));
+      assertThat(result).isNull();
     }
+  }
 
-    @DisplayName("존재하지 않는 상품 ID를 주면, null이 반환된다.")
+  @DisplayName("주문 생성")
+  @Nested
+  class Save {
+    @DisplayName("주문 생성을 한다.")
     @Test
-    void 실패_존재하지_않는_상품ID() {
+    void 성공_주문생성() {
       // arrange
+      List<OrderItem> orderItems = new ArrayList<>();
+      orderItems.add(OrderItem.create(savedProducts.get(0).getId(), 2L, Money.wons(5_000)));
+      Order order = Order.create(savedUser.getId(), OrderStatus.PENDING, Money.wons(10_000), orderItems);
 
       // act
-      Product result = productService.getProduct((long) 1000);
+      orderService.save(order);
 
       // assert
-      assertThat(result).isNull();
+      assertAll(
+          () -> verify(orderJpaRepository, times(1)).save(order)
+      );
     }
   }
-
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductAssertions.java
index 2c437632d..99d2a1c9b 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductAssertions.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductAssertions.java
@@ -1,10 +1,5 @@
 package com.loopers.domain.product;
 
-import com.loopers.domain.brand.Brand;
-
-import java.math.BigDecimal;
-import java.util.List;
-
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class ProductAssertions {
@@ -13,6 +8,6 @@ public static void assertProduct(Product actual, Product expected) {
     assertThat(expected).isNotNull();
     assertThat(actual.getId()).isEqualTo(expected.getId());
     assertThat(actual.getName()).isEqualTo(expected.getName());
-    assertThat(actual.getPrice()).isEqualByComparingTo(expected.getPrice());
+    assertThat(actual.getPrice()).isEqualTo(expected.getPrice());
   }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
index cf44ba293..119b0d1b3 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
@@ -1,6 +1,7 @@
 package com.loopers.domain.product;
 
 import com.loopers.domain.brand.Brand;
+import com.loopers.domain.order.Money;
 import com.loopers.support.error.CoreException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
@@ -11,7 +12,7 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 class ProductModelTest {
   Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
@@ -24,10 +25,10 @@ class Create_Product {
     @DisplayName("브랜드모델, 상품명, 가격, 재고, 예약재고가 모두 주어지면, 정상적으로 생성된다.")
     @Test
     void 성공_Product_객체생성() {
-      product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10);
+      product = Product.create(brand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10);
       assertThat(product).isNotNull();
       assertThat(product.getName()).isEqualTo("Wild Faith Rose Sweatshirt");
-      assertThat(product.getPrice()).isEqualTo(new BigDecimal(80_000));
+      assertThat(product.getPrice()).isEqualTo(Money.wons(80_000));
       assertThat(product.getStock()).isEqualTo(10);
     }
   }
@@ -42,16 +43,16 @@ void setup() {
 
     @Test
     void 실패_가격_음수오류() {
-      assertThrows(CoreException.class, () -> {
-        product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(-1000), 0);
-      });
+      assertThatThrownBy(() -> {
+        product = Product.create(brand, "Wild Faith Rose Sweatshirt", Money.wons(-1_000), 0);
+      }).isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     void 실패_재고_음수오류() {
-      assertThrows(CoreException.class, () -> {
-        product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), -1);
-      });
+      assertThatThrownBy(() -> {
+        product = Product.create(brand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), -1);
+      }).isInstanceOf(CoreException.class);
     }
 
   }
@@ -61,21 +62,21 @@ void setup() {
   class Valid_재고차감 {
     @BeforeEach
     void setup() {
-      product = Product.create(brand, "Wild Faith Rose Sweatshirt", new BigDecimal(1000), 10);
+      product = Product.create(brand, "Wild Faith Rose Sweatshirt", Money.wons(1_000), 10);
     }
 
     @Test
     void 실패_예약재고0_차감오류() {
-      assertThrows(CoreException.class, () -> {
+      assertThatThrownBy(() -> {
         product.deductStock(0);
-      });
+      }).isInstanceOf(CoreException.class);
     }
 
     @Test
     void 실패_예약재고20_차감오류() {
-      assertThrows(CoreException.class, () -> {
+      assertThatThrownBy(() -> {
         product.deductStock(20);
-      });
+      }).isInstanceOf(CoreException.class);
     }
 
     @Test
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
index b1693262e..84e439388 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
@@ -1,6 +1,7 @@
 package com.loopers.domain.product;
 
 import com.loopers.domain.brand.Brand;
+import com.loopers.domain.order.Money;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
 import com.loopers.utils.DatabaseCleanUp;
@@ -42,9 +43,9 @@ void setup() {
         , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
     savedBrands = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
 
-    List<Product> productList = List.of(Product.create(savedBrands.get(0), "Wild Faith Rose Sweatshirt", new BigDecimal(80_000), 10)
-        , Product.create(savedBrands.get(0), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
-        , Product.create(savedBrands.get(1), "Flower Pattern Fleece Jacket", new BigDecimal(178_000), 20)
+    List<Product> productList = List.of(Product.create(savedBrands.get(0), "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10)
+        , Product.create(savedBrands.get(0), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
+        , Product.create(savedBrands.get(1), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
     );
     savedProducts = productList.stream().map((product) -> productJpaRepository.save(product)).toList();
 
@@ -58,7 +59,7 @@ void tearDown() {
   @DisplayName("상품목록을 조회할 때,")
   @Nested
   class GetList {
-    @DisplayName("페이징 처리되어, 초기설정시 size=0, sort=최신순으로 목록이 조회된다.")
+    @DisplayName("페이징 처리되어, 초기설정시 size=20, sort=최신순으로 목록이 조회된다.")
     @Test
     void 성공_상품목록조회() {
       // arrange
@@ -76,7 +77,7 @@ class GetList {
       // arrange
 
       // act
-      Page<Product> productsPage = productService.getProducts(savedProducts.get(0).getId(), null, 0, 20);
+      Page<Product> productsPage = productService.getProducts(savedBrands.get(0).getId(), null, 0, 20);
       List<Product> productList = productsPage.getContent();
 
       // assert
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
index 7cc9e69a0..00ea5e77f 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
@@ -46,8 +46,7 @@ class Join {
 
       // assert
       assertAll(
-          () -> verify(userJpaRepository, times(1)).save(user),
-          () -> assertThrows(CoreException.class, () -> userService.join(user))
+          () -> verify(userJpaRepository, times(1)).save(user)
       );
     }
 
@@ -78,12 +77,12 @@ class Get {
       userService.join(user);
 
       // act
-      User result = userService.getUser(user.getUserId());
+      User result = userService.getUser(user.getLoginId());
 
       // assert
       assertAll(
           () -> assertThat(result).isNotNull(),
-          () -> assertThat(result.getUserId()).isEqualTo(user.getUserId()),
+          () -> assertThat(result.getLoginId()).isEqualTo(user.getLoginId()),
           () -> assertThat(result.getEmail()).isEqualTo(user.getEmail()),
           () -> assertThat(result.getBirthday()).isEqualTo(user.getBirthday()),
           () -> assertThat(result.getGender()).isEqualTo(user.getGender())
@@ -97,7 +96,7 @@ class Get {
       User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
 
       // act
-      User result = userService.getUser(user.getUserId());
+      User result = userService.getUser(user.getLoginId());
 
       // assert
       assertThat(result).isNull();
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java
index 91eb7b75f..9ac8f48fb 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java
@@ -101,6 +101,6 @@ void setup() {
   void 성공_User_객체생성() {
     user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
     assertThat(user).isNotNull();
-    assertThat(user.getUserId()).isEqualTo("user1");
+    assertThat(user.getLoginId()).isEqualTo("user1");
   }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
index d1730a77f..e385bd9a0 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
@@ -117,7 +117,7 @@ class Charge {
     void 실패_ID없음_400() {
       //given
       HttpHeaders headers = new HttpHeaders();
-      headers.set("X-USER-ID", "2");
+      headers.set("X-USER-ID", "999999");
 
       //when
       String url = "/api/v1/user/point/charge";
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
index 21fcb759e..d3ce611e3 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
@@ -96,7 +96,7 @@ class Get {
       userService.join(user);
 
       //when
-      String url = ENDPOINT_GET.apply(user.getUserId());
+      String url = ENDPOINT_GET.apply(user.getLoginId());
       ParameterizedTypeReference<ApiResponse<UserCreateV1Dto.UserResponse>> resType = new ParameterizedTypeReference<>() {
       };
       ResponseEntity<ApiResponse<UserCreateV1Dto.UserResponse>> res = testRestTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null), resType);
@@ -104,7 +104,7 @@ class Get {
       //then
       assertThat(res.getStatusCode()).isEqualTo(HttpStatus.OK);
       assertThat(res.getBody().data().userId()).isNotNull();
-      assertThat(res.getBody().data().userId()).isEqualTo(user.getUserId());
+      assertThat(res.getBody().data().userId()).isEqualTo(user.getLoginId());
     }
 
     @DisplayName("E2E테스트2")

From 7acb09ec1c570ff99cd213d4fa9526a6d9ad1464 Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Sun, 16 Nov 2025 02:11:24 +0900
Subject: [PATCH 05/11] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=EB=9E=98?=
 =?UTF-8?q?=EB=B9=97=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../loopers/domain/like/LikeRepository.java   |  2 +-
 .../com/loopers/domain/like/LikeService.java  |  6 ++--
 .../domain/order/CreateOrderService.java      |  2 --
 .../java/com/loopers/domain/order/Order.java  | 13 ++++---
 .../com/loopers/domain/order/OrderItem.java   |  5 +++
 .../com/loopers/domain/order/OrderStatus.java |  2 --
 .../domain/product/ProductService.java        |  2 +-
 .../like/LikeJpaRepository.java               |  2 --
 .../order/OrderRepositoryImpl.java            |  1 -
 .../interfaces/api/order/OrderV1ApiSpec.java  |  4 +--
 .../interfaces/api/user/UserCreateV1Dto.java  |  4 +--
 .../order/OrderFacadeIntegrationTest.java     |  2 +-
 .../product/ProductFacadeIntegrationTest.java |  4 ---
 .../loopers/domain/brand/BrandAssertions.java |  2 --
 .../domain/like/FakeLikeRepository.java       | 36 +++++++++----------
 .../like/LikeServiceIntegrationTest.java      | 15 ++------
 .../loopers/domain/like/LikeServiceTest.java  | 17 ++++-----
 .../domain/order/OrderItemModelTest.java      | 10 ++----
 .../loopers/domain/order/OrderModelTest.java  | 14 +++++---
 .../order/OrderServiceIntegrationTest.java    |  8 -----
 .../point/PointServiceIntegrationTest.java    |  1 -
 .../domain/product/ProductModelTest.java      |  3 --
 .../ProductServiceIntegrationTest.java        |  1 -
 .../user/UserServiceIntegrationTest.java      |  1 -
 .../interfaces/api/PointV1ApiE2ETest.java     |  1 -
 docs/week2/01-requirements.md                 | 10 +++---
 docs/week2/02-sequence-diagrams.md            |  4 +--
 27 files changed, 67 insertions(+), 105 deletions(-)

diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
index b0a87fb3a..fad3cef9b 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
@@ -7,7 +7,7 @@
 
 public interface LikeRepository {
 
-  Optional<Like> findById(Long userId, Long productid);
+  Optional<Like> findById(Long userId, Long productId);
 
   Like save(Like like);
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
index 01610a251..383dc8485 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
@@ -63,10 +63,8 @@ private Sort getSortBySortType(String sortType) {
     switch (sortType.toLowerCase()) {
       case "latest":
         return latestSort;
-      case "price_asc":
-        return Sort.by("price").ascending().and(latestSort);
-      case "likes_desc":
-        return Sort.by("likesCount").descending().and(latestSort);
+      case "product_asc":
+        return Sort.by("refProductId").ascending().and(latestSort);
       default:
         return latestSort;
     }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
index e385ef9fd..9b43bc189 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
@@ -2,7 +2,6 @@
 package com.loopers.domain.order;
 
 import com.loopers.domain.product.Product;
-import com.loopers.domain.product.ProductRepository;
 import com.loopers.domain.user.User;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
@@ -14,7 +13,6 @@
 @RequiredArgsConstructor
 @Component
 public class CreateOrderService {
-  private final ProductRepository productRepository;
   private final OrderRepository orderRepository;
 
   public Order save(User user, List<Product> productList, Map<Long, Long> quantityMap) {
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
index 8749bcc1b..4865296a7 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
@@ -19,15 +19,15 @@ public class Order extends BaseEntity {
 
   private OrderStatus status;
 
-  Money totalPrice;
+  private Money totalPrice;
 
-  ZonedDateTime orderAt;
+  private ZonedDateTime orderAt;
 
   @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
   @JoinColumn(name = "ref_order_id")
   private List<OrderItem> orderItems = new ArrayList<>();
 
-  public void setOrderItems(List<OrderItem> orderItems) {
+  private void setOrderItems(List<OrderItem> orderItems) {
     if (orderItems == null || orderItems.isEmpty()) {
       throw new CoreException(ErrorType.BAD_REQUEST, "주문 상세내역이 없습니다.");
     }
@@ -42,13 +42,16 @@ private Order(long refUserId, OrderStatus status, Money totalPrice, List<OrderIt
     this.status = status;
     this.totalPrice = totalPrice;
     this.orderAt = ZonedDateTime.now();
-    this.orderItems = orderItems;
+    setOrderItems(orderItems);
   }
 
   public static Order create(long refUserId, OrderStatus status, Money totalPrice, List<OrderItem> orderItems) {
     if (status == null) {
       throw new CoreException(ErrorType.BAD_REQUEST, "상태 정보는 비어있을 수 없습니다.");
     }
+    if (orderItems == null || orderItems.isEmpty()) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "주문 상세내역이 없습니다.");
+    }
     return new Order(refUserId, status, totalPrice, orderItems);
   }
 
@@ -61,7 +64,7 @@ public void cancel() {
 
   public void preparing() {
     if (status == OrderStatus.PENDING) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "결재대기중 주문입니다.");
+      throw new CoreException(ErrorType.BAD_REQUEST, "결제대기중 주문입니다.");
     }
     if (status == OrderStatus.PREPARING) {
       throw new CoreException(ErrorType.BAD_REQUEST, "이미 준비중인 주문입니다.");
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
index ed4aa292e..c8820a684 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
@@ -1,6 +1,8 @@
 package com.loopers.domain.order;
 
 import com.loopers.domain.BaseEntity;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
 import jakarta.persistence.*;
 import lombok.Getter;
 
@@ -45,6 +47,9 @@ private OrderItem(long refProductId, long quantity, Money unitPrice, Money total
   }
 
   public static OrderItem create(long refProductId, long quantity, Money unitPrice) {
+    if (quantity <= 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "수량은 0보다 커야 합니다.");
+    }
     Money totalPrice = unitPrice.multiply(quantity);
     return new OrderItem(refProductId, quantity, unitPrice, totalPrice);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
index 4b7375384..f0db451db 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
@@ -1,7 +1,5 @@
 package com.loopers.domain.order;
 
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
index 04f04a8e3..e7c69be56 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
@@ -99,7 +99,7 @@ private Sort getSortBySortType(String sortType) {
       case "latest":
         return latestSort;
       case "price_asc":
-        return Sort.by("price").ascending().and(latestSort);
+        return Sort.by("totalPrice").ascending().and(latestSort);
       case "likes_desc":
         return Sort.by("likesCount").descending().and(latestSort);
       default:
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
index 1008e0adb..a98d2cb7e 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
@@ -25,6 +25,4 @@ public interface LikeJpaRepository extends JpaRepository<Like, Long> {
       countQuery = "SELECT COUNT(l) FROM Like l WHERE l.user.id = :userId"
   )
   Page<Like> getLikedProducts(@Param("userId") Long userId, Pageable pageable);
-
-  //Page<Like> getLikedProducts(@Param("userId") Long userId, Pageable pageable);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java
index 3e29c2a90..609a774e2 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java
@@ -2,7 +2,6 @@
 
 import com.loopers.domain.order.Order;
 import com.loopers.domain.order.OrderRepository;
-import com.loopers.domain.product.ProductRepository;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
index 9ec1bf2a8..9474e7d9d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
@@ -29,11 +29,11 @@ ApiResponse<Page<Order>> getOrderList(
 
   @Operation(
       summary = "주문 요청",
-      description = "ID로 주문를 충전합니다."
+      description = "ID로 주문를 요청합니다."
   )
   @Valid
   ApiResponse<OrderCreateV1Dto.OrderResponse> createOrder(
-      @Schema(name = "사용자 ID", description = "충전할 사용자의 ID")
+      @Schema(name = "사용자 ID", description = "주문할 사용자의 ID")
       @RequestHeader(value = "X-USER-ID", required = false) Long userId
       , @RequestBody OrderCreateV1Dto.OrderRequest request
   );
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserCreateV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserCreateV1Dto.java
index 837984c43..c658c78ab 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserCreateV1Dto.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserCreateV1Dto.java
@@ -2,8 +2,6 @@
 
 import com.loopers.application.user.UserInfo;
 
-import java.time.LocalDate;
-
 public class UserCreateV1Dto {
   public record UserRequest(String userId, String email, String birthday, String gender) {
   }
@@ -14,7 +12,7 @@ public static UserResponse from(UserInfo info) {
       return new UserResponse(
           info.userId(),
           info.email(),
-          info.birthday().toString(),
+          info.birthday(),
           info.gender()
       );
     }
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
index b48df283e..b88492169 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
@@ -119,7 +119,7 @@ class Get {
   @DisplayName("주문을 생성할 때,")
   @Nested
   class Post {
-    @DisplayName("10재고가 있는 8원의 상품 1건 주문시, 기본 포인트10으로 결재가 된다.")
+    @DisplayName("10재고가 있는 8원의 상품 1건 주문시, 기본 포인트10으로 결제가 된다.")
     @Test
     void 성공_단건주문생성() {
       List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
index f6175d493..e2e348966 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
@@ -6,7 +6,6 @@
 import com.loopers.domain.product.ProductService;
 import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
-import com.loopers.infrastructure.product.ProductJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
 import com.loopers.support.error.CoreException;
 import com.loopers.utils.DatabaseCleanUp;
@@ -15,14 +14,11 @@
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.data.domain.Page;
 import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
-import org.springframework.transaction.annotation.Transactional;
 
-import java.math.BigDecimal;
 import java.util.List;
 
 import static com.loopers.domain.product.ProductAssertions.assertProduct;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandAssertions.java
index bdf45dac1..e445bac82 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandAssertions.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandAssertions.java
@@ -1,7 +1,5 @@
 package com.loopers.domain.brand;
 
-import java.util.List;
-
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class BrandAssertions {
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
index e6e6a40ec..23974fbc7 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
@@ -18,30 +18,26 @@ public class FakeLikeRepository implements LikeRepository {
 
   @Override
   public Optional<Like> findById(Long userId, Long productId) {
-    Long id = (long) 0;
     for (Long key : list.keySet()) {
-      id = key;
       Like like = list.get(key);
-      if (like.getUser().getId() == userId
-          && like.getProduct().getId() == productId) {
+      if (like.getUser().getId().equals(userId)
+          && like.getProduct().getId().equals(productId)) {
         return Optional.of(like);
       }
     }
-    return null;
+    return Optional.empty();
   }
 
   @Override
   public Like save(Like entity) {
-    Long id = (long) 0;
     for (Long key : list.keySet()) {
-      id = key;
       Like like = list.get(key);
-      if (like.getUser().getId() == entity.getUser().getId()
-          && like.getProduct().getId() == entity.getUser().getId()) {
+      if (like.getUser().getId().equals(entity.getUser().getId())
+          && like.getProduct().getId().equals(entity.getProduct().getId())) {
         throw new CoreException(ErrorType.BAD_REQUEST, "중복 좋아요 요청입니다.");
       }
     }
-    id = id == (long) 0 ? 0 : id + 1;
+    Long id = list.keySet().stream().max(Long::compareTo).orElse(0L) + 1;
     list.put(id, entity);
     return entity;
   }
@@ -49,11 +45,13 @@ public Like save(Like entity) {
   @Override
   public long remove(Long userId, Long productId) {
     long cnt = 0;
-    for (Long key : list.keySet()) {
-      Like like = list.get(key);
-      if (like.getUser().getId() == userId
-          && like.getProduct().getId() == productId) {
-        list.remove(key);
+    Iterator<Map.Entry<Long, Like>> iterator = list.entrySet().iterator();
+    while (iterator.hasNext()) {
+      Map.Entry<Long, Like> entry = iterator.next();
+      Like like = entry.getValue();
+      if (like.getUser().getId().equals(userId)
+          && like.getProduct().getId().equals(productId)) {
+        iterator.remove();
         cnt++;
       }
     }
@@ -64,8 +62,8 @@ public long remove(Long userId, Long productId) {
   public boolean isLiked(Long userId, Long productId) {
     for (Long key : list.keySet()) {
       Like like = list.get(key);
-      if (like.getUser().getId() == userId
-          && like.getProduct().getId() == productId) {
+      if (like.getUser().getId().equals(userId)
+          && like.getProduct().getId().equals(productId)) {
         return true;
       }
     }
@@ -77,7 +75,7 @@ public long getLikeCount(Long productId) {
     long cnt = 0;
     for (Long key : list.keySet()) {
       Like like = list.get(key);
-      if (like.getProduct().getId() == productId) {
+      if (like.getProduct().getId().equals(productId)) {
         cnt++;
       }
     }
@@ -86,7 +84,7 @@ public long getLikeCount(Long productId) {
 
   @Override
   public Page<Like> getLikedProducts(Long userId, Pageable pageable) {
-    return new PageImpl<>(list.values().stream().filter(i -> i.getUser().getId() == userId)
+    return new PageImpl<>(list.values().stream().filter(i -> i.getUser().getId().equals(userId))
         .collect(Collectors.toList()), pageable, list.size());
   }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
index d1555e8ca..ed365c557 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
@@ -12,12 +12,9 @@
 import org.junit.jupiter.api.*;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.context.TestConfiguration;
-import org.springframework.context.annotation.Bean;
 import org.springframework.data.domain.Page;
 import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
 
-import java.math.BigDecimal;
 import java.util.List;
 import java.util.Optional;
 
@@ -48,14 +45,6 @@ class LikeServiceIntegrationTest {
   Brand savedBrand;
   Product savedProduct;
 
-  @TestConfiguration
-  static class FakeRepositoryConfig {
-    @Bean
-    public LikeRepository likeRepository() {
-      return new FakeLikeRepository();
-    }
-  }
-
   @BeforeEach
   void setup() {
     User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
@@ -125,10 +114,10 @@ class Delete {
 
       // act
       likeService.remove(savedUser.getId(), savedProduct.getId());
-      Long result1 = likeService.remove(savedUser.getId(), savedProduct.getId());
+      likeService.remove(savedUser.getId(), savedProduct.getId());
 
       // assert
-      verify(likeJpaRepository, times(3)).deleteByUserIdAndProductId(savedUser.getId(), savedProduct.getId());
+      verify(likeJpaRepository, times(2)).deleteByUserIdAndProductId(savedUser.getId(), savedProduct.getId());
 
     }
   }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
index 2e0855b94..47d447e4f 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
@@ -13,16 +13,13 @@
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.context.TestConfiguration;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Primary;
 import org.springframework.data.domain.Page;
 import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
 
-import java.math.BigDecimal;
 import java.util.List;
 
 import static com.loopers.domain.like.LikeAssertions.assertLike;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.verify;
 
 
 @SpringBootTest
@@ -74,12 +71,12 @@ void tearDown() {
     databaseCleanUp.truncateAllTables();
   }
 
-  @DisplayName("좋아요를 조회할 때,")
+  @DisplayName("좋아요를 생성할 때,")
   @Nested
   class Create {
-    @DisplayName("존재하는 좋아요 ID를 주면, 해당 좋아요 정보를 반환한다.")
+    @DisplayName("좋아요를 생성하면, 생성된 좋아요 정보를 반환한다.")
     @Test
-    void 성공_존재하는_좋아요ID() {
+    void 성공_좋아요_생성() {
       // arrange
 
       // act
@@ -90,9 +87,9 @@ class Create {
       assertThat(result.getUser()).isNotNull();
     }
 
-    @DisplayName("존재하지 않는 좋아요 ID를 주면, 예외가 발생하지 않는다.")
+    @DisplayName("동일한 좋아요를 중복 생성하면, 멱등성이 보장된다.")
     @Test
-    void 성공_이미_존재하는_좋아요ID() {
+    void 성공_중복_좋아요_멱등성() {
       // arrange
       Like result1 = likeService.save(Like.create(savedUser, savedProduct));
 
@@ -117,7 +114,7 @@ class Delete {
       likeService.remove(savedUser.getId(), savedProduct.getId());
       //assert
       boolean result = likeService.isLiked(savedUser.getId(), savedProduct.getId());
-      assertThat(result).isEqualTo(true);
+      assertThat(result).isFalse();
     }
 
     @DisplayName("존재하지 않는 좋아요 ID를 삭제하면, 예외가 발생하지 않는다.")
@@ -132,7 +129,7 @@ class Delete {
 
       // assert
       boolean result = likeService.isLiked(savedUser.getId(), savedProduct.getId());
-      assertThat(result).isEqualTo(false);
+      assertThat(result).isFalse();
     }
   }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemModelTest.java
index 7b68669a0..a41f50b54 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemModelTest.java
@@ -5,11 +5,7 @@
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 class OrderItemModelTest {
@@ -35,14 +31,14 @@ class Valid_Order {
 
     @Test
     void 실패_수량_음수오류() {
-      assertThrows(IllegalArgumentException.class, () -> {
-        orderItems = OrderItem.create(1L, -2L, Money.wons(5_000));
+      Money unitPrice = Money.wons(5_000);
+      assertThrows(CoreException.class, () -> {
+        orderItems = OrderItem.create(1L, -2L, unitPrice);
       });
     }
 
     @Test
     void 실패_단가_음수오류() {
-
       assertThrows(IllegalArgumentException.class, () -> {
         orderItems = OrderItem.create(1L, 2L, Money.wons(-5_000));
       });
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
index afa1239e1..a3d605ee4 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
@@ -1,6 +1,7 @@
 package com.loopers.domain.order;
 
 import com.loopers.support.error.CoreException;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -16,13 +17,18 @@ class OrderModelTest {
   Order order;
   List<OrderItem> orderItems = new ArrayList<>();
 
+  @BeforeEach
+  void setup() {
+    orderItems = new ArrayList<>();
+    orderItems.add(OrderItem.create(1L, 2L, Money.wons(5_000)));
+  }
+
   @DisplayName("주문 모델을 생성할 때, ")
   @Nested
   class Create_Order {
     @DisplayName("사용자ID, 상태, 지불금액,총금액, 주문일자가 모두 주어지면, 정상적으로 생성된다.")
     @Test
     void 성공_Order_객체생성() {
-      orderItems = new ArrayList<>();
       order = Order.create(1, OrderStatus.PENDING, Money.wons(10_000), orderItems);
       assertThat(order.getRefUserId()).isEqualTo(1);
       assertThat(order.getStatus()).isEqualTo(OrderStatus.PENDING);
@@ -46,11 +52,11 @@ class Valid_Order {
   @Nested
   class Valid_상품준비중 {
     @Test
-    void 실패_주문확인_결재전오류() {
+    void 실패_주문확인_결제전오류() {
       order = Order.create(1, OrderStatus.PENDING, Money.wons(10_000), orderItems);
       assertThatThrownBy(() -> {
         order.preparing();
-      }).isInstanceOf(CoreException.class).hasMessageContaining("결재대기중");
+      }).isInstanceOf(CoreException.class).hasMessageContaining("결제대기중");
     }
 
     @Test
@@ -84,7 +90,7 @@ class Valid_주문취소 {
     void 실패_주문취소_이미취소된주문오류() {
       order = Order.create(1, OrderStatus.CANCELLED, Money.wons(10_000), orderItems);
       assertThrows(CoreException.class, () -> {
-        order.preparing();
+        order.cancel();
       });
     }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
index 15ce9fcb5..d32ced323 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
@@ -2,13 +2,11 @@
 
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.product.Product;
-import com.loopers.domain.product.ProductService;
 import com.loopers.domain.user.User;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.order.OrderJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
-import com.loopers.support.error.CoreException;
 import com.loopers.utils.DatabaseCleanUp;
 import org.junit.jupiter.api.*;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -17,18 +15,12 @@
 import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Currency;
 import java.util.List;
 
-import static com.loopers.domain.brand.BrandAssertions.assertBrand;
 import static com.loopers.domain.order.OrderAssertions.assertOrder;
-import static com.loopers.domain.product.ProductAssertions.assertProduct;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.jupiter.api.Assertions.assertAll;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
index c86ebaea9..f40915700 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
@@ -14,7 +14,6 @@
 import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
 
 import java.math.BigDecimal;
-import java.util.Optional;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
index 119b0d1b3..4df13baa5 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
@@ -8,11 +8,8 @@
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
-import java.math.BigDecimal;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 class ProductModelTest {
   Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
index 84e439388..df85a8c15 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
@@ -12,7 +12,6 @@
 import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.math.BigDecimal;
 import java.util.List;
 
 import static com.loopers.domain.brand.BrandAssertions.assertBrand;
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
index 00ea5e77f..cc5ee4ff5 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
@@ -11,7 +11,6 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.jupiter.api.Assertions.assertAll;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
index e385bd9a0..2a5e7503c 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
@@ -2,7 +2,6 @@
 
 import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserService;
-import com.loopers.infrastructure.user.UserJpaRepository;
 import com.loopers.utils.DatabaseCleanUp;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.DisplayName;
diff --git a/docs/week2/01-requirements.md b/docs/week2/01-requirements.md
index 118853dbc..bef4af13f 100644
--- a/docs/week2/01-requirements.md
+++ b/docs/week2/01-requirements.md
@@ -53,10 +53,10 @@
 4. 없으면 저장, 있으면 삭제
 5. 좋아요 수 반영
 
-### 유저 스토리5 - 주문 생성 및 결재
+### 유저 스토리5 - 주문 생성 및 결제
 
 - 사용자는 여러상품을 선택해 상품을 주문할수 있다.
-- 결재는 포인트로 가능하다.
+- 결제는 포인트로 가능하다.
 - 주문 생성 후 주문 상세 내역을 확인할수 있다.
 
 #### 기능 흐름
@@ -69,8 +69,8 @@
    4-3. 재고가 있을 경우, 해당 수량 HOLD 및 포인트 확인
 5. 5-1. 포인트가 부족할 경우, "포인트가 부족합니다."
    5-2. 포인트 사용이 가능할 경우, 포인트 차감.
-6. 6-1. 결재중 오류 발생시, "결재중 오류가 발생했습니다."
-   6-2. 결재 성공시, 재고 차감 및 주문 정보 저장
+6. 6-1. 결제중 오류 발생시, "결제중 오류가 발생했습니다."
+   6-2. 결제 성공시, 재고 차감 및 주문 정보 저장
 7. 외부 시스템으로 주문 정보 전송
 8. 주문 상세 정보 조회 및 "주문 완료되었습니다."
 
@@ -129,7 +129,7 @@
 1. 로그인 사용자만 가능
 2. 비로그인일 경우, "로그인이 필요합니다."
 3. 주문 목록에서 주문 상세를 볼수 있다.
-4. 구매자 정보/결재정보/배송지정보를 확인할수 있다.
+4. 구매자 정보/결제정보/배송지정보를 확인할수 있다.
 
 ---
 
diff --git a/docs/week2/02-sequence-diagrams.md b/docs/week2/02-sequence-diagrams.md
index 7ff2daa8b..f1d66331f 100644
--- a/docs/week2/02-sequence-diagrams.md
+++ b/docs/week2/02-sequence-diagrams.md
@@ -108,7 +108,7 @@ S-->>C: 성공
 C-->>U: 응답 200
 ```
 
-### 5. 주문 생성 및 결재
+### 5. 주문 생성 및 결제
 
 ```mermaid
 sequenceDiagram  
@@ -143,7 +143,7 @@ S--x C: 400 BadRequestException
 C-->>U: "포인트가 부족합니다."
 end  
 S->>R : payPoint(user)
-R-->>S: 포인트 결재 성공  
+R-->>S: 포인트 결제 성공  
 S->>R : commitProducts(products)
 R-->>S: 재고차감 완료  
 S->>R : saveOrder(order)

From 28c1cb7449ecf864aa4b7eca03f3a259855bdf2e Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Sun, 16 Nov 2025 22:45:18 +0900
Subject: [PATCH 06/11] =?UTF-8?q?test:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?=
 =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20Fixture=20=EC=83=9D=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../order/OrderFacadeIntegrationTest.java     |   5 +-
 .../product/ProductFacadeIntegrationTest.java |   4 +-
 .../like/LikeServiceIntegrationTest.java      |   4 +-
 .../loopers/domain/like/LikeServiceTest.java  |   4 +-
 .../order/OrderServiceIntegrationTest.java    |   4 +-
 .../loopers/domain/point/PointFixture.java    |  46 ++++++++
 .../point/PointServiceIntegrationTest.java    |  23 ++--
 .../com/loopers/domain/point/PointTest.java   |  21 ++--
 .../loopers/domain/user/UserAssertions.java   |  14 +++
 .../com/loopers/domain/user/UserFixture.java  | 110 ++++++++++++++++++
 .../user/UserServiceIntegrationTest.java      |   8 +-
 .../com/loopers/domain/user/UserTest.java     |  99 +++++++---------
 .../interfaces/api/PointV1ApiE2ETest.java     |  14 +--
 .../interfaces/api/UserV1ApiE2ETest.java      |   3 +-
 build.gradle.kts                              |  11 +-
 gradle.properties                             |   1 +
 16 files changed, 265 insertions(+), 106 deletions(-)
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/point/PointFixture.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/user/UserAssertions.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/user/UserFixture.java

diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
index b88492169..7436c278a 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
@@ -1,5 +1,6 @@
 package com.loopers.application.order;
 
+import com.loopers.application.user.UserFacade;
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.order.Money;
 import com.loopers.domain.order.Order;
@@ -8,6 +9,7 @@
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.order.OrderJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
@@ -51,8 +53,7 @@ class OrderFacadeIntegrationTest {
   @BeforeEach
   void setup() {
     // arrange
-    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
-    savedUser = userJpaRepository.save(user);
+    savedUser = userJpaRepository.save(UserFixture.createUser());
     List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
         , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
     List<Brand> savedBrandList = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
index e2e348966..3b8460944 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
@@ -5,6 +5,7 @@
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
 import com.loopers.support.error.CoreException;
@@ -42,8 +43,7 @@ class ProductFacadeIntegrationTest {
   @BeforeEach
   void setup() {
     // arrange
-    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
-    savedUser = userJpaRepository.save(user);
+    savedUser = userJpaRepository.save(UserFixture.createUser());
     List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
         , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
     List<Brand> savedBrandList = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
index ed365c557..110060f50 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
@@ -4,6 +4,7 @@
 import com.loopers.domain.order.Money;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.like.LikeJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
@@ -47,8 +48,7 @@ class LikeServiceIntegrationTest {
 
   @BeforeEach
   void setup() {
-    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
-    savedUser = userJpaRepository.save(user);
+    savedUser = userJpaRepository.save(UserFixture.createUser());
     Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
     savedBrand = brandJpaRepository.save(brand);
     Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10);
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
index 47d447e4f..3905ae796 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
@@ -4,6 +4,7 @@
 import com.loopers.domain.order.Money;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
@@ -57,8 +58,7 @@ public LikeRepository likeRepository() {
 
   @BeforeEach
   void setup() {
-    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
-    savedUser = userJpaRepository.save(user);
+    savedUser = userJpaRepository.save(UserFixture.createUser());
     Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
     savedBrand = brandJpaRepository.save(brand);
     Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10);
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
index d32ced323..48861d185 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
@@ -3,6 +3,7 @@
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.infrastructure.brand.BrandJpaRepository;
 import com.loopers.infrastructure.order.OrderJpaRepository;
 import com.loopers.infrastructure.product.ProductJpaRepository;
@@ -51,8 +52,7 @@ class OrderServiceIntegrationTest {
 
   @BeforeEach
   void setup() {
-    User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
-    savedUser = userJpaRepository.save(user);
+    savedUser = userJpaRepository.save(UserFixture.createUser());
 
     List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
         , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointFixture.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointFixture.java
new file mode 100644
index 000000000..8cd11aab9
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointFixture.java
@@ -0,0 +1,46 @@
+package com.loopers.domain.point;
+
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
+import org.instancio.Instancio;
+import org.instancio.Model;
+
+import java.math.BigDecimal;
+
+import static org.instancio.Select.field;
+
+public class PointFixture {
+  private static final Model<Point> POINT_MODEL = Instancio.of(Point.class)
+      .ignore(field(Point::getId))
+      .ignore(field(Point::getCreatedAt))
+      .ignore(field(Point::getUpdatedAt))
+      .ignore(field(Point::getDeletedAt))
+      .ignore(field(Point::getUser))
+      .generate(field(Point::getAmount), gen -> gen.math().bigDecimal().min(BigDecimal.ZERO)
+      )
+      .toModel();
+
+  public static Point createPoint() {
+    Point point = Instancio.of(POINT_MODEL).create();
+    User user = UserFixture.createUser(point);
+    point.setUser(user);
+    return point;
+  }
+
+  /**
+   * 특정 필드만 override
+   */
+  public static Point createPointWith(BigDecimal amount) {
+    Point point = Instancio.of(POINT_MODEL)
+        .set(field(Point::getAmount), amount)
+        .create();
+
+    User user = UserFixture.createUser(point);
+    point.setUser(user);
+    return point;
+  }
+
+  public static Point createPoint(User user) {
+    return Point.create(user, BigDecimal.TEN);
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
index f40915700..5367afd2d 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
@@ -1,6 +1,8 @@
 package com.loopers.domain.point;
 
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
+import com.loopers.domain.user.UserService;
 import com.loopers.infrastructure.point.PointJpaRepository;
 import com.loopers.infrastructure.user.UserJpaRepository;
 import com.loopers.support.error.CoreException;
@@ -27,7 +29,7 @@ class PointServiceIntegrationTest {
   private PointService pointService;
 
   @Autowired
-  private UserJpaRepository userJpaRepository;
+  private UserService userService;
 
   @MockitoSpyBean
   private PointJpaRepository pointJpaRepository;
@@ -47,9 +49,8 @@ class Get {
     @Test
     void 성공_존재하는_유저ID() {
       // arrange
-      BigDecimal JOIN_POINT = BigDecimal.TEN;
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      User saved = userJpaRepository.save(user);
+      User user = UserFixture.createUser();
+      User saved = userService.join(user);
 
       // act
       BigDecimal pointAmt = pointService.getAmount(saved.getId());
@@ -57,7 +58,7 @@ class Get {
       // assert(회원가입시, 기본포인트 10)
       assertAll(
           () -> assertThat(pointAmt).isNotNull(),
-          () -> assertEquals(0, pointAmt.compareTo(new BigDecimal(10)))
+          () -> assertEquals(0, pointAmt.compareTo(saved.getPoint().getAmount()))
       );
     }
 
@@ -83,7 +84,7 @@ class Charge {
     @Test
     void 실패_존재하지않는_유저ID() {
       // arrange (등록된 회원 없음)
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User user = UserFixture.createUser();
 
       // act, assert
       assertThatThrownBy(() -> {
@@ -96,17 +97,17 @@ class Charge {
     @Test
     void 성공_존재하는_유저ID() {
       // arrange
-      BigDecimal JOIN_POINT = BigDecimal.TEN;
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      User savedUser = userJpaRepository.save(user);
+      BigDecimal chargeAmt = BigDecimal.TEN;
+
+      User savedUser = userService.join(UserFixture.createUser());
 
       // act
-      BigDecimal pointAmt = pointService.charge(savedUser, BigDecimal.TEN);
+      BigDecimal pointAmt = pointService.charge(savedUser, chargeAmt);
 
       // assert(회원가입시, 기본포인트 10)
       assertAll(
           () -> assertThat(pointAmt).isNotNull(),
-          () -> assertEquals(0, pointAmt.compareTo(new BigDecimal(20)))
+          () -> assertEquals(0, pointAmt.compareTo(savedUser.getPoint().getAmount().add(chargeAmt)))
       );
     }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
index 29d6b647a..cfe26b8dd 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
@@ -1,6 +1,7 @@
 package com.loopers.domain.point;
 
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.support.error.CoreException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
@@ -18,7 +19,7 @@ class PointTest {
 
   @BeforeEach
   void setup() {
-    user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    user = UserFixture.createUser();
   }
 
   @DisplayName("포인트 충전")
@@ -28,7 +29,7 @@ class Charge {
     @Test
     void 실패_포인트충전_0() {
       // arrange
-      point = Point.create(user, BigDecimal.ZERO);
+      point = PointFixture.createPoint();
 
       // act, assert
       assertThatThrownBy(() -> {
@@ -39,13 +40,14 @@ class Charge {
     @Test
     void 성공_포인트충전() {
       // arrange
-      point = Point.create(user, new BigDecimal(20));
+      BigDecimal chargeAmt = new BigDecimal(5);
+      point = PointFixture.createPoint();
 
       // act
-      point.charge(new BigDecimal(5));
+      point.charge(chargeAmt);
 
       // assert
-      assertThat(point.getAmount()).isEqualTo(new BigDecimal(25));
+      assertThat(point.getAmount()).isEqualTo(point.getAmount().add(chargeAmt));
     }
   }
 
@@ -55,7 +57,7 @@ class Use {
     @Test
     void 실패_포인트사용() {
       // arrange
-      point = Point.create(user, BigDecimal.ZERO);
+      point = PointFixture.createPointWith(BigDecimal.ONE);
 
       // act, assert
       assertThatThrownBy(() -> {
@@ -66,13 +68,14 @@ class Use {
     @Test
     void 성공_포인트사용() {
       // arrange
-      point = Point.create(user, new BigDecimal(20));
+      BigDecimal useAmt = new BigDecimal(5);
+      point = PointFixture.createPointWith(new BigDecimal(20));
 
       // act
-      point.use(new BigDecimal(5));
+      point.use(useAmt);
 
       // assert
-      assertThat(point.getAmount()).isEqualTo(new BigDecimal(15));
+      assertThat(point.getAmount()).isEqualTo(point.getAmount().subtract(useAmt));
     }
   }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserAssertions.java
new file mode 100644
index 000000000..5e2f6f903
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserAssertions.java
@@ -0,0 +1,14 @@
+package com.loopers.domain.user;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UserAssertions {
+  public static void assertUser(User actual, User expected) {
+    assertThat(actual).isNotNull();
+    assertThat(expected).isNotNull();
+    assertThat(actual.getId()).isEqualTo(expected.getId());
+    assertThat(actual.getLoginId()).isEqualTo(expected.getLoginId());
+    assertThat(actual.getEmail()).isEqualTo(expected.getEmail());
+    assertThat(actual.getGender()).isEqualTo(expected.getGender());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserFixture.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserFixture.java
new file mode 100644
index 000000000..bc05b9952
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserFixture.java
@@ -0,0 +1,110 @@
+package com.loopers.domain.user;
+
+import com.loopers.domain.point.Point;
+import com.loopers.domain.point.PointFixture;
+import org.instancio.Instancio;
+import org.instancio.Model;
+
+import java.time.LocalDate;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+import static org.instancio.Select.field;
+
+public class UserFixture {
+
+  private static final Model<User> USER_MODEL = Instancio.of(User.class)
+      .ignore(field(User::getId))
+      .ignore(field(User::getCreatedAt))
+      .ignore(field(User::getUpdatedAt))
+      .ignore(field(User::getDeletedAt))
+      .ignore(field(User::getPoint))
+      .set(field(User::getLoginId), generateLoginId())
+      .generate(field(User::getEmail),
+          gen -> gen.net().email()
+      )
+      .generate(field(User::getBirthday), gen -> gen.temporal()
+          .localDate()
+          .past()
+      )
+      .generate(field(User::getGender), gen ->
+          gen.oneOf("F", "M")
+      )
+      .toModel();
+
+  /**
+   * User + Point 자동 연결
+   */
+  public static User createUser() {
+    User user = Instancio.of(USER_MODEL).create();
+    Point point = PointFixture.createPoint(user);
+    user.setPoint(point);
+    return user;
+  }
+
+  public static User createUser(Point point) {
+    User user = Instancio.of(USER_MODEL).create();
+    user.setPoint(point);
+    return user;
+  }
+
+  /**
+   * 특정 필드만 override
+   */
+  public static User createUserWith(String loginId, String email, LocalDate birthday, String gender) {
+    User user = Instancio.of(USER_MODEL)
+        .set(field(User::getLoginId), loginId)
+        .set(field(User::getEmail), email)
+        .set(field(User::getBirthday), birthday)
+        .set(field(User::getGender), gender)
+        .create();
+
+    Point point = PointFixture.createPoint(user);
+    user.setPoint(point);
+    return user;
+  }
+
+  /**
+   * loginId만 override
+   */
+  public static User createUserWithLoginId(String loginId) {
+    User user = Instancio.of(USER_MODEL)
+        .set(field(User::getLoginId), loginId)
+        .create();
+    Point point = PointFixture.createPoint(user);
+    user.setPoint(point);
+    return user;
+  }
+
+  /**
+   * 연관관계 없이 User만 생성하고 싶을 때
+   */
+  public static User createUserWithoutPoint() {
+    return Instancio.of(USER_MODEL).create();
+  }
+
+  private static String generateLoginId() {
+    Random random = new Random();
+    String letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    String digits = "0123456789";
+
+    // 최소 1글자씩 확보
+    char letter = letters.charAt(random.nextInt(letters.length()));
+    char digit = digits.charAt(random.nextInt(digits.length()));
+
+    int remainingLength = random.nextInt(8) + 1; // 총 길이 2~10
+    String allChars = letters + digits;
+    StringBuilder sb = new StringBuilder();
+    sb.append(letter).append(digit);
+
+    for (int i = 2; i < remainingLength; i++) {
+      sb.append(allChars.charAt(random.nextInt(allChars.length())));
+    }
+
+    List<Character> chars = sb.chars().mapToObj(c -> (char) c).collect(Collectors.toList());
+    Collections.shuffle(chars);
+    return chars.stream().map(String::valueOf).collect(Collectors.joining());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
index cc5ee4ff5..102aab462 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceIntegrationTest.java
@@ -38,7 +38,7 @@ class Join {
     @Test
     void 성공_회원가입() {
       // arrange
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User user = UserFixture.createUser();
 
       // act
       userService.join(user);
@@ -53,7 +53,7 @@ class Join {
     @Test
     void 실패_이미_가입된ID() {
       // arrange
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User user = UserFixture.createUser();
       userService.join(user);
 
       // act
@@ -72,7 +72,7 @@ class Get {
     @Test
     void 성공_존재하는_유저ID() {
       // arrange
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User user = UserFixture.createUser();
       userService.join(user);
 
       // act
@@ -92,7 +92,7 @@ class Get {
     @Test
     void 실패_존재하지_않는_유저ID() {
       // arrange
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User user = UserFixture.createUser();
 
       // act
       User result = userService.getUser(user.getLoginId());
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java
index 9ac8f48fb..0b6ed5b34 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserTest.java
@@ -1,106 +1,89 @@
 package com.loopers.domain.user;
 
 import com.loopers.support.error.CoreException;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 class UserTest {
-  User user;
-  String validMsg = "";
 
-  @DisplayName("회원가입-단위테스트1")
+  @DisplayName("회원가입-단위테스트1: ID 검증")
   @Nested
   class ValidID {
-    @BeforeEach
-    void setup() {
-      validMsg = "아이디 형식이 잘못되었습니다.(영문 및 숫자 1~10자 이내)";
-    }
-
-    @Test
-    void 실패_ID_숫자만() {
-      assertThatThrownBy(() -> {
-        user = User.create("12345", "user1@test.XXX", "1999-01-01", "F");
-      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
-    }
 
-    @Test
-    void 실패_ID_문자만() {
-      assertThatThrownBy(() -> {
-        user = User.create("hello", "user1@test.XXX", "1999-01-01", "F");
-      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
-    }
+    private final String validMsg = "아이디 형식이 잘못되었습니다.(영문 및 숫자 1~10자 이내)";
 
-    @Test
-    void 실패_ID_특수문자() {
-      assertThatThrownBy(() -> {
-        user = User.create("12345!", "user1@test.XXX", "1999-01-01", "F");
-      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
-    }
-
-    @Test
-    void 실패_ID_10자이상오류() {
-      assertThatThrownBy(() -> {
-        user = User.create("12345678910", "user1@test.XXX", "1999-01-01", "F");
-      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
+    @ParameterizedTest
+    @ValueSource(strings = {"12345", "hello", "12345!", "12345678910"})
+    void 실패_ID_최소숫자1자_영문1자_이상(String loginId) {
+      User user = UserFixture.createUserWithLoginId(loginId);
+      assertThatThrownBy(() -> User.create(user.getLoginId(), user.getEmail(), user.getBirthday().toString(), user.getGender()))
+          .isInstanceOf(CoreException.class)
+          .hasMessageContaining(validMsg);
     }
   }
 
-  @DisplayName("회원가입-단위테스트2")
+  @DisplayName("회원가입-단위테스트2: 이메일 검증")
   @Nested
   class Valid_Email {
-    @BeforeEach
-    void setup() {
-      validMsg = "이메일 형식이 잘못되었습니다.";
-    }
+
+    private final String validMsg = "이메일 형식이 잘못되었습니다.";
 
     @Test
     void 실패_이메일_기호없음() {
-      assertThatThrownBy(() -> {
-        user = User.create("user1", "user1test.XXX", "1999-01-01", "F");
-      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
+      User user = UserFixture.createUser();
+      assertThatThrownBy(() -> User.create(user.getLoginId(), "user1test.XXX", user.getBirthday().toString(), user.getGender()))
+          .isInstanceOf(CoreException.class)
+          .hasMessageContaining(validMsg);
     }
 
     @Test
     void 실패_이메일_한글포함() {
-      assertThatThrownBy(() -> {
-        user = User.create("user1", "ㄱuser1@test.XXX", "1999-01-01", "F");
-      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
+      User user = UserFixture.createUser();
+      assertThatThrownBy(() -> User.create(user.getLoginId(), "ㄱuser1@test.XXX", user.getBirthday().toString(), user.getGender()))
+          .isInstanceOf(CoreException.class)
+          .hasMessageContaining(validMsg);
     }
   }
 
-
-  @DisplayName("회원가입-단위테스트3")
+  @DisplayName("회원가입-단위테스트3: 생년월일 검증")
   @Nested
   class Valid_Birthday {
-    @BeforeEach
-    void setup() {
-      validMsg = "생년월일 형식이 유효하지 않습니다.";
-    }
+
+    private final String validMsg = "생년월일 형식이 유효하지 않습니다.";
 
     @Test
     void 실패_생년월일_형식오류() {
-      assertThatThrownBy(() -> {
-        user = User.create("user1", "user1@test.XXX", "19990101", "F");
-      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
+      User user = UserFixture.createUser();
+      assertThatThrownBy(() -> User.create(user.getLoginId(), user.getEmail(), "19990101", user.getGender()))
+          .isInstanceOf(CoreException.class)
+          .hasMessageContaining(validMsg);
     }
 
     @Test
     void 실패_생년월일_날짜오류() {
-      assertThatThrownBy(() -> {
-        user = User.create("user1", "user1@test.XXX", "1999-13-01", "F");
-      }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
+      User user = UserFixture.createUser();
+      assertThatThrownBy(() -> User.create(user.getLoginId(), user.getEmail(), "1999-13-01", user.getGender()))
+          .isInstanceOf(CoreException.class)
+          .hasMessageContaining(validMsg);
     }
   }
 
   @Test
+  @DisplayName("회원가입 성공 - 정상 User 객체 생성")
   void 성공_User_객체생성() {
-    user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+    User user = UserFixture.createUser();
+    User result = User.create(user.getLoginId(), user.getEmail(), user.getBirthday().toString(), user.getGender());
+    assertThat(result).isNotNull();
     assertThat(user).isNotNull();
-    assertThat(user.getLoginId()).isEqualTo("user1");
+    assertThat(result.getId()).isEqualTo(user.getId());
+    assertThat(result.getLoginId()).isEqualTo(user.getLoginId());
+    assertThat(result.getEmail()).isEqualTo(user.getEmail());
+    assertThat(result.getGender()).isEqualTo(user.getGender());
   }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
index 2a5e7503c..50b587844 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
@@ -1,6 +1,7 @@
 package com.loopers.interfaces.api;
 
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.domain.user.UserService;
 import com.loopers.utils.DatabaseCleanUp;
 import org.junit.jupiter.api.AfterEach;
@@ -50,8 +51,7 @@ class Get {
       //given
       BigDecimal JOIN_POINT = BigDecimal.TEN;
 
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      User savedUser = userService.join(user);
+      User savedUser = userService.join(UserFixture.createUser());
 
       //when
       HttpHeaders headers = new HttpHeaders();
@@ -91,10 +91,8 @@ class Charge {
     @Test
     void 성공_포인트충전() {
       //given
-      BigDecimal CHARGE_POINT = BigDecimal.TEN;
-
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
-      User savedUser = userService.join(user);
+      BigDecimal chargeAmt = new BigDecimal(1_000);
+      User savedUser = userService.join(UserFixture.createUser());
 
       HttpHeaders headers = new HttpHeaders();
       headers.set("X-USER-ID", savedUser.getId().toString());
@@ -103,12 +101,12 @@ class Charge {
       String url = "/api/v1/user/point/charge";
       ParameterizedTypeReference<ApiResponse<BigDecimal>> resType = new ParameterizedTypeReference<>() {
       };
-      ResponseEntity<ApiResponse<BigDecimal>> res = testRestTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(new BigDecimal(1_000), headers), resType);
+      ResponseEntity<ApiResponse<BigDecimal>> res = testRestTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(chargeAmt, headers), resType);
 
       //then
       assertThat(res.getStatusCode()).isEqualTo(HttpStatus.OK);
       assertThat(res.getBody().data()).isNotNull();
-      assertEquals(0, res.getBody().data().compareTo(new BigDecimal(1_010)));
+      assertEquals(0, res.getBody().data().compareTo(savedUser.getPoint().getAmount().add(chargeAmt)));
     }
 
     @DisplayName("E2E테스트2-존재하지 않는 유저로 요청할 경우, 404 Not Found 응답을 반환")
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
index d3ce611e3..0d226bb10 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
@@ -1,6 +1,7 @@
 package com.loopers.interfaces.api;
 
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.domain.user.UserService;
 import com.loopers.interfaces.api.user.UserCreateV1Dto;
 import com.loopers.utils.DatabaseCleanUp;
@@ -92,7 +93,7 @@ class Get {
     @Test
     void 성공_정보조회() {
       //given
-      User user = User.create("user1", "user1@test.XXX", "1999-01-01", "F");
+      User user = UserFixture.createUser();
       userService.join(user);
 
       //when
diff --git a/build.gradle.kts b/build.gradle.kts
index 9c8490b8a..bf8898620 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -63,6 +63,7 @@ subprojects {
         testImplementation("com.ninja-squad:springmockk:${project.properties["springMockkVersion"]}")
         testImplementation("org.mockito:mockito-core:${project.properties["mockitoVersion"]}")
         testImplementation("org.instancio:instancio-junit:${project.properties["instancioJUnitVersion"]}")
+        testImplementation("org.instancio:instancio-core:${project.properties["instancioVersion"]}")
         // Testcontainers
         testImplementation("org.springframework.boot:spring-boot-testcontainers")
         testImplementation("org.testcontainers:testcontainers")
@@ -95,11 +96,11 @@ subprojects {
         }
         afterEvaluate {
             classDirectories.setFrom(
-                files(
-                    classDirectories.files.map {
-                        fileTree(it)
-                    },
-                ),
+                    files(
+                            classDirectories.files.map {
+                                fileTree(it)
+                            },
+                    ),
             )
         }
     }
diff --git a/gradle.properties b/gradle.properties
index 142d7120f..b8a8de7aa 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -14,5 +14,6 @@ springDocOpenApiVersion=2.7.0
 springMockkVersion=4.0.2
 mockitoVersion=5.14.0
 instancioJUnitVersion=5.0.2
+instancioVersion=5.0.2
 slackAppenderVersion=1.6.1
 kotlin.daemon.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m

From 8391e89f16c5d97ed84a99fd4255c8e4cac68fb1 Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Wed, 19 Nov 2025 00:21:58 +0900
Subject: [PATCH 07/11] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20Fix?=
 =?UTF-8?q?ture=20=EC=83=9D=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/order/CreateOrderCommand.java |  16 +-
 .../application/order/OrderFacade.java        |  36 ++--
 .../product/ProductDetailInfo.java            |   7 +-
 .../application/product/ProductFacade.java    |   8 +-
 .../loopers/domain/brand/BrandRepository.java |   2 +
 .../loopers/domain/brand/BrandService.java    |   7 +
 .../java/com/loopers/domain/like/Like.java    |   3 +
 .../domain/order/CreateOrderService.java      |  37 ----
 .../java/com/loopers/domain/order/Order.java  |  15 +-
 .../loopers/domain/order/OrderPreparer.java   |  32 ----
 .../loopers/domain/point/PointRepository.java |   4 -
 .../loopers/domain/point/PointService.java    |  12 +-
 .../com/loopers/domain/product/Product.java   |  40 +----
 .../domain/product/ProductRepository.java     |   2 +-
 .../domain/product/ProductService.java        |   4 +-
 .../domain/product/ProductStockService.java   |  26 ---
 .../java/com/loopers/domain/stock/Stock.java  |  42 +++++
 .../loopers/domain/stock/StockRepository.java |  12 ++
 .../loopers/domain/stock/StockService.java    |  34 ++++
 .../java/com/loopers/domain/user/User.java    |   2 +
 .../loopers/domain/user/UserPointService.java |  25 ---
 .../brand/BrandJpaRepository.java             |   1 +
 .../brand/BrandRepositoryImpl.java            |   6 +
 .../point/PointJpaRepository.java             |   5 +-
 .../product/ProductRepositoryImpl.java        |   2 +-
 .../stock/StockJpaRepository.java             |  18 ++
 .../stock/StockRepositoryImpl.java            |  29 ++++
 .../order/OrderFacadeConcurrencyTest.java     | 164 ++++++++++++++++++
 .../order/OrderFacadeIntegrationTest.java     |  99 ++++++-----
 .../product/ProductFacadeIntegrationTest.java |  66 +++----
 .../loopers/domain/brand/BrandFixture.java    |  45 +++++
 .../loopers/domain/brand/BrandModelTest.java  |  11 +-
 .../brand/BrandServiceIntegrationTest.java    |  21 ++-
 .../like/LikeServiceIntegrationTest.java      |   2 +-
 .../loopers/domain/like/LikeServiceTest.java  |   2 +-
 .../loopers/domain/order/MoneyFixture.java    |  35 ++++
 .../loopers/domain/order/OrderModelTest.java  |  37 ++--
 .../order/OrderServiceIntegrationTest.java    |  58 +++----
 .../com/loopers/domain/point/PointTest.java   |  23 ++-
 .../domain/product/ProductFixture.java        |  70 ++++++++
 .../domain/product/ProductModelTest.java      |  55 +-----
 .../ProductServiceIntegrationTest.java        |  41 ++---
 .../loopers/domain/stock/StockAssertions.java |  13 ++
 .../loopers/domain/stock/StockFixture.java    |  39 +++++
 .../loopers/domain/stock/StockModelTest.java  |  74 ++++++++
 .../stock/StockServiceIntegrationTest.java    |  88 ++++++++++
 46 files changed, 942 insertions(+), 428 deletions(-)
 delete mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
 delete mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java
 delete mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/stock/Stock.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/stock/StockRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/stock/StockService.java
 delete mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/stock/StockJpaRepository.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/infrastructure/stock/StockRepositoryImpl.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandFixture.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/order/MoneyFixture.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/product/ProductFixture.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/stock/StockAssertions.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/stock/StockFixture.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/stock/StockModelTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/stock/StockServiceIntegrationTest.java

diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java b/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java
index 61aa9f7e3..ceea3a4a2 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderCommand.java
@@ -2,21 +2,19 @@
 
 import com.loopers.interfaces.api.order.OrderCreateV1Dto;
 
-import java.util.Map;
-import java.util.stream.Collectors;
+import java.util.List;
+
+public record CreateOrderCommand(Long userId, List<OrderItemCommand> orderItemCommands) {
+  public record OrderItemCommand(Long productId, Long quantity) {
 
-public record CreateOrderCommand(Long userId, Map<Long, Long> orderItemInfo) {
-  public record ItemCommand(Long productId, Map<Long, Long> quantity) {
   }
 
   public static CreateOrderCommand from(Long userId, OrderCreateV1Dto.OrderRequest request) {
     return new CreateOrderCommand(
         userId,
         request.items().stream()
-            .collect(Collectors.toMap(
-                OrderCreateV1Dto.OrderItemRequest::productId,
-                OrderCreateV1Dto.OrderItemRequest::quantity,
-                Long::sum
-            )));
+            .map(item -> new OrderItemCommand(item.productId(), item.quantity()))
+            .toList()
+    );
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
index 8b45690bf..a4458e300 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
@@ -1,13 +1,11 @@
 package com.loopers.application.order;
 
-import com.loopers.domain.order.CreateOrderService;
-import com.loopers.domain.order.Order;
-import com.loopers.domain.order.OrderService;
+import com.loopers.domain.order.*;
+import com.loopers.domain.point.PointService;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
-import com.loopers.domain.product.ProductStockService;
+import com.loopers.domain.stock.StockService;
 import com.loopers.domain.user.User;
-import com.loopers.domain.user.UserPointService;
 import com.loopers.domain.user.UserService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
@@ -15,18 +13,15 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
-import java.util.Map;
 
 @RequiredArgsConstructor
 @Component
 public class OrderFacade {
   private final UserService userService;
   private final ProductService productService;
+  private final StockService stockService;
   private final OrderService orderService;
-  private final CreateOrderService createOrderService;
-
-  private final ProductStockService productStockService;
-  private final UserPointService userPointService;
+  private final PointService pointService;
 
   @Transactional(readOnly = true)
   public Page<Order> getOrderList(Long userId,
@@ -35,7 +30,7 @@ public Page<Order> getOrderList(Long userId,
                                   int size) {
     return orderService.getOrders(userId, sortType, page, size);
   }
-  
+
   @Transactional(readOnly = true)
   public OrderInfo getOrderDetail(Long orderId) {
     Order order = orderService.getOrder(orderId);
@@ -45,14 +40,23 @@ public OrderInfo getOrderDetail(Long orderId) {
   @Transactional
   public OrderInfo createOrder(CreateOrderCommand command) {
 
-    Map<Long, Long> quantityMap = command.orderItemInfo();
+    List<CreateOrderCommand.OrderItemCommand> orderItemCommands = command.orderItemCommands();
     User user = userService.getActiveUser(command.userId());
-    List<Product> productList = productService.getExistingProducts(quantityMap.keySet());
 
-    productStockService.deduct(productList, quantityMap);
-    userPointService.use(user, productList, quantityMap);
+    // 2. Command -> Domain 객체(OrderItem) 변환
+    List<OrderItem> orderItems = orderItemCommands.stream()
+        .map(cmd -> {
+          Product product = productService.getProduct(cmd.productId());
+          stockService.deduct(product.getId(), cmd.quantity()); // 재고 차감
+          return OrderItem.create(product.getId(), cmd.quantity(), product.getPrice());
+        }).toList();
+
+    // 3. 주문 생성
+    Order savedOrder = orderService.save(Order.create(user.getId(), orderItems));
 
-    Order savedOrder = createOrderService.save(user, productList, quantityMap);
+    // 4. 포인트 차감(비관적락)
+    pointService.use(user.getId(), savedOrder.getTotalPrice().getAmount());
+    savedOrder.paid();
     return OrderInfo.from(savedOrder);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
index ed39408d9..0ed1e9803 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
@@ -3,6 +3,7 @@
 import com.loopers.application.brand.BrandInfo;
 import com.loopers.application.like.LikeInfo;
 import com.loopers.domain.product.Product;
+import com.loopers.domain.stock.Stock;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 
@@ -10,15 +11,15 @@
 
 public record ProductDetailInfo(Long id, String name, BigDecimal price, long stock
     , BrandInfo brandInfo, LikeInfo likeInfo) {
-  public static ProductDetailInfo from(Product model, boolean isLiked) {
+  public static ProductDetailInfo from(Product model, Stock stock, boolean isLiked) {
     if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "상품정보를 찾을수 없습니다.");
     return new ProductDetailInfo(
         model.getId(),
         model.getName(),
         model.getPrice().getAmount(),
-        model.getStock(),
+        stock.getAvailable(),
         BrandInfo.from(model.getBrand()),
-        LikeInfo.from(model.getLikeCount(), isLiked)
+        LikeInfo.from(1, isLiked)
     );
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
index a0108db28..f2782465a 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
@@ -3,6 +3,8 @@
 import com.loopers.domain.like.LikeService;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
+import com.loopers.domain.stock.Stock;
+import com.loopers.domain.stock.StockService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Component;
@@ -12,6 +14,7 @@
 @Component
 public class ProductFacade {
   private final ProductService productService;
+  private final StockService stockService;
   private final LikeService likeService;
 
   @Transactional(readOnly = true)
@@ -25,9 +28,10 @@ public Page<Product> getProductList(Long brandId,
   @Transactional(readOnly = true)
   public ProductDetailInfo getProductDetail(long userId, long productId) {
     Product product = productService.getExistingProduct(productId);
-
+    Stock stock = stockService.findByProductId(productId);
+    
     boolean isLiked = likeService.isLiked(userId, productId);
-    return ProductDetailInfo.from(product, isLiked);
+    return ProductDetailInfo.from(product, stock, isLiked);
   }
 
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java
index ceb5791f4..07872308a 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java
@@ -1,5 +1,6 @@
 package com.loopers.domain.brand;
 
+import java.util.List;
 import java.util.Optional;
 
 public interface BrandRepository {
@@ -7,4 +8,5 @@ public interface BrandRepository {
 
   Brand save(Brand brand);
 
+  List<Brand> saveAll(List<Brand> brand);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
index 6f7bc22d1..8145a7216 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
@@ -6,6 +6,8 @@
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.List;
+
 @RequiredArgsConstructor
 @Component
 public class BrandService {
@@ -25,4 +27,9 @@ public Brand save(Brand brand) {
     return brandRepository.save(brand);
   }
 
+  @Transactional
+  public List<Brand> saveAll(List<Brand> brands) {
+    return brandRepository.saveAll(brands);
+  }
+
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
index 6351c086c..136ee5dff 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
@@ -22,6 +22,9 @@ public class Like extends BaseEntity {
   @JoinColumn(name = "ref_product_id", nullable = false)
   private Product product;
 
+  @Version
+  Long version;
+
   protected Like() {
   }
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
deleted file mode 100644
index 9b43bc189..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/CreateOrderService.java
+++ /dev/null
@@ -1,37 +0,0 @@
-
-package com.loopers.domain.order;
-
-import com.loopers.domain.product.Product;
-import com.loopers.domain.user.User;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-@RequiredArgsConstructor
-@Component
-public class CreateOrderService {
-  private final OrderRepository orderRepository;
-
-  public Order save(User user, List<Product> productList, Map<Long, Long> quantityMap) {
-
-    List<OrderItem> orderItems = new ArrayList<>();
-
-    Money totalPrice = productList.stream().map(product -> {
-      long quantity = quantityMap.getOrDefault(product.getId(), 0L);
-      Money unitPrice = product.getPrice();
-      Money itemTotalPrice = unitPrice.multiply(quantity);
-
-      OrderItem orderItem = OrderItem.create(product.getId(), quantity, unitPrice);
-      orderItems.add(orderItem);
-
-      return itemTotalPrice;
-    }).reduce(Money.wons(0), Money::add);
-
-    Order order = Order.create(user.getId(), OrderStatus.PAID, totalPrice, orderItems);
-    return orderRepository.save(order);
-  }
-
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
index 4865296a7..9d589642e 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
@@ -45,14 +45,19 @@ private Order(long refUserId, OrderStatus status, Money totalPrice, List<OrderIt
     setOrderItems(orderItems);
   }
 
-  public static Order create(long refUserId, OrderStatus status, Money totalPrice, List<OrderItem> orderItems) {
-    if (status == null) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "상태 정보는 비어있을 수 없습니다.");
-    }
+  public static Order create(long refUserId, List<OrderItem> orderItems) {
     if (orderItems == null || orderItems.isEmpty()) {
       throw new CoreException(ErrorType.BAD_REQUEST, "주문 상세내역이 없습니다.");
     }
-    return new Order(refUserId, status, totalPrice, orderItems);
+    Money totalPrice = orderItems.stream().map(item -> item.getTotalPrice()).reduce(Money.wons(0), Money::add);
+    return new Order(refUserId, OrderStatus.PENDING, totalPrice, orderItems);
+  }
+
+  public void paid() {
+    if (status == OrderStatus.PAID) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "이미 결재완료된 주문입니다.");
+    }
+    this.status = OrderStatus.PAID;
   }
 
   public void cancel() {
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java
deleted file mode 100644
index 7aa9f8a7b..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderPreparer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-
-package com.loopers.domain.order;
-
-import com.loopers.domain.product.Product;
-
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.Map;
-
-
-public final class OrderPreparer {
-  public static List<Product> verifyProductStockDeductable(List<Product> products, Map<Long, Long> quantityMap) {
-    products.forEach(i -> i.deductStock(quantityMap.getOrDefault(i.getId(), 0L)));
-    return products;
-  }
-
-  public static List<Product> verifyProductStockAddable(List<Product> products, Map<Long, Long> quantityMap) {
-    products.forEach(i -> i.addStock(quantityMap.get(i.getId())));
-    return products;
-  }
-
-  public static BigDecimal getTotalAmt(List<Product> products, Map<Long, Long> quantityMap) {
-    return products.stream()
-        .map(product -> {
-          Long productId = product.getId();
-          Long quantity = quantityMap.getOrDefault(productId, 0L);
-          return product.getPrice().getAmount().multiply(BigDecimal.valueOf(quantity));
-        })
-        .reduce(BigDecimal.ZERO, BigDecimal::add);
-  }
-
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
index 263f963a3..9bfe88f2d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
@@ -1,8 +1,5 @@
 package com.loopers.domain.point;
 
-import jakarta.persistence.LockModeType;
-import org.springframework.data.jpa.repository.Lock;
-
 import java.util.Optional;
 
 public interface PointRepository {
@@ -10,7 +7,6 @@ public interface PointRepository {
 
   Optional<Point> findByUserLoginId(String userId);
 
-  @Lock(LockModeType.PESSIMISTIC_WRITE)
   Optional<Point> findByUserIdForUpdate(Long userId);
 
   Point save(Point point);
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
index 9482ade25..48d7e95a3 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
@@ -37,18 +37,18 @@ public BigDecimal charge(User user, BigDecimal chargeAmt) {
       throw new CoreException(ErrorType.NOT_FOUND, "현재 포인트 정보를 찾을수 없습니다.");
     }
     pointOpt.get().charge(chargeAmt);
-    pointRepository.save(pointOpt.get());
-    return getAmount(user.getId());
+    Point saved = pointRepository.save(pointOpt.get());
+    return saved.getAmount();
   }
 
   @Transactional
-  public BigDecimal use(User user, BigDecimal useAmt) {
-    Optional<Point> pointOpt = pointRepository.findByUserIdForUpdate(user.getId());
+  public BigDecimal use(Long userId, BigDecimal useAmt) {
+    Optional<Point> pointOpt = pointRepository.findByUserIdForUpdate(userId);
     if (!pointOpt.isPresent()) {
       throw new CoreException(ErrorType.NOT_FOUND, "현재 포인트 정보를 찾을수 없습니다.");
     }
     pointOpt.get().use(useAmt);
-    pointRepository.save(pointOpt.get());
-    return getAmount(user.getId());
+    Point saved = pointRepository.save(pointOpt.get());
+    return saved.getAmount();
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
index 3d99209b9..9bf2740e6 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
@@ -3,8 +3,7 @@
 import com.loopers.domain.BaseEntity;
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.order.Money;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
+import com.loopers.domain.stock.Stock;
 import jakarta.persistence.*;
 import lombok.Getter;
 
@@ -19,51 +18,28 @@ public class Product extends BaseEntity {
   private Brand brand;
 
   private Money price;
-  private long stock;
 
-  @Transient
-  private long likeCount;
+  @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
+  @JoinColumn(name = "ref_stock_id", updatable = false)
+  private Stock stock;
 
   public void setBrand(Brand brand) {
     this.brand = brand;
   }
 
-  public void setLikeCount(long likeCount) {
-    this.likeCount = likeCount;
-  }
-
   protected Product() {
-    this.stock = 0;
+
   }
 
-  private Product(Brand brand, String name, Money price, long stock) {
+  private Product(Brand brand, String name, Money price, Stock stock) {
     this.setBrand(brand);
     this.name = name;
     this.price = price;
     this.stock = stock;
   }
 
-  public static Product create(Brand brand, String name, Money price, long stock) {
-    if (stock < 0) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "재고는 0보다 커야 합니다.");
-    }
-    return new Product(brand, name, price, stock);
-  }
-
-  public void deductStock(long quantity) {
-    if (quantity <= 0) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "수량은 0보다 커야 합니다.");
-    }
-    if (this.stock < quantity) {
-      throw new CoreException(ErrorType.INSUFFICIENT_STOCK, "재고가 부족합니다.");
-    }
-    this.stock -= quantity;
-  }
+  public static Product create(Brand brand, String name, Money price) {
 
-  public void addStock(long quantity) {
-    if (quantity <= 0) {
-      throw new CoreException(ErrorType.BAD_REQUEST, "수량은 0보다 커야 합니다.");
-    }
-    this.stock += quantity;
+    return new Product(brand, name, price, Stock.create(null, 0));
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
index 55173cf39..19a9bbb86 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
@@ -18,6 +18,6 @@ public interface ProductRepository {
 
   Product save(Product product);
 
-  List<Product> save(List<Product> products);
+  List<Product> saveAll(List<Product> products);
 
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
index e7c69be56..03bc9908d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
@@ -88,8 +88,8 @@ public Product save(Product product) {
   }
 
   @Transactional
-  public List<Product> save(List<Product> product) {
-    return productRepository.save(product);
+  public List<Product> saveAll(List<Product> product) {
+    return productRepository.saveAll(product);
   }
 
   private Sort getSortBySortType(String sortType) {
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
deleted file mode 100644
index bda2ed6e2..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductStockService.java
+++ /dev/null
@@ -1,26 +0,0 @@
-
-package com.loopers.domain.product;
-
-import com.loopers.domain.order.OrderPreparer;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-
-import java.util.List;
-import java.util.Map;
-
-@RequiredArgsConstructor
-@Component
-public class ProductStockService {
-  private final ProductRepository productRepository;
-
-  public void deduct(List<Product> products, Map<Long, Long> quantityMap) {
-    List<Product> deductedProducts = OrderPreparer.verifyProductStockDeductable(products, quantityMap);
-    productRepository.save(deductedProducts);
-  }
-
-  public void add(List<Product> products, Map<Long, Long> quantityMap) {
-    List<Product> addedProducts = OrderPreparer.verifyProductStockAddable(products, quantityMap);
-    productRepository.save(addedProducts);
-  }
-
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/stock/Stock.java b/apps/commerce-api/src/main/java/com/loopers/domain/stock/Stock.java
new file mode 100644
index 000000000..b196b6bf3
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/stock/Stock.java
@@ -0,0 +1,42 @@
+package com.loopers.domain.stock;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.domain.product.Product;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.*;
+import lombok.Getter;
+
+@Entity
+@Table(name = "stock")
+@Getter
+public class Stock extends BaseEntity {
+  private Long refProductId;
+  private long available = 0;
+
+  protected Stock() {
+
+  }
+
+  private Stock(Long refProductId, long available) {
+    this.refProductId = refProductId;
+    this.available = available;
+  }
+
+  public static Stock create(Long refProductId, long available) {
+    if (available < 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "재고는 0보다 커야 합니다.");
+    }
+    return new Stock(refProductId, available);
+  }
+
+  public void deduct(long quantity) {
+    if (quantity <= 0) {
+      throw new CoreException(ErrorType.BAD_REQUEST, "수량은 0보다 커야 합니다.");
+    }
+    if (this.available < quantity) {
+      throw new CoreException(ErrorType.INSUFFICIENT_STOCK, "재고가 부족합니다.");
+    }
+    this.available -= quantity;
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/stock/StockRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/stock/StockRepository.java
new file mode 100644
index 000000000..28044b05b
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/stock/StockRepository.java
@@ -0,0 +1,12 @@
+package com.loopers.domain.stock;
+
+import java.util.Optional;
+
+public interface StockRepository {
+  Optional<Stock> findByProductId(Long refProductId);
+
+  Optional<Stock> findByProductIdForUpdate(Long refProductId);
+
+  Stock save(Stock stock);
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/stock/StockService.java b/apps/commerce-api/src/main/java/com/loopers/domain/stock/StockService.java
new file mode 100644
index 000000000..aa0266ef4
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/stock/StockService.java
@@ -0,0 +1,34 @@
+
+package com.loopers.domain.stock;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@RequiredArgsConstructor
+@Component
+public class StockService {
+
+  private final StockRepository stockRepository;
+
+  @Transactional(readOnly = true)
+  public Stock findByProductId(Long productId) {
+    return stockRepository.findByProductId(productId)
+        .orElse(Stock.create(productId, 0));
+  }
+
+  @Transactional
+  public Stock save(Stock stock) {
+    return stockRepository.save(stock);
+  }
+
+  @Transactional
+  public Stock deduct(Long productId, long quantity) {
+    Stock stock = stockRepository.findByProductIdForUpdate(productId)
+        .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "상품 재고가 존재하지 않습니다."));
+    stock.deduct(quantity);
+    return stockRepository.save(stock);
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
index 55b863217..5657071a2 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
@@ -6,6 +6,7 @@
 import com.loopers.support.error.ErrorType;
 import jakarta.persistence.*;
 import lombok.Getter;
+import org.hibernate.annotations.DynamicUpdate;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
@@ -15,6 +16,7 @@
 @Entity
 @Table(name = "user")
 @Getter
+@DynamicUpdate
 public class User extends BaseEntity {
   private String loginId;
   private String email;
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
deleted file mode 100644
index 7b064cb54..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserPointService.java
+++ /dev/null
@@ -1,25 +0,0 @@
-
-package com.loopers.domain.user;
-
-import com.loopers.domain.order.OrderPreparer;
-import com.loopers.domain.point.PointService;
-import com.loopers.domain.product.Product;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.Map;
-
-@RequiredArgsConstructor
-@Component
-public class UserPointService {
-
-  private final PointService pointService;
-
-
-  public void use(User user, List<Product> productList, Map<Long, Long> quantityMap) {
-    BigDecimal totalAmt = OrderPreparer.getTotalAmt(productList, quantityMap);
-    pointService.use(user, totalAmt);
-  }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
index 72daca8e5..aa99ac6ca 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
@@ -4,4 +4,5 @@
 import org.springframework.data.jpa.repository.JpaRepository;
 
 public interface BrandJpaRepository extends JpaRepository<Brand, Long> {
+
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java
index 60f26343a..e4eb73b78 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java
@@ -5,6 +5,7 @@
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
 import java.util.Optional;
 
 @RequiredArgsConstructor
@@ -21,4 +22,9 @@ public Optional<Brand> findById(Long id) {
   public Brand save(Brand brand) {
     return jpaRepository.save(brand);
   }
+
+  @Override
+  public List<Brand> saveAll(List<Brand> brands) {
+    return jpaRepository.saveAll(brands);
+  }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
index f7969fc95..33cd00e38 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
@@ -1,7 +1,9 @@
 package com.loopers.infrastructure.point;
 
 import com.loopers.domain.point.Point;
+import jakarta.persistence.LockModeType;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Lock;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
 
@@ -12,7 +14,8 @@ public interface PointJpaRepository extends JpaRepository<Point, Long> {
   Optional<Point> findByUserLoginId(String userId);
 
   Optional<Point> findByUserId(Long userId);
-
+  
+  @Lock(LockModeType.PESSIMISTIC_WRITE)
   @Query("SELECT p FROM Point p WHERE p.user.id = :userId")
   Optional<Point> findByUserIdForUpdate(@Param("userId") Long userId);
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
index 584ac2a5c..089e79cc5 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
@@ -42,7 +42,7 @@ public Product save(Product product) {
   }
 
   @Override
-  public List<Product> save(List<Product> products) {
+  public List<Product> saveAll(List<Product> products) {
     return jpaRepository.saveAll(products);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/stock/StockJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/stock/StockJpaRepository.java
new file mode 100644
index 000000000..6562fe6d9
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/stock/StockJpaRepository.java
@@ -0,0 +1,18 @@
+package com.loopers.infrastructure.stock;
+
+import com.loopers.domain.stock.Stock;
+import jakarta.persistence.LockModeType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.Optional;
+
+public interface StockJpaRepository extends JpaRepository<Stock, Long> {
+  Optional<Stock> findByRefProductId(Long productId);
+
+  @Lock(LockModeType.PESSIMISTIC_WRITE)
+  @Query("select s from Stock s where s.refProductId = :id")
+  Optional<Stock> findByProductIdForUpdate(@Param("id") Long id);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/stock/StockRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/stock/StockRepositoryImpl.java
new file mode 100644
index 000000000..f6008f812
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/stock/StockRepositoryImpl.java
@@ -0,0 +1,29 @@
+package com.loopers.infrastructure.stock;
+
+import com.loopers.domain.stock.Stock;
+import com.loopers.domain.stock.StockRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Component
+public class StockRepositoryImpl implements StockRepository {
+  private final StockJpaRepository jpaRepository;
+
+  @Override
+  public Optional<Stock> findByProductId(Long productId) {
+    return jpaRepository.findByRefProductId(productId);
+  }
+
+  @Override
+  public Optional<Stock> findByProductIdForUpdate(Long productId) {
+    return jpaRepository.findByProductIdForUpdate(productId);
+  }
+
+  @Override
+  public Stock save(Stock stock) {
+    return jpaRepository.save(stock);
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java
new file mode 100644
index 000000000..edcde77aa
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java
@@ -0,0 +1,164 @@
+package com.loopers.application.order;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.brand.BrandService;
+import com.loopers.domain.order.Money;
+import com.loopers.domain.order.Order;
+import com.loopers.domain.order.OrderItem;
+import com.loopers.domain.order.OrderService;
+import com.loopers.domain.point.Point;
+import com.loopers.domain.point.PointService;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductFixture;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.stock.Stock;
+import com.loopers.domain.stock.StockFixture;
+import com.loopers.domain.stock.StockService;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
+import com.loopers.domain.user.UserService;
+import com.loopers.interfaces.api.order.OrderCreateV1Dto;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+
+@SpringBootTest
+class OrderFacadeConcurrencyTest {
+  @Autowired
+  private OrderFacade sut;
+  @MockitoSpyBean
+  private UserService userService;
+  @MockitoSpyBean
+  private PointService pointService;
+  @MockitoSpyBean
+  private BrandService brandService;
+  @MockitoSpyBean
+  private ProductService productService;
+  @MockitoSpyBean
+  private StockService stockService;
+
+  @MockitoSpyBean
+  private OrderService orderService;
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+
+  List<User> savedUsers;
+  List<Brand> savedBrands;
+  List<Product> savedProducts;
+  Order savedOrder;
+  Stock savedStock;
+
+  @BeforeEach
+  void setup() {
+    // arrange
+    List<User> userList = List.of(UserFixture.createUserWithLoginId("user1"), UserFixture.createUserWithLoginId("user2"));
+    savedUsers = List.of(userService.join(userList.get(0)), userService.join(userList.get(1)));
+
+    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
+    savedBrands = brandService.saveAll(brandList);
+
+    List<Product> productList = List.of(ProductFixture.createProductWith("product1", Money.wons(1))
+        , ProductFixture.createProductWith("product2", Money.wons(4))
+        , ProductFixture.createProduct(savedBrands.get(1)));
+    savedProducts = productService.saveAll(productList);
+
+    Stock stock = StockFixture.createStockWith(savedProducts.get(0).getId(), 10);
+    savedStock = stockService.save(stock);
+    stock = StockFixture.createStockWith(savedProducts.get(1).getId(), 10);
+    savedStock = stockService.save(stock);
+
+    List<OrderItem> orderItems = new ArrayList<>();
+    orderItems.add(OrderItem.create(savedProducts.get(0).getId(), 2L, Money.wons(5_000)));
+    Order order = Order.create(savedUsers.get(0).getId(), orderItems);
+    savedOrder = orderService.save(order);
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("동시에 주문해도 재고가 정상적으로 차감된다.")
+  @Test
+  void 성공_재고10_단가1원_쓰레드10() throws InterruptedException {
+    Long productId = savedProducts.get(0).getId();
+    List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
+    items.add(new OrderCreateV1Dto.OrderItemRequest(productId, 1));
+    OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
+    CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUsers.get(0).getId(), request);
+
+
+    int threadCount = 10;
+    ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+    CountDownLatch latch = new CountDownLatch(threadCount);
+
+    for (int i = 0; i < threadCount; i++) {
+      executor.submit(() -> {
+        try {
+          sut.createOrder(orderCommand);
+        } catch (Exception e) {
+          System.out.println("실패: " + e.getMessage());
+        } finally {
+          latch.countDown();
+        }
+      });
+    }
+
+    latch.await();
+
+    Stock stock = stockService.findByProductId(productId);
+    assertThat(stock.getAvailable()).isZero();
+  }
+
+//  @DisplayName("동시에 주문해도 재고가 정상적으로 차감된다.")
+//  @Test
+//  void 실패_재고10_단가4원_쓰레드10() throws InterruptedException {
+//    Long userId = savedUsers.get(0).getId();
+//    Long productId = savedProducts.get(1).getId();
+//    List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
+//    items.add(new OrderCreateV1Dto.OrderItemRequest(productId, 1));
+//    OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
+//    CreateOrderCommand orderCommand = CreateOrderCommand.from(userId, request);
+//
+//    AtomicInteger errorCount = new AtomicInteger();
+//    int threadCount = 10;
+//    ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+//    CountDownLatch latch = new CountDownLatch(threadCount);
+//
+//    for (int i = 0; i < threadCount; i++) {
+//      executor.submit(() -> {
+//        try {
+//          sut.createOrder(orderCommand);
+//        } catch (Exception e) {
+//          System.out.println("실패: " + e.getMessage());
+//          errorCount.getAndIncrement();
+//        } finally {
+//          latch.countDown();
+//        }
+//      });
+//    }
+//
+//    latch.await();
+//
+//    Stock stock = stockService.findByProductId(productId);
+//    assertThat(stock.getAvailable()).isEqualTo(0);
+//    BigDecimal bigDecimal = pointService.getAmount(userId);
+//    assertThat(bigDecimal).isEqualByComparingTo(new BigDecimal(2));
+//  }
+
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
index 7436c278a..6901bae12 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
@@ -1,18 +1,22 @@
 package com.loopers.application.order;
 
-import com.loopers.application.user.UserFacade;
 import com.loopers.domain.brand.Brand;
-import com.loopers.domain.order.Money;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.brand.BrandService;
+import com.loopers.domain.brand.BrandService;
+import com.loopers.domain.order.*;
 import com.loopers.domain.order.Order;
-import com.loopers.domain.order.OrderItem;
-import com.loopers.domain.order.OrderStatus;
 import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductFixture;
 import com.loopers.domain.product.ProductService;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.stock.Stock;
+import com.loopers.domain.stock.StockFixture;
+import com.loopers.domain.stock.StockService;
+import com.loopers.domain.stock.StockService;
 import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserFixture;
-import com.loopers.infrastructure.brand.BrandJpaRepository;
-import com.loopers.infrastructure.order.OrderJpaRepository;
-import com.loopers.infrastructure.user.UserJpaRepository;
+import com.loopers.domain.user.UserService;
 import com.loopers.interfaces.api.order.OrderCreateV1Dto;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
@@ -33,40 +37,50 @@
 @SpringBootTest
 class OrderFacadeIntegrationTest {
   @Autowired
-  private OrderFacade orderFacade;
+  private OrderFacade sut;
   @MockitoSpyBean
-  private UserJpaRepository userJpaRepository;
+  private UserService userService;
   @MockitoSpyBean
-  private BrandJpaRepository brandJpaRepository;
+  private BrandService brandService;
   @MockitoSpyBean
   private ProductService productService;
+  @MockitoSpyBean
+  private StockService stockService;
 
   @MockitoSpyBean
-  private OrderJpaRepository orderJpaRepository;
+  private OrderService orderService;
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
 
-  User savedUser;
+  List<User> savedUsers;
+  List<Brand> savedBrands;
   List<Product> savedProducts;
   Order savedOrder;
+  Stock savedStock;
 
   @BeforeEach
   void setup() {
     // arrange
-    savedUser = userJpaRepository.save(UserFixture.createUser());
-    List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
-        , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
-    List<Brand> savedBrandList = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
-
-    List<Product> productList = List.of(Product.create(savedBrandList.get(0), "Wild Faith Rose Sweatshirt", Money.wons(8), 10)
-        , Product.create(savedBrandList.get(0), "Flower Pattern Fleece Jacket", Money.wons(4), 10)
-        , Product.create(savedBrandList.get(1), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
-    );
-    savedProducts = productService.save(productList);
+    List<User> userList = List.of(UserFixture.createUserWithLoginId("user1"), UserFixture.createUserWithLoginId("user2"));
+    savedUsers = List.of(userService.join(userList.get(0)), userService.join(userList.get(1)));
+
+    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
+    savedBrands = brandService.saveAll(brandList);
+
+    List<Product> productList = List.of(ProductFixture.createProductWith("product1", Money.wons(8))
+        , ProductFixture.createProductWith("product2", Money.wons(4))
+        , ProductFixture.createProduct(savedBrands.get(1)));
+    savedProducts = productService.saveAll(productList);
+
+    Stock stock = StockFixture.createStockWith(savedProducts.get(0).getId(), 10);
+    savedStock = stockService.save(stock);
+    stock = StockFixture.createStockWith(savedProducts.get(1).getId(), 10);
+    savedStock = stockService.save(stock);
+
     List<OrderItem> orderItems = new ArrayList<>();
-    orderItems.add(OrderItem.create(productList.get(0).getId(), 2L, Money.wons(5_000)));
-    Order order = Order.create(savedUser.getId(), OrderStatus.PENDING, Money.wons(10_000), orderItems);
-    savedOrder = orderJpaRepository.save(order);
+    orderItems.add(OrderItem.create(savedProducts.get(0).getId(), 2L, Money.wons(5_000)));
+    Order order = Order.create(savedUsers.get(0).getId(), orderItems);
+    savedOrder = orderService.save(order);
 
   }
 
@@ -81,8 +95,9 @@ class GetList {
     @DisplayName("페이징 처리되어, 초기설정시 size=20, sort=최신순으로 목록이 조회된다.")
     @Test
     void 성공_주문목록조회() {
+      Long userId = savedUsers.get(0).getId();
       // act
-      Page<Order> ordersPage = orderFacade.getOrderList(savedUser.getId(), "latest", 0, 20);
+      Page<Order> ordersPage = sut.getOrderList(userId, "latest", 0, 20);
       List<Order> orders = ordersPage.getContent();
       // assert
       assertThat(orders).isNotEmpty().hasSize(1);
@@ -92,12 +107,13 @@ class GetList {
   @DisplayName("주문을 조회할 때,")
   @Nested
   class Get {
-    @DisplayName("존재하는 상품 ID를 주면, 해당 상품 정보를 반환한다.")
+    @DisplayName("존재하는 주문 ID를 주면, 해당 주문 정보를 반환한다.")
     @Test
     void 성공_존재하는_상품ID() {
       // arrange
+      Long orderId = savedOrder.getId();
       // act
-      OrderInfo result = orderFacade.getOrderDetail(savedOrder.getId());
+      OrderInfo result = sut.getOrderDetail(orderId);
 
       // assert
       assertThat(result.id()).isEqualTo(savedOrder.getId());
@@ -105,14 +121,15 @@ class Get {
       assertThat(result.totalPrice()).isEqualByComparingTo(savedOrder.getTotalPrice().getAmount());
     }
 
-    @DisplayName("존재하지 않는 상품 ID를 주면, 예외가 반환된다.")
+    @DisplayName("존재하지 않는 주문 ID를 주면, 예외가 반환된다.")
     @Test
     void 실패_존재하지_않는_상품ID() {
       // arrange
+      Long orderId = (long) -1;
       // act
       // assert
       assertThrows(CoreException.class, () -> {
-        orderFacade.getOrderDetail(0L);
+        sut.getOrderDetail(orderId);
       });
     }
   }
@@ -120,34 +137,34 @@ class Get {
   @DisplayName("주문을 생성할 때,")
   @Nested
   class Post {
-    @DisplayName("10재고가 있는 8원의 상품 1건 주문시, 기본 포인트10으로 결제가 된다.")
+    @DisplayName("10재고가 있는 8원의 'product1' 상품 1건 주문시, 기본 포인트10으로 결제가 된다.")
     @Test
     void 성공_단건주문생성() {
       List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
       items.add(new OrderCreateV1Dto.OrderItemRequest(savedProducts.get(0).getId(), 1));
       OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
-      CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUser.getId(), request);
+      CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUsers.get(0).getId(), request);
       // act
-      OrderInfo savedOrder = orderFacade.createOrder(orderCommand);
+      OrderInfo savedOrder = sut.createOrder(orderCommand);
       // assert
       assertThat(savedOrder).isNotNull();
       assertThat(savedOrder.totalPrice()).isEqualByComparingTo(savedProducts.get(0).getPrice().getAmount());
       assertThat(savedOrder.orderItemInfo()).hasSize(1);
     }
 
-    @DisplayName("10재고가 있는 8원의 상품 20건 주문시, 재고 없음 오류가 발생한다.")
+    @DisplayName("10재고가 있는 8원의 'product1'상품 20건 주문시, 재고 없음 오류가 발생한다.")
     @Test
     void 실패_재고없음오류() {
       List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
       items.add(new OrderCreateV1Dto.OrderItemRequest(savedProducts.get(0).getId(), 20));
       OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
-      CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUser.getId(), request);
+      CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUsers.get(0).getId(), request);
       // act
       // assert
-      assertThrows(CoreException.class, () -> orderFacade.createOrder(orderCommand)).getErrorType().equals(ErrorType.INSUFFICIENT_STOCK);
+      assertThrows(CoreException.class, () -> sut.createOrder(orderCommand)).getErrorType().equals(ErrorType.INSUFFICIENT_STOCK);
     }
 
-    @DisplayName("10재고가 있는 4원의 상품 3건 주문시, 포인트 부족 오류가 발생한다.")
+    @DisplayName("10재고가 있는 4원의 'product2'상품 3건 주문시, 포인트 부족 오류가 발생한다.")
     @Test
     void 실패_포인트부족오류() {
       long productId = savedProducts.get(1).getId();
@@ -155,14 +172,14 @@ class Post {
       List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
       items.add(new OrderCreateV1Dto.OrderItemRequest(productId, quantity));
       OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
-      CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUser.getId(), request);
+      CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUsers.get(0).getId(), request);
       // act
       // assert
       CoreException actualException = assertThrows(CoreException.class,
-          () -> orderFacade.createOrder(orderCommand));
+          () -> sut.createOrder(orderCommand));
       assertThat(actualException.getErrorType()).isEqualTo(ErrorType.INSUFFICIENT_POINT);
-      Product deductedProduct = productService.getProduct(productId);
-      assertThat(deductedProduct.getStock()).isEqualTo(10);
+      Stock deductedStock = stockService.findByProductId(productId);
+      assertThat(deductedStock.getAvailable()).isEqualTo(10);
     }
   }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
index 3b8460944..9f7d66b8c 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
@@ -1,20 +1,20 @@
 package com.loopers.application.product;
 
 import com.loopers.domain.brand.Brand;
-import com.loopers.domain.order.Money;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.brand.BrandRepository;
 import com.loopers.domain.product.Product;
-import com.loopers.domain.product.ProductService;
+import com.loopers.domain.product.ProductFixture;
+import com.loopers.domain.product.ProductRepository;
 import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserFixture;
-import com.loopers.infrastructure.brand.BrandJpaRepository;
-import com.loopers.infrastructure.user.UserJpaRepository;
+import com.loopers.domain.user.UserRepository;
 import com.loopers.support.error.CoreException;
 import com.loopers.utils.DatabaseCleanUp;
 import org.junit.jupiter.api.*;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.data.domain.Page;
-import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
 
 import java.util.List;
 
@@ -26,13 +26,13 @@
 @SpringBootTest
 class ProductFacadeIntegrationTest {
   @Autowired
-  private ProductFacade productFacade;
-  @MockitoSpyBean
-  private UserJpaRepository userJpaRepository;
-  @MockitoSpyBean
-  private BrandJpaRepository brandJpaRepository;
-  @MockitoSpyBean
-  private ProductService productService;
+  private ProductFacade sut;
+  @Autowired
+  private UserRepository userRepository;
+  @Autowired
+  private BrandRepository brandRepository;
+  @Autowired
+  private ProductRepository productRepository;
 
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
@@ -43,17 +43,14 @@ class ProductFacadeIntegrationTest {
   @BeforeEach
   void setup() {
     // arrange
-    savedUser = userJpaRepository.save(UserFixture.createUser());
-    List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
-        , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
-    List<Brand> savedBrandList = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
-
-    List<Product> productList = List.of(Product.create(savedBrandList.get(0), "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10)
-        , Product.create(savedBrandList.get(0), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
-        , Product.create(savedBrandList.get(1), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
-    );
-    savedProducts = productService.save(productList);
-
+    savedUser = userRepository.save(UserFixture.createUser());
+    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
+    List<Brand> savedBrands = brandRepository.saveAll(brandList);
+
+    List<Product> productList = List.of(ProductFixture.createProduct(savedBrands.get(0))
+        , ProductFixture.createProduct(savedBrands.get(0))
+        , ProductFixture.createProduct(savedBrands.get(1)));
+    savedProducts = productRepository.saveAll(productList);
   }
 
   @AfterEach
@@ -67,8 +64,10 @@ class GetList {
     @DisplayName("페이징 처리되어, 초기설정시 size=20, sort=최신순으로 목록이 조회된다.")
     @Test
     void 성공_상품목록조회() {
+      // arrange
+      Long brandId = null;
       // act
-      Page<Product> productsPage = productFacade.getProductList(null, "latest", 0, 20);
+      Page<Product> productsPage = sut.getProductList(brandId, "latest", 0, 20);
       List<Product> products = productsPage.getContent();
       // assert
       assertThat(products).isNotEmpty().hasSize(3);
@@ -78,15 +77,15 @@ class GetList {
     @Test
     void 성공_상품목록조회_브랜드ID() {
       // arrange
+      Long brandId = savedProducts.get(0).getBrand().getId();
       // act
-      Page<Product> productsPage = productFacade.getProductList(savedProducts.get(0).getId(), null, 0, 20);
-      List<Product> productList = productsPage.getContent();
+      Page<Product> productsPage = sut.getProductList(brandId, null, 0, 20);
+      List<Product> resultList = productsPage.getContent();
 
       // assert
-      assertThat(productList).isNotEmpty().hasSize(2);
-
-      assertProduct(productList.get(0), savedProducts.get(1));
-      assertProduct(productList.get(1), savedProducts.get(0));
+      assertThat(resultList).isNotEmpty().hasSize(2);
+      assertProduct(resultList.get(0), savedProducts.get(1));
+      assertProduct(resultList.get(1), savedProducts.get(0));
     }
   }
 
@@ -97,9 +96,9 @@ class Get {
     @Test
     void 성공_존재하는_상품ID() {
       // arrange
+      Long productId = savedProducts.get(0).getId();
       // act
-      ProductDetailInfo result = productFacade.getProductDetail(savedUser.getId(), savedProducts.get(0).getId());
-
+      ProductDetailInfo result = sut.getProductDetail(savedUser.getId(), productId);
       // assert
       assertThat(result.name()).isEqualTo(savedProducts.get(0).getName());
     }
@@ -108,10 +107,11 @@ class Get {
     @Test
     void 실패_존재하지_않는_상품ID() {
       // arrange
+      Long productId = (long) -1;
       // act
       // assert
       assertThrows(CoreException.class, () -> {
-        productFacade.getProductDetail(savedUser.getId(), (long) 100);
+        sut.getProductDetail(savedUser.getId(), productId);
       });
     }
   }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandFixture.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandFixture.java
new file mode 100644
index 000000000..9a9aabfcc
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandFixture.java
@@ -0,0 +1,45 @@
+package com.loopers.domain.brand;
+
+import org.instancio.Instancio;
+import org.instancio.Model;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.instancio.Select.field;
+
+public class BrandFixture {
+
+  private static final Model<Brand> BRAND_MODEL = Instancio.of(Brand.class)
+      .ignore(field(Brand::getId))
+      .ignore(field(Brand::getCreatedAt))
+      .ignore(field(Brand::getUpdatedAt))
+      .ignore(field(Brand::getDeletedAt))
+      .generate(field(Brand::getName), gen -> gen.string())
+      .generate(field(Brand::getStory), gen -> gen.string())
+      .toModel();
+
+  public static Brand createBrand() {
+    Brand brand = Instancio.of(BRAND_MODEL).create();
+    return brand;
+  }
+
+  /**
+   * 특정 필드만 override
+   */
+  public static Brand createBrandWith(String name, String story) {
+    Brand brand = Instancio.of(BRAND_MODEL)
+        .set(field(Brand::getName), name)
+        .set(field(Brand::getStory), story)
+        .create();
+    return brand;
+  }
+
+  public static List<Brand> createBrandList(int size) {
+    return Instancio.ofList(Brand.class)
+        .size(size)
+        .create();
+  }
+
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
index 5974e7877..2191e678d 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandModelTest.java
@@ -10,7 +10,7 @@
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 class BrandModelTest {
-  Brand brand;
+  Brand brand = BrandFixture.createBrand();
   String validMsg = "";
 
   @DisplayName("브랜드 모델을 생성할 때, ")
@@ -20,12 +20,9 @@ class Create_Brand {
     @Test
     void 성공_Brand_객체생성() {
       //given
-      brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
-
+      Brand result = Brand.create(brand.getName(), brand.getStory());
       //assert
-      assertThat(brand).isNotNull();
-      assertThat(brand.getName()).isEqualTo("레이브");
-      assertThat(brand.getStory()).isEqualTo("레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
+      BrandAssertions.assertBrand(brand, result);
     }
   }
 
@@ -39,7 +36,7 @@ void setup() {
     @Test
     void 실패_이름_오류() {
       assertThatThrownBy(() -> {
-        brand = Brand.create("", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
+        Brand result = Brand.create("", brand.getStory());
       }).isInstanceOf(CoreException.class).hasMessageContaining(validMsg);
     }
   }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java
index 1a7346003..ba0378607 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java
@@ -15,18 +15,20 @@
 @SpringBootTest
 class BrandServiceIntegrationTest {
   @Autowired
-  private BrandService brandService;
+  private BrandService sut;
 
   @MockitoSpyBean
-  private BrandJpaRepository brandJpaRepository;
+  private BrandRepository brandRepository;
 
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
 
-  Brand brand;
+  List<Brand> savedBrands;
 
   @BeforeEach
   void setup() {
+    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
+    savedBrands = brandRepository.saveAll(brandList);
 
   }
 
@@ -42,24 +44,21 @@ class Get {
     @Test
     void 성공_존재하는_브랜드ID() {
       // arrange
-      List<Brand> brands = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
-          , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
-      List<Brand> savedBrands = brands.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
-
+      Long brandId = savedBrands.get(0).getId();
       // act
-      Brand result = brandService.getBrand(savedBrands.get(0).getId());
+      Brand result = sut.getBrand(brandId);
 
       // assert
       assertBrand(result, savedBrands.get(0));
     }
 
-    @DisplayName("존재하지 않는 브랜드 ID를 주면, NOT_FOUND 예외가 발생한다.")
+    @DisplayName("존재하지 않는 브랜드 ID를 주면, null을 반환한다.")
     @Test
     void 실패_존재하지_않는_브랜드ID() {
       // arrange
-
+      Long brandId = (long) -1;
       // act
-      Brand result = brandService.getBrand((long) 1);
+      Brand result = sut.getBrand(brandId);
 
       // assert
       assertThat(result).isNull();
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
index 110060f50..6ec390a2a 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
@@ -51,7 +51,7 @@ void setup() {
     savedUser = userJpaRepository.save(UserFixture.createUser());
     Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
     savedBrand = brandJpaRepository.save(brand);
-    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10);
+    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", Money.wons(80_000));
     savedProduct = productJpaRepository.save(product);
   }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
index 3905ae796..0b47777e6 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
@@ -61,7 +61,7 @@ void setup() {
     savedUser = userJpaRepository.save(UserFixture.createUser());
     Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
     savedBrand = brandJpaRepository.save(brand);
-    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10);
+    Product product = Product.create(savedBrand, "Wild Faith Rose Sweatshirt", Money.wons(80_000));
     savedProduct = productJpaRepository.save(product);
 
   }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/MoneyFixture.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/MoneyFixture.java
new file mode 100644
index 000000000..bf8a9c03f
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/MoneyFixture.java
@@ -0,0 +1,35 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.product.Product;
+import org.instancio.Instancio;
+import org.instancio.Model;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.instancio.Select.field;
+
+public class MoneyFixture {
+
+  private static final Model<Money> MODEL = Instancio.of(Money.class)
+      .generate(field(Money::getAmount),
+          gen -> gen.math().bigDecimal().scale(0).min(BigDecimal.ZERO))
+      .generate(field(Money::getCurrency),
+          gen -> gen.enumOf(CurrencyCode.class))
+      .toModel();
+
+  public static Money create() {
+    return Instancio.of(MODEL).create();
+  }
+
+  public static Money createWith(BigDecimal amount, CurrencyCode currency) {
+    return Instancio.of(MODEL)
+        .set(field(Money::getAmount), amount)
+        .set(field(Money::getCurrency), currency)
+        .create();
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
index a3d605ee4..1b322f477 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
@@ -29,31 +29,19 @@ class Create_Order {
     @DisplayName("사용자ID, 상태, 지불금액,총금액, 주문일자가 모두 주어지면, 정상적으로 생성된다.")
     @Test
     void 성공_Order_객체생성() {
-      order = Order.create(1, OrderStatus.PENDING, Money.wons(10_000), orderItems);
+      order = Order.create(1, orderItems);
       assertThat(order.getRefUserId()).isEqualTo(1);
       assertThat(order.getStatus()).isEqualTo(OrderStatus.PENDING);
 
     }
   }
 
-  @DisplayName("주문 모델을 생성할 때, 검증")
-  @Nested
-  class Valid_Order {
-
-    @Test
-    void 실패_총가격_음수오류() {
-      assertThrows(IllegalArgumentException.class, () -> {
-        order = Order.create(1, OrderStatus.PENDING, Money.wons(-10_000), orderItems);
-      });
-    }
-  }
-
-  @DisplayName("주문 확인후, 상품준비중 상태로 변경")
+  @DisplayName("주문 확인시, 상품준비중 상태로 변경")
   @Nested
   class Valid_상품준비중 {
     @Test
     void 실패_주문확인_결제전오류() {
-      order = Order.create(1, OrderStatus.PENDING, Money.wons(10_000), orderItems);
+      order = Order.create(1, orderItems);
       assertThatThrownBy(() -> {
         order.preparing();
       }).isInstanceOf(CoreException.class).hasMessageContaining("결제대기중");
@@ -61,23 +49,19 @@ class Valid_상품준비중 {
 
     @Test
     void 실패_주문확인_상품준비중오류() {
-      order = Order.create(1, OrderStatus.PREPARING, Money.wons(10_000), orderItems);
+      order = Order.create(1, orderItems);
+      order.paid();
+      order.preparing();
       assertThatThrownBy(() -> {
         order.preparing();
       }).isInstanceOf(CoreException.class).hasMessageContaining("이미 준비중");
     }
 
-    @Test
-    void 실패_주문확인_상품준비완료오류() {
-      order = Order.create(1, OrderStatus.SHIPPED, Money.wons(10_000), orderItems);
-      assertThatThrownBy(() -> {
-        order.preparing();
-      }).isInstanceOf(CoreException.class).hasMessageContaining("상품준비 완료");
-    }
 
     @Test
     void 성공_상품준비중() {
-      order = Order.create(1, OrderStatus.PAID, Money.wons(10_000), orderItems);
+      order = Order.create(1, orderItems);
+      order.paid();
       order.preparing();
       assertThat(order.getStatus()).isEqualTo(OrderStatus.PREPARING);
     }
@@ -88,7 +72,8 @@ class Valid_상품준비중 {
   class Valid_주문취소 {
     @Test
     void 실패_주문취소_이미취소된주문오류() {
-      order = Order.create(1, OrderStatus.CANCELLED, Money.wons(10_000), orderItems);
+      order = Order.create(1, orderItems);
+      order.cancel();
       assertThrows(CoreException.class, () -> {
         order.cancel();
       });
@@ -96,7 +81,7 @@ class Valid_주문취소 {
 
     @Test
     void 성공_주문취소() {
-      order = Order.create(1, OrderStatus.PENDING, Money.wons(10_000), orderItems);
+      order = Order.create(1, orderItems);
       order.cancel();
       assertThat(order.getStatus()).isEqualTo(OrderStatus.CANCELLED);
     }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
index 48861d185..85b5e3fe2 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
@@ -1,13 +1,14 @@
 package com.loopers.domain.order;
 
 import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.brand.BrandRepository;
 import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductFixture;
+import com.loopers.domain.product.ProductRepository;
 import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserFixture;
-import com.loopers.infrastructure.brand.BrandJpaRepository;
-import com.loopers.infrastructure.order.OrderJpaRepository;
-import com.loopers.infrastructure.product.ProductJpaRepository;
-import com.loopers.infrastructure.user.UserJpaRepository;
+import com.loopers.domain.user.UserRepository;
 import com.loopers.utils.DatabaseCleanUp;
 import org.junit.jupiter.api.*;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -30,17 +31,17 @@
 @Transactional
 class OrderServiceIntegrationTest {
   @Autowired
-  private OrderService orderService;
+  private OrderService sut;
 
   @MockitoSpyBean
-  private UserJpaRepository userJpaRepository;
+  private UserRepository userRepository;
 
   @MockitoSpyBean
-  private BrandJpaRepository brandJpaRepository;
+  private BrandRepository brandRepository;
   @MockitoSpyBean
-  private ProductJpaRepository productJpaRepository;
+  private ProductRepository productRepository;
   @MockitoSpyBean
-  private OrderJpaRepository orderJpaRepository;
+  private OrderRepository orderRepository;
 
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
@@ -52,22 +53,19 @@ class OrderServiceIntegrationTest {
 
   @BeforeEach
   void setup() {
-    savedUser = userJpaRepository.save(UserFixture.createUser());
+    savedUser = userRepository.save(UserFixture.createUser());
+    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
+    savedBrands = brandRepository.saveAll(brandList);
 
-    List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
-        , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
-    savedBrands = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
-
-    List<Product> productList = List.of(Product.create(savedBrands.get(0), "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10)
-        , Product.create(savedBrands.get(0), "Flower Pattern Fleece Jacket", Money.wons(80_000), 20)
-        , Product.create(savedBrands.get(1), "Flower Pattern Fleece Jacket", Money.wons(80_000), 20)
-    );
-    savedProducts = productList.stream().map((product) -> productJpaRepository.save(product)).toList();
+    List<Product> productList = List.of(ProductFixture.createProduct(savedBrands.get(0))
+        , ProductFixture.createProduct(savedBrands.get(0))
+        , ProductFixture.createProduct(savedBrands.get(1)));
+    savedProducts = productRepository.saveAll(productList);
 
     List<OrderItem> orderItems = new ArrayList<>();
     orderItems.add(OrderItem.create(productList.get(0).getId(), 2L, Money.wons(5_000)));
-    Order order = Order.create(savedUser.getId(), OrderStatus.PENDING, Money.wons(10_000), orderItems);
-    savedOrder = orderJpaRepository.save(order);
+    Order order = Order.create(savedUser.getId(), orderItems);
+    savedOrder = orderRepository.save(order);
 
   }
 
@@ -83,9 +81,9 @@ class GetList {
     @Test
     void 성공_상품목록조회() {
       // arrange
-
+      Long userId = savedUser.getId();
       // act
-      Page<Order> ordersPage = orderService.getOrders(savedUser.getId(), "latest", 0, 20);
+      Page<Order> ordersPage = sut.getOrders(userId, "latest", 0, 20);
       List<Order> orders = ordersPage.getContent();
       // assert
       assertThat(orders).isNotEmpty().hasSize(1);
@@ -99,8 +97,9 @@ class Get {
     @Test
     void 성공_존재하는_주문ID() {
       // arrange
+      Long orderId = savedOrder.getId();
       // act
-      Order result = orderService.getOrder(savedOrder.getId());
+      Order result = sut.getOrder(orderId);
 
       // assert
       assertOrder(result, savedOrder);
@@ -110,10 +109,9 @@ class Get {
     @Test
     void 실패_존재하지_않는_주문ID() {
       // arrange
-
+      Long orderId = (long) -1;
       // act
-      Order result = orderService.getOrder((long) 99999);
-
+      Order result = sut.getOrder(orderId);
       // assert
       assertThat(result).isNull();
     }
@@ -128,14 +126,14 @@ class Save {
       // arrange
       List<OrderItem> orderItems = new ArrayList<>();
       orderItems.add(OrderItem.create(savedProducts.get(0).getId(), 2L, Money.wons(5_000)));
-      Order order = Order.create(savedUser.getId(), OrderStatus.PENDING, Money.wons(10_000), orderItems);
+      Order order = Order.create(savedUser.getId(), orderItems);
 
       // act
-      orderService.save(order);
+      sut.save(order);
 
       // assert
       assertAll(
-          () -> verify(orderJpaRepository, times(1)).save(order)
+          () -> verify(orderRepository, times(1)).save(order)
       );
     }
   }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
index cfe26b8dd..c97dba985 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
@@ -29,25 +29,29 @@ class Charge {
     @Test
     void 실패_포인트충전_0() {
       // arrange
-      point = PointFixture.createPoint();
+      BigDecimal pointAmt = new BigDecimal(1);
+      BigDecimal chargeAmt = new BigDecimal(0);
+
+      point = PointFixture.createPointWith(pointAmt);
 
       // act, assert
       assertThatThrownBy(() -> {
-        point.charge(BigDecimal.ZERO);
+        point.charge(chargeAmt);
       }).isInstanceOf(CoreException.class).hasMessageContaining("충전 금액은 0보다 커야 합니다.");
     }
 
     @Test
     void 성공_포인트충전() {
       // arrange
+      BigDecimal pointAmt = new BigDecimal(1);
       BigDecimal chargeAmt = new BigDecimal(5);
-      point = PointFixture.createPoint();
+      point = PointFixture.createPointWith(pointAmt);
 
       // act
       point.charge(chargeAmt);
 
       // assert
-      assertThat(point.getAmount()).isEqualTo(point.getAmount().add(chargeAmt));
+      assertThat(point.getAmount()).isEqualTo(pointAmt.add(chargeAmt));
     }
   }
 
@@ -57,25 +61,28 @@ class Use {
     @Test
     void 실패_포인트사용() {
       // arrange
-      point = PointFixture.createPointWith(BigDecimal.ONE);
+      BigDecimal pointAmt = new BigDecimal(1);
+      BigDecimal useAmt = new BigDecimal(10);
+      point = PointFixture.createPointWith(pointAmt);
 
       // act, assert
       assertThatThrownBy(() -> {
-        point.use(BigDecimal.TEN);
+        point.use(useAmt);
       }).isInstanceOf(CoreException.class).hasMessageContaining("포인트가 부족합니다.");
     }
 
     @Test
     void 성공_포인트사용() {
       // arrange
+      BigDecimal pointAmt = new BigDecimal(20);
       BigDecimal useAmt = new BigDecimal(5);
-      point = PointFixture.createPointWith(new BigDecimal(20));
+      point = PointFixture.createPointWith(pointAmt);
 
       // act
       point.use(useAmt);
 
       // assert
-      assertThat(point.getAmount()).isEqualTo(point.getAmount().subtract(useAmt));
+      assertThat(point.getAmount()).isEqualTo(pointAmt.subtract(useAmt));
     }
   }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductFixture.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductFixture.java
new file mode 100644
index 000000000..5528e3684
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductFixture.java
@@ -0,0 +1,70 @@
+package com.loopers.domain.product;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.order.Money;
+import com.loopers.domain.order.MoneyFixture;
+import org.instancio.Instancio;
+import org.instancio.Model;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.instancio.Select.field;
+
+public class ProductFixture {
+
+  private static final Model<Product> PRODUCT_MODEL = Instancio.of(Product.class)
+      .ignore(field(Product::getId))
+      .ignore(field(Product::getCreatedAt))
+      .ignore(field(Product::getUpdatedAt))
+      .ignore(field(Product::getDeletedAt))
+      .ignore(field(Product::getBrand))
+      .generate(field(Product::getName), gen -> gen.string())
+      .set(field(Product::getPrice), MoneyFixture.create())
+      .toModel();
+
+  public static Product createProduct() {
+    Product product = Instancio.of(PRODUCT_MODEL).create();
+    Brand brand = BrandFixture.createBrand();
+    product.setBrand(brand);
+    return product;
+  }
+
+  public static Product createProduct(Brand brand) {
+    Product product = Instancio.of(PRODUCT_MODEL).create();
+    product.setBrand(brand);
+    return product;
+  }
+
+  /**
+   * 특정 필드만 override
+   */
+  public static Product createProductWith(String name, Money price) {
+    Product stock = Instancio.of(PRODUCT_MODEL)
+        .set(field(Product::getName), name)
+        .set(field(Product::getPrice), price)
+        .create();
+    return stock;
+  }
+
+  public static Product createUserWithoutBrand() {
+    return Instancio.of(PRODUCT_MODEL).create();
+  }
+
+  public static List<Product> createProductList(int size) {
+    return Instancio.ofList(Product.class)
+        .size(size)
+        .create();
+  }
+
+  public static List<Product> createProductList(List<Brand> brandList) {
+    return IntStream.range(0, brandList.size())
+        .mapToObj(i -> Instancio.of(PRODUCT_MODEL)
+            .set(field(Product::getBrand), brandList.get(i))
+            .create()
+        )
+        .collect(Collectors.toList());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
index 4df13baa5..8794019ed 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductModelTest.java
@@ -1,7 +1,10 @@
 package com.loopers.domain.product;
 
 import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandFixture;
 import com.loopers.domain.order.Money;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.support.error.CoreException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
@@ -12,21 +15,18 @@
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 class ProductModelTest {
-  Brand brand = Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.");
-  Product product;
+  Brand brand = BrandFixture.createBrand();
+  Product product = ProductFixture.createProduct(brand);
   String validMsg = "";
 
   @DisplayName("상품 모델을 생성할 때, ")
   @Nested
   class Create_Product {
-    @DisplayName("브랜드모델, 상품명, 가격, 재고, 예약재고가 모두 주어지면, 정상적으로 생성된다.")
+    @DisplayName("브랜드모델, 상품명, 가격이 모두 주어지면, 정상적으로 생성된다.")
     @Test
     void 성공_Product_객체생성() {
-      product = Product.create(brand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10);
-      assertThat(product).isNotNull();
-      assertThat(product.getName()).isEqualTo("Wild Faith Rose Sweatshirt");
-      assertThat(product.getPrice()).isEqualTo(Money.wons(80_000));
-      assertThat(product.getStock()).isEqualTo(10);
+      Product result = Product.create(product.getBrand(), product.getName(), product.getPrice());
+      ProductAssertions.assertProduct(product, result);
     }
   }
 
@@ -41,45 +41,8 @@ void setup() {
     @Test
     void 실패_가격_음수오류() {
       assertThatThrownBy(() -> {
-        product = Product.create(brand, "Wild Faith Rose Sweatshirt", Money.wons(-1_000), 0);
+        Product.create(product.getBrand(), product.getName(), Money.wons(-1_000));
       }).isInstanceOf(IllegalArgumentException.class);
     }
-
-    @Test
-    void 실패_재고_음수오류() {
-      assertThatThrownBy(() -> {
-        product = Product.create(brand, "Wild Faith Rose Sweatshirt", Money.wons(80_000), -1);
-      }).isInstanceOf(CoreException.class);
-    }
-
-  }
-
-  @DisplayName("상품 모델을 생성후, 재고 예약")
-  @Nested
-  class Valid_재고차감 {
-    @BeforeEach
-    void setup() {
-      product = Product.create(brand, "Wild Faith Rose Sweatshirt", Money.wons(1_000), 10);
-    }
-
-    @Test
-    void 실패_예약재고0_차감오류() {
-      assertThatThrownBy(() -> {
-        product.deductStock(0);
-      }).isInstanceOf(CoreException.class);
-    }
-
-    @Test
-    void 실패_예약재고20_차감오류() {
-      assertThatThrownBy(() -> {
-        product.deductStock(20);
-      }).isInstanceOf(CoreException.class);
-    }
-
-    @Test
-    void 성공_재고예약() {
-      product.deductStock(2);
-      assertThat(product.getStock()).isEqualTo(8);
-    }
   }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
index df85a8c15..f4e0cdbdf 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
@@ -1,9 +1,8 @@
 package com.loopers.domain.product;
 
 import com.loopers.domain.brand.Brand;
-import com.loopers.domain.order.Money;
-import com.loopers.infrastructure.brand.BrandJpaRepository;
-import com.loopers.infrastructure.product.ProductJpaRepository;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.brand.BrandRepository;
 import com.loopers.utils.DatabaseCleanUp;
 import org.junit.jupiter.api.*;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -23,12 +22,12 @@
 @Transactional
 class ProductServiceIntegrationTest {
   @Autowired
-  private ProductService productService;
+  private ProductService sut;
 
   @MockitoSpyBean
-  private BrandJpaRepository brandJpaRepository;
+  private BrandRepository brandRepository;
   @MockitoSpyBean
-  private ProductJpaRepository productJpaRepository;
+  private ProductRepository productRepository;
 
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
@@ -38,16 +37,13 @@ class ProductServiceIntegrationTest {
 
   @BeforeEach
   void setup() {
-    List<Brand> brandList = List.of(Brand.create("레이브", "레이브는 음악, 영화, 예술 등 다양한 문화에서 영감을 받아 경계 없고 자유분방한 스타일을 제안하는 패션 레이블입니다.")
-        , Brand.create("마뗑킴", "마뗑킴은 트렌디하면서도 편안함을 더한 디자인을 선보입니다. 일상에서 조화롭게 적용할 수 있는 자연스러운 패션 문화를 지향합니다."));
-    savedBrands = brandList.stream().map((brand) -> brandJpaRepository.save(brand)).toList();
-
-    List<Product> productList = List.of(Product.create(savedBrands.get(0), "Wild Faith Rose Sweatshirt", Money.wons(80_000), 10)
-        , Product.create(savedBrands.get(0), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
-        , Product.create(savedBrands.get(1), "Flower Pattern Fleece Jacket", Money.wons(178_000), 20)
-    );
-    savedProducts = productList.stream().map((product) -> productJpaRepository.save(product)).toList();
+    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
+    savedBrands = brandRepository.saveAll(brandList);
 
+    List<Product> productList = List.of(ProductFixture.createProduct(savedBrands.get(0))
+        , ProductFixture.createProduct(savedBrands.get(0))
+        , ProductFixture.createProduct(savedBrands.get(1)));
+    savedProducts = productRepository.saveAll(productList);
   }
 
   @AfterEach
@@ -62,9 +58,9 @@ class GetList {
     @Test
     void 성공_상품목록조회() {
       // arrange
-
+      Long brandId = null;
       // act
-      Page<Product> productsPage = productService.getProducts(null, "latest", 0, 20);
+      Page<Product> productsPage = sut.getProducts(brandId, "latest", 0, 20);
       List<Product> products = productsPage.getContent();
       // assert
       assertThat(products).isNotEmpty().hasSize(3);
@@ -74,9 +70,9 @@ class GetList {
     @Test
     void 성공_상품목록조회_브랜드ID() {
       // arrange
-
+      Long brandId = savedBrands.get(0).getId();
       // act
-      Page<Product> productsPage = productService.getProducts(savedBrands.get(0).getId(), null, 0, 20);
+      Page<Product> productsPage = sut.getProducts(brandId, null, 0, 20);
       List<Product> productList = productsPage.getContent();
 
       // assert
@@ -94,8 +90,9 @@ class Get {
     @Test
     void 성공_존재하는_상품ID() {
       // arrange
+      Long productId = savedProducts.get(0).getId();
       // act
-      Product result = productService.getProduct(savedProducts.get(0).getId());
+      Product result = sut.getProduct(productId);
 
       // assert
       assertProduct(result, savedProducts.get(0));
@@ -106,9 +103,9 @@ class Get {
     @Test
     void 실패_존재하지_않는_상품ID() {
       // arrange
-
+      Long productId = (long) -1;
       // act
-      Product result = productService.getProduct((long) 1000);
+      Product result = sut.getProduct(productId);
 
       // assert
       assertThat(result).isNull();
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockAssertions.java b/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockAssertions.java
new file mode 100644
index 000000000..a8fde045b
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockAssertions.java
@@ -0,0 +1,13 @@
+package com.loopers.domain.stock;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class StockAssertions {
+  public static void assertStock(Stock actual, Stock expected) {
+    assertThat(actual).isNotNull();
+    assertThat(expected).isNotNull();
+    assertThat(actual.getId()).isEqualTo(expected.getId());
+    assertThat(actual.getRefProductId()).isEqualTo(expected.getRefProductId());
+    assertThat(actual.getAvailable()).isEqualTo(expected.getAvailable());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockFixture.java b/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockFixture.java
new file mode 100644
index 000000000..70559d438
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockFixture.java
@@ -0,0 +1,39 @@
+package com.loopers.domain.stock;
+
+import org.instancio.Instancio;
+import org.instancio.Model;
+
+import static org.instancio.Select.field;
+
+public class StockFixture {
+
+  private static final Model<Stock> STOCK_MODEL = Instancio.of(Stock.class)
+      .ignore(field(Stock::getId))
+      .ignore(field(Stock::getCreatedAt))
+      .ignore(field(Stock::getUpdatedAt))
+      .ignore(field(Stock::getDeletedAt))
+      .generate(field(Stock::getRefProductId), gen -> gen.longs().min(0L))
+      .generate(field(Stock::getAvailable),
+          gen -> gen.longs().min(0L)
+      )
+      .toModel();
+
+  /**
+   * Stock + Point 자동 연결
+   */
+  public static Stock createStock() {
+    Stock stock = Instancio.of(STOCK_MODEL).create();
+    return stock;
+  }
+
+  /**
+   * 특정 필드만 override
+   */
+  public static Stock createStockWith(Long productId, long available) {
+    Stock stock = Instancio.of(STOCK_MODEL)
+        .set(field(Stock::getRefProductId), productId)
+        .set(field(Stock::getAvailable), available)
+        .create();
+    return stock;
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockModelTest.java
new file mode 100644
index 000000000..28bca4e55
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockModelTest.java
@@ -0,0 +1,74 @@
+package com.loopers.domain.stock;
+
+import com.loopers.support.error.CoreException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class StockModelTest {
+  Stock stock;
+  String validMsg = "";
+
+  @DisplayName("상품 모델을 생성할 때, ")
+  @Nested
+  class Create_Stock {
+    @DisplayName("브랜드모델, 상품명, 가격, 재고, 예약재고가 모두 주어지면, 정상적으로 생성된다.")
+    @Test
+    void 성공_Stock_객체생성() {
+      stock = StockFixture.createStock();
+      Stock result = Stock.create(stock.getRefProductId(), stock.getAvailable());
+      StockAssertions.assertStock(stock, result);
+    }
+  }
+
+  @DisplayName("상품 모델을 생성할 때, 검증")
+  @Nested
+  class Valid_Stock {
+    @BeforeEach
+    void setup() {
+      validMsg = "형식이 유효하지 않습니다.";
+    }
+
+    @Test
+    void 실패_재고_음수오류() {
+      assertThatThrownBy(() -> {
+        stock = StockFixture.createStockWith(1L, -1000);
+        Stock.create(stock.getRefProductId(), stock.getAvailable());
+      }).isInstanceOf(CoreException.class);
+    }
+
+  }
+
+  @DisplayName("상품 모델을 생성후, 재고 예약")
+  @Nested
+  class Valid_재고차감 {
+    @BeforeEach
+    void setup() {
+      stock = StockFixture.createStock();
+    }
+
+    @Test
+    void 실패_차감재고0_차감오류() {
+      assertThatThrownBy(() -> {
+        stock.deduct(0);
+      }).isInstanceOf(CoreException.class);
+    }
+
+    @Test
+    void 실패_차감재고20_차감오류() {
+      assertThatThrownBy(() -> {
+        stock.deduct(stock.getAvailable() + 20);
+      }).isInstanceOf(CoreException.class);
+    }
+
+    @Test
+    void 성공_재고예약() {
+      stock.deduct(stock.getAvailable() - 1);
+      assertThat(stock.getAvailable()).isEqualTo(1);
+    }
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockServiceIntegrationTest.java
new file mode 100644
index 000000000..a1f06dc3a
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/stock/StockServiceIntegrationTest.java
@@ -0,0 +1,88 @@
+package com.loopers.domain.stock;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.brand.BrandRepository;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductFixture;
+import com.loopers.domain.product.ProductRepository;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+@SpringBootTest
+@Transactional
+class StockServiceIntegrationTest {
+  @Autowired
+  private StockService sut;
+
+  @MockitoSpyBean
+  private BrandRepository brandRepository;
+  @MockitoSpyBean
+  private ProductRepository productRepository;
+  @MockitoSpyBean
+  private StockRepository stockRepository;
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+
+  List<Brand> savedBrands;
+  List<Product> savedProducts;
+  Stock savedStock;
+
+  @BeforeEach
+  void setup() {
+    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
+    savedBrands = brandRepository.saveAll(brandList);
+
+    List<Product> productList = List.of(ProductFixture.createProduct(savedBrands.get(0))
+        , ProductFixture.createProduct(savedBrands.get(0))
+        , ProductFixture.createProduct(savedBrands.get(1)));
+    savedProducts = productRepository.saveAll(productList);
+
+    Stock stock = StockFixture.createStockWith(savedProducts.get(0).getId(), 10);
+    savedStock = stockRepository.save(stock);
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("재고를 조회할 때,")
+  @Nested
+  class Get {
+    @DisplayName("존재하는 상품 ID를 주면, 해당 상품의 재고 정보를 반환한다.")
+    @Test
+    void 성공_존재하는_상품ID() {
+      // arrange
+      Long productId = savedProducts.get(0).getId();
+      // act
+      Stock result = sut.findByProductId(productId);
+
+      // assert
+      assertThat(result.getAvailable()).isEqualTo(savedStock.getAvailable());
+    }
+
+    @DisplayName("존재하지 않는 상품 ID를 주면, 재고0이 반환된다.")
+    @Test
+    void 실패_존재하지_않는_상품ID() {
+      // arrange
+      Long productId = (long) -1;
+      // act
+      Stock result = sut.findByProductId(productId);
+
+      // assert
+      assertThat(result).isNotNull();
+      assertThat(result.getAvailable()).isZero();
+    }
+  }
+
+}

From 62d4b91a6781e1c35a2c21d46b91f29964b49379 Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Thu, 20 Nov 2025 02:07:21 +0900
Subject: [PATCH 08/11] =?UTF-8?q?test:=20=EC=A3=BC=EB=AC=B8=20=EB=8F=99?=
 =?UTF-8?q?=EC=8B=9C=EC=84=B1=20=ED=85=8C=EC=8A=A4=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/order/OrderFacade.java        | 41 ++++++---
 .../java/com/loopers/domain/order/Order.java  | 10 ++-
 .../com/loopers/domain/order/OrderItem.java   | 10 ++-
 .../loopers/domain/order/OrderService.java    |  1 +
 .../java/com/loopers/domain/point/Point.java  |  2 +-
 .../loopers/domain/point/PointService.java    |  9 +-
 .../domain/product/ProductService.java        |  4 +-
 .../java/com/loopers/domain/user/User.java    |  3 +-
 .../point/PointRepositoryImpl.java            |  2 +-
 .../user/UserRepositoryImpl.java              |  2 +-
 .../order/OrderFacadeConcurrencyTest.java     | 83 ++++++++++---------
 .../com/loopers/domain/user/UserLazyTest.java | 61 ++++++++++++++
 12 files changed, 160 insertions(+), 68 deletions(-)
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/domain/user/UserLazyTest.java

diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
index a4458e300..fc1afb1e3 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
@@ -1,27 +1,37 @@
 package com.loopers.application.order;
 
 import com.loopers.domain.order.*;
+import com.loopers.domain.point.Point;
+import com.loopers.domain.point.PointRepository;
 import com.loopers.domain.point.PointService;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
 import com.loopers.domain.stock.StockService;
 import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserRepository;
 import com.loopers.domain.user.UserService;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
 import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.exception.ContextedRuntimeException;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
+import java.util.Optional;
 
 @RequiredArgsConstructor
 @Component
 public class OrderFacade {
   private final UserService userService;
+  private final UserRepository userRepository;
   private final ProductService productService;
   private final StockService stockService;
   private final OrderService orderService;
   private final PointService pointService;
+  private final PointRepository pointRepository;
 
   @Transactional(readOnly = true)
   public Page<Order> getOrderList(Long userId,
@@ -41,22 +51,31 @@ public OrderInfo getOrderDetail(Long orderId) {
   public OrderInfo createOrder(CreateOrderCommand command) {
 
     List<CreateOrderCommand.OrderItemCommand> orderItemCommands = command.orderItemCommands();
-    User user = userService.getActiveUser(command.userId());
+    Long userId = command.userId();
+    List<Long> productIds = orderItemCommands.stream().map(CreateOrderCommand.OrderItemCommand::productId).toList();
+    List<Product> products = productService.getExistingProducts(productIds);
 
     // 2. Command -> Domain 객체(OrderItem) 변환
-    List<OrderItem> orderItems = orderItemCommands.stream()
-        .map(cmd -> {
-          Product product = productService.getProduct(cmd.productId());
-          stockService.deduct(product.getId(), cmd.quantity()); // 재고 차감
-          return OrderItem.create(product.getId(), cmd.quantity(), product.getPrice());
-        }).toList();
-
+    for (CreateOrderCommand.OrderItemCommand item : orderItemCommands) {
+      stockService.deduct(item.productId(), item.quantity()); // 재고 차감
+    }
     // 3. 주문 생성
-    Order savedOrder = orderService.save(Order.create(user.getId(), orderItems));
+    List<OrderItem> orderItems = orderItemCommands.stream()
+        .map(item ->
+        {
+          Long productId = item.productId();
+          Product product = products.stream().filter(p -> p.getId().equals(productId)).findFirst()
+              .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, ""));
+          return OrderItem.create(productId, item.quantity(), product.getPrice());
+        })
+        .toList();
+    Order order = Order.create(userId, orderItems);
+    Order savedOrder = orderService.save(order);
+    savedOrder.paid();
 
     // 4. 포인트 차감(비관적락)
-    pointService.use(user.getId(), savedOrder.getTotalPrice().getAmount());
-    savedOrder.paid();
+    pointService.use(userId, order.getTotalPrice().getAmount());
     return OrderInfo.from(savedOrder);
   }
+
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
index 9d589642e..e6387dcd4 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
@@ -37,10 +37,9 @@ private void setOrderItems(List<OrderItem> orderItems) {
   protected Order() {
   }
 
-  private Order(long refUserId, OrderStatus status, Money totalPrice, List<OrderItem> orderItems) {
+  private Order(long refUserId, OrderStatus status, List<OrderItem> orderItems) {
     this.refUserId = refUserId;
     this.status = status;
-    this.totalPrice = totalPrice;
     this.orderAt = ZonedDateTime.now();
     setOrderItems(orderItems);
   }
@@ -49,8 +48,7 @@ public static Order create(long refUserId, List<OrderItem> orderItems) {
     if (orderItems == null || orderItems.isEmpty()) {
       throw new CoreException(ErrorType.BAD_REQUEST, "주문 상세내역이 없습니다.");
     }
-    Money totalPrice = orderItems.stream().map(item -> item.getTotalPrice()).reduce(Money.wons(0), Money::add);
-    return new Order(refUserId, OrderStatus.PENDING, totalPrice, orderItems);
+    return new Order(refUserId, OrderStatus.PENDING, orderItems);
   }
 
   public void paid() {
@@ -79,4 +77,8 @@ public void preparing() {
     }
     this.status = OrderStatus.PREPARING;
   }
+
+  public Money getTotalPrice() {
+    return orderItems.stream().map(item -> item.getTotalPrice()).reduce(Money.wons(0), Money::add);
+  }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
index c8820a684..8a0afb233 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
@@ -39,18 +39,20 @@ public class OrderItem extends BaseEntity {
   protected OrderItem() {
   }
 
-  private OrderItem(long refProductId, long quantity, Money unitPrice, Money totalPrice) {
+  private OrderItem(long refProductId, long quantity, Money unitPrice) {
     this.refProductId = refProductId;
     this.quantity = quantity;
     this.unitPrice = unitPrice;
-    this.totalPrice = totalPrice;
   }
 
   public static OrderItem create(long refProductId, long quantity, Money unitPrice) {
     if (quantity <= 0) {
       throw new CoreException(ErrorType.BAD_REQUEST, "수량은 0보다 커야 합니다.");
     }
-    Money totalPrice = unitPrice.multiply(quantity);
-    return new OrderItem(refProductId, quantity, unitPrice, totalPrice);
+    return new OrderItem(refProductId, quantity, unitPrice);
+  }
+
+  public Money getTotalPrice() {
+    return this.unitPrice.multiply(this.quantity);
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
index c832fcb8a..6acc8d16a 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
@@ -40,6 +40,7 @@ public Order getOrder(Long id) {
     return orderRepository.findById(id).orElse(null);
   }
 
+  @Transactional
   public Order save(Order order) {
     return orderRepository.save(order);
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
index aa683a5d0..809f18159 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
@@ -16,7 +16,7 @@
 public class Point extends BaseEntity {
   private BigDecimal amount;
 
-  @OneToOne
+  @OneToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "ref_user_Id", unique = true, nullable = false)
   private User user;
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
index 48d7e95a3..cb76979a8 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
@@ -6,6 +6,7 @@
 import com.loopers.support.error.ErrorType;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
@@ -25,6 +26,7 @@ public Point getAvailablePoints(Long userId) {
 
   @Transactional(readOnly = true)
   public BigDecimal getAmount(Long userId) {
+    System.out.println(">>>" + userId);
     return pointRepository.findByUserId(userId)
         .map(Point::getAmount)
         .orElse(null);
@@ -47,8 +49,11 @@ public BigDecimal use(Long userId, BigDecimal useAmt) {
     if (!pointOpt.isPresent()) {
       throw new CoreException(ErrorType.NOT_FOUND, "현재 포인트 정보를 찾을수 없습니다.");
     }
-    pointOpt.get().use(useAmt);
-    Point saved = pointRepository.save(pointOpt.get());
+    Point point = pointOpt.get();
+    System.out.println(">>>" + point.getAmount() + "," + useAmt + "," + userId);
+    point.use(useAmt);
+    Point saved = pointRepository.save(point);
+    System.out.println(">>>" + saved.getAmount() + "," + point.getAmount() + "," + userId);
     return saved.getAmount();
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
index 03bc9908d..c05f009a3 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
@@ -60,13 +60,13 @@ public Product getExistingProduct(Long id) {
   }
 
   @Transactional(readOnly = true)
-  public List<Product> getExistingProducts(Set<Long> productIds) {
+  public List<Product> getExistingProducts(List<Long> productIds) {
 
     if (productIds == null || productIds.isEmpty()) {
       return Collections.emptyList();
     }
     Set<Long> uniqueIds = new HashSet<>(productIds);
-    List<Product> products = productRepository.findAllById(productIds);
+    List<Product> products = productRepository.findAllById(uniqueIds);
 
     if (products.size() != uniqueIds.size()) {
       Set<Long> foundIds = products.stream()
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
index 5657071a2..d3bda90d5 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
@@ -16,14 +16,13 @@
 @Entity
 @Table(name = "user")
 @Getter
-@DynamicUpdate
 public class User extends BaseEntity {
   private String loginId;
   private String email;
   private LocalDate birthday;
   private String gender;
 
-  @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
+  @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
   private Point point;
 
   public void setPoint(Point point) {
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
index 6329b5c74..6fea79b12 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
@@ -14,7 +14,7 @@ public class PointRepositoryImpl implements PointRepository {
 
   @Override
   public Point save(Point point) {
-    return jpaRepository.save(point);
+    return jpaRepository.saveAndFlush(point);
   }
 
   @Override
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
index 326f5f3a9..855023b6b 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/user/UserRepositoryImpl.java
@@ -16,7 +16,7 @@ public class UserRepositoryImpl implements UserRepository {
   public User save(User user) {
     return jpaRepository.save(user);
   }
-  
+
   @Override
   public Optional<User> findById(Long userId) {
     return jpaRepository.findById(userId);
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java
index edcde77aa..c55ce955d 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java
@@ -17,9 +17,13 @@
 import com.loopers.domain.stock.StockService;
 import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserFixture;
+import com.loopers.domain.user.UserRepository;
 import com.loopers.domain.user.UserService;
 import com.loopers.interfaces.api.order.OrderCreateV1Dto;
 import com.loopers.utils.DatabaseCleanUp;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
+import org.hibernate.Hibernate;
 import org.junit.jupiter.api.*;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
@@ -34,6 +38,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 
@@ -42,6 +47,8 @@ class OrderFacadeConcurrencyTest {
   @Autowired
   private OrderFacade sut;
   @MockitoSpyBean
+  private UserRepository userRepository;
+  @MockitoSpyBean
   private UserService userService;
   @MockitoSpyBean
   private PointService pointService;
@@ -56,7 +63,6 @@ class OrderFacadeConcurrencyTest {
   private OrderService orderService;
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
-
   List<User> savedUsers;
   List<Brand> savedBrands;
   List<Product> savedProducts;
@@ -82,10 +88,6 @@ void setup() {
     stock = StockFixture.createStockWith(savedProducts.get(1).getId(), 10);
     savedStock = stockService.save(stock);
 
-    List<OrderItem> orderItems = new ArrayList<>();
-    orderItems.add(OrderItem.create(savedProducts.get(0).getId(), 2L, Money.wons(5_000)));
-    Order order = Order.create(savedUsers.get(0).getId(), orderItems);
-    savedOrder = orderService.save(order);
   }
 
   @AfterEach
@@ -125,40 +127,41 @@ void tearDown() {
     assertThat(stock.getAvailable()).isZero();
   }
 
-//  @DisplayName("동시에 주문해도 재고가 정상적으로 차감된다.")
-//  @Test
-//  void 실패_재고10_단가4원_쓰레드10() throws InterruptedException {
-//    Long userId = savedUsers.get(0).getId();
-//    Long productId = savedProducts.get(1).getId();
-//    List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
-//    items.add(new OrderCreateV1Dto.OrderItemRequest(productId, 1));
-//    OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
-//    CreateOrderCommand orderCommand = CreateOrderCommand.from(userId, request);
-//
-//    AtomicInteger errorCount = new AtomicInteger();
-//    int threadCount = 10;
-//    ExecutorService executor = Executors.newFixedThreadPool(threadCount);
-//    CountDownLatch latch = new CountDownLatch(threadCount);
-//
-//    for (int i = 0; i < threadCount; i++) {
-//      executor.submit(() -> {
-//        try {
-//          sut.createOrder(orderCommand);
-//        } catch (Exception e) {
-//          System.out.println("실패: " + e.getMessage());
-//          errorCount.getAndIncrement();
-//        } finally {
-//          latch.countDown();
-//        }
-//      });
-//    }
-//
-//    latch.await();
-//
-//    Stock stock = stockService.findByProductId(productId);
-//    assertThat(stock.getAvailable()).isEqualTo(0);
-//    BigDecimal bigDecimal = pointService.getAmount(userId);
-//    assertThat(bigDecimal).isEqualByComparingTo(new BigDecimal(2));
-//  }
+  @DisplayName("동시에 주문해도 포인트가 정상적으로 차감된다.")
+  @Test
+  void 실패_재고10_단가4원_쓰레드10() throws InterruptedException {
+    Long userId = savedUsers.get(0).getId();
+    Long productId = savedProducts.get(1).getId();
+    List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
+    items.add(new OrderCreateV1Dto.OrderItemRequest(productId, 1));
+    OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
+    CreateOrderCommand orderCommand = CreateOrderCommand.from(userId, request);
+
+    AtomicInteger errorCount = new AtomicInteger();
+    int threadCount = 10;
+    ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+    CountDownLatch latch = new CountDownLatch(threadCount);
+
+    for (int i = 0; i < threadCount; i++) {
+      executor.submit(() -> {
+        try {
+          sut.createOrder(orderCommand);
+        } catch (Exception e) {
+          System.out.println("실패: " + e.getMessage());
+          errorCount.getAndIncrement();
+        } finally {
+          latch.countDown();
+        }
+      });
+    }
+
+    latch.await();
+
+    assertThat(errorCount.get()).isEqualTo(8);
+    Stock stock = stockService.findByProductId(productId);
+    assertThat(stock.getAvailable()).isEqualTo(8);
+    BigDecimal bigDecimal = pointService.getAmount(userId);
+    assertThat(bigDecimal).isEqualByComparingTo(new BigDecimal(2));
+  }
 
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserLazyTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserLazyTest.java
new file mode 100644
index 000000000..fb5901831
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserLazyTest.java
@@ -0,0 +1,61 @@
+package com.loopers.domain.user;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceUnitUtil;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest
+class UserLazyTest {
+  @Autowired
+  private UserRepository userRepository;
+
+  @Autowired
+  private EntityManager entityManager;
+
+  /**
+   * 테스트 유틸리티: User 엔티티 생성 및 저장
+   */
+  private User setupUserWithPoint() {
+    // 픽스처 대신 직접 생성하여 명확하게 Point가 생성됨을 보장
+    User user = User.create("testuser1", "test@email.com", "1990-01-01", "M");
+
+    // Point.create 내부에서 Point 객체가 생성되고 user.setPoint(this)를 통해 양방향 연결됨
+    // User 생성자 내부에 'this.point = Point.create(this, BigDecimal.TEN);' 로직이 있으므로 별도 setPoint 불필요
+
+    return userRepository.save(user);
+  }
+
+  @Test
+  @Transactional // 트랜잭션 내에서 조회해야 영속성 컨텍스트를 사용할 수 있습니다.
+  @DisplayName("User 조회 시 Point 필드는 로드되지 않아야 한다 (FetchType.LAZY 검증)")
+  void testPointIsLazyLoaded() {
+    // 1. Arrange: User 및 Point 데이터 저장
+    User savedUser = setupUserWithPoint();
+
+    // 2. Clear: 영속성 컨텍스트를 비워, 다음 findById가 DB에서 새로운 엔티티를 로드하도록 강제
+    // (이것이 없으면 1차 캐시의 User를 재사용할 수 있음)
+    entityManager.clear();
+
+    // 3. Act: DB에서 User 엔티티를 다시 조회
+    // 이 시점에 Point는 LAZY이므로 로드되면 안 됩니다.
+    User foundUser = userRepository.findById(savedUser.getId())
+        .orElseThrow(() -> new AssertionError("User Not Found"));
+
+    // 4. Assert: PersistenceUnitUtil을 사용하여 Point 필드의 로드 여부 검증
+    // PersistenceUnitUtil은 필드에 접근하지 않고 로딩 상태만 확인합니다.
+    PersistenceUnitUtil unitUtil = entityManager.getEntityManagerFactory().getPersistenceUnitUtil();
+
+    boolean isPointLoaded = unitUtil.isLoaded(foundUser, "point");
+
+    // 검증: LAZY 설정대로 Point 필드가 로드되지 않았어야 합니다.
+    assertThat(isPointLoaded)
+        .as("User 엔티티를 조회할 때 Point 필드는 LAZY Loading으로 인해 로드되지 않아야 합니다.")
+        .isFalse();
+  }
+}

From 40650d85210b88e92a35f423935690968fa653e3 Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Fri, 21 Nov 2025 01:13:58 +0900
Subject: [PATCH 09/11] =?UTF-8?q?test:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?=
 =?UTF-8?q?=EB=8F=99=EC=8B=9C=EC=84=B1=20=ED=85=8C=EC=8A=A4=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../loopers/application/like/LikeFacade.java  |  28 +++
 .../product/ProductDetailInfo.java            |   4 +-
 .../application/product/ProductFacade.java    |   7 +-
 .../java/com/loopers/domain/like/Like.java    |   2 +-
 .../loopers/domain/like/LikeRepository.java   |   4 +-
 .../com/loopers/domain/like/LikeService.java  |  31 ++-
 .../loopers/domain/point/PointService.java    |   4 -
 .../like/LikeJpaRepository.java               |   9 +-
 .../like/LikeRepositoryImpl.java              |   8 +-
 .../interfaces/api/like/LikeCreateV1Dto.java  |  20 ++
 .../interfaces/api/like/LikeV1ApiSpec.java    |  29 +++
 .../interfaces/api/like/LikeV1Controller.java |  38 ++++
 .../like/LikeFacadeConcurrencyTest.java       | 182 ++++++++++++++++++
 .../like/LikeFacadeIntegrationTest.java       | 149 ++++++++++++++
 .../order/OrderFacadeConcurrencyTest.java     | 128 ++++++++----
 .../order/OrderFacadeIntegrationTest.java     |  58 +++---
 .../domain/like/FakeLikeRepository.java       |  20 +-
 .../like/LikeServiceIntegrationTest.java      |  55 ++++--
 .../loopers/domain/like/LikeServiceTest.java  |  36 ++--
 19 files changed, 677 insertions(+), 135 deletions(-)
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeCreateV1Dto.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1ApiSpec.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Controller.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeConcurrencyTest.java
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeIntegrationTest.java

diff --git a/apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java
new file mode 100644
index 000000000..72b3f3e97
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java
@@ -0,0 +1,28 @@
+package com.loopers.application.like;
+
+import com.loopers.domain.like.LikeService;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.user.UserService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class LikeFacade {
+  private final UserService userService;
+  private final ProductService productService;
+  private final LikeService likeService;
+
+  public LikeInfo like(Long userId, Long productId) {
+    likeService.save(userId, productId);
+    long likeCnt = likeService.getLikeCount(productId);
+    return LikeInfo.from(likeCnt, true);
+  }
+
+  public LikeInfo unlike(Long userId, Long productId) {
+    likeService.remove(userId, productId);
+    long likeCnt = likeService.getLikeCount(productId);
+    return LikeInfo.from(likeCnt, false);
+  }
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
index 0ed1e9803..a7f3ae445 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
@@ -11,7 +11,7 @@
 
 public record ProductDetailInfo(Long id, String name, BigDecimal price, long stock
     , BrandInfo brandInfo, LikeInfo likeInfo) {
-  public static ProductDetailInfo from(Product model, Stock stock, boolean isLiked) {
+  public static ProductDetailInfo from(Product model, Stock stock, LikeInfo likeInfo) {
     if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "상품정보를 찾을수 없습니다.");
     return new ProductDetailInfo(
         model.getId(),
@@ -19,7 +19,7 @@ public static ProductDetailInfo from(Product model, Stock stock, boolean isLiked
         model.getPrice().getAmount(),
         stock.getAvailable(),
         BrandInfo.from(model.getBrand()),
-        LikeInfo.from(1, isLiked)
+        likeInfo
     );
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
index f2782465a..cd49b7fbe 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
@@ -1,5 +1,6 @@
 package com.loopers.application.product;
 
+import com.loopers.application.like.LikeInfo;
 import com.loopers.domain.like.LikeService;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
@@ -29,9 +30,11 @@ public Page<Product> getProductList(Long brandId,
   public ProductDetailInfo getProductDetail(long userId, long productId) {
     Product product = productService.getExistingProduct(productId);
     Stock stock = stockService.findByProductId(productId);
-    
+    long likeCount = likeService.getLikeCount(productId);
     boolean isLiked = likeService.isLiked(userId, productId);
-    return ProductDetailInfo.from(product, stock, isLiked);
+
+    LikeInfo likeInfo = LikeInfo.from(likeCount, isLiked);
+    return ProductDetailInfo.from(product, stock, likeInfo);
   }
 
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
index 136ee5dff..2b5212cca 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
@@ -23,7 +23,7 @@ public class Like extends BaseEntity {
   private Product product;
 
   @Version
-  Long version;
+  Long version = 0L;
 
   protected Like() {
   }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
index fad3cef9b..efbc04f58 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
@@ -9,9 +9,9 @@ public interface LikeRepository {
 
   Optional<Like> findById(Long userId, Long productId);
 
-  Like save(Like like);
+  int save(Long userId, Long productId);
 
-  long remove(Long userId, Long productId);
+  void remove(Long userId, Long productId);
 
   boolean isLiked(Long userId, Long productId);
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
index 383dc8485..7d6609ce6 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
@@ -1,4 +1,3 @@
-
 package com.loopers.domain.like;
 
 import com.loopers.domain.product.Product;
@@ -19,34 +18,30 @@ public class LikeService {
   private final LikeRepository likeRepository;
 
   @Transactional
-  public Like save(Like like) {
-    Long userId = like.getUser().getId();
-    Long productId = like.getProduct().getId();
-    Optional<Like> existingLike = likeRepository.findById(userId, productId);
-
-    if (existingLike.isPresent()) {
-      return existingLike.get();
+  public void save(Long userId, Long productId) {
+    Optional<Like> liked = likeRepository.findById(userId, productId);
+    if (!liked.isPresent()) {
+      likeRepository.save(userId, productId);
     }
-    return likeRepository.save(like);
   }
 
 
   @Transactional
-  public long remove(Long userId, Long productId) {
-    return likeRepository.remove(userId, productId);
+  public void remove(Long userId, Long productId) {
+    likeRepository.remove(userId, productId);
   }
 
-  @Transactional
+  @Transactional(readOnly = true)
   public boolean isLiked(Long userId, Long productId) {
     return likeRepository.isLiked(userId, productId);
   }
 
-  public Page<Product> getLikedProducts(
-      Long userId,
-      String sortType,
-      int page,
-      int size
-  ) {
+  @Transactional(readOnly = true)
+  public long getLikeCount(Long productId) {
+    return likeRepository.getLikeCount(productId);
+  }
+
+  public Page<Product> getLikedProducts(Long userId, String sortType, int page, int size) {
     if (userId == null) {
       throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
     }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
index cb76979a8..edc9aa443 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
@@ -6,7 +6,6 @@
 import com.loopers.support.error.ErrorType;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
@@ -26,7 +25,6 @@ public Point getAvailablePoints(Long userId) {
 
   @Transactional(readOnly = true)
   public BigDecimal getAmount(Long userId) {
-    System.out.println(">>>" + userId);
     return pointRepository.findByUserId(userId)
         .map(Point::getAmount)
         .orElse(null);
@@ -50,10 +48,8 @@ public BigDecimal use(Long userId, BigDecimal useAmt) {
       throw new CoreException(ErrorType.NOT_FOUND, "현재 포인트 정보를 찾을수 없습니다.");
     }
     Point point = pointOpt.get();
-    System.out.println(">>>" + point.getAmount() + "," + useAmt + "," + userId);
     point.use(useAmt);
     Point saved = pointRepository.save(point);
-    System.out.println(">>>" + saved.getAmount() + "," + point.getAmount() + "," + userId);
     return saved.getAmount();
   }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
index a98d2cb7e..ce7416cf6 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
@@ -4,6 +4,7 @@
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
 
@@ -12,9 +13,13 @@
 public interface LikeJpaRepository extends JpaRepository<Like, Long> {
   Optional<Like> findByUserIdAndProductId(Long userId, Long productId);
 
-  Like save(Like like);
+  @Modifying
+  @Query(value = "INSERT IGNORE INTO likes (ref_user_id, ref_product_id, created_at, updated_at) VALUES (:userId, :productId, NOW(), NOW())", nativeQuery = true)
+  int save(@Param("userId") Long userId, @Param("productId") Long productId);
 
-  long deleteByUserIdAndProductId(Long userId, Long productId);
+  @Modifying
+  @Query("delete from Like l where l.user.id = :userId and l.product.id = :productId")
+  void delete(@Param("userId") Long userId, @Param("productId") Long productId);
 
   boolean existsByUserIdAndProductId(Long userId, Long productId);
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
index 74507560e..f8e138b69 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
@@ -22,13 +22,13 @@ public Optional<Like> findById(Long userId, Long productId) {
   }
 
   @Override
-  public Like save(Like like) {
-    return jpaRepository.save(like);
+  public int save(Long userId, Long productId) {
+    return jpaRepository.save(userId, productId);
   }
 
   @Override
-  public long remove(Long userId, Long productId) {
-    return jpaRepository.deleteByUserIdAndProductId(userId, productId);
+  public void remove(Long userId, Long productId) {
+    jpaRepository.delete(userId, productId);
   }
 
   @Override
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeCreateV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeCreateV1Dto.java
new file mode 100644
index 000000000..d4fa070ca
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeCreateV1Dto.java
@@ -0,0 +1,20 @@
+package com.loopers.interfaces.api.like;
+
+import com.loopers.application.user.UserInfo;
+
+public class LikeCreateV1Dto {
+  public record UserRequest(String userId, String email, String birthday, String gender) {
+  }
+
+  public record UserResponse(String userId, String email, String birthday, String gender) {
+    public static UserResponse from(UserInfo info) {
+      if (info == null) return null;
+      return new UserResponse(
+          info.userId(),
+          info.email(),
+          info.birthday(),
+          info.gender()
+      );
+    }
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1ApiSpec.java
new file mode 100644
index 000000000..7a708b6f4
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1ApiSpec.java
@@ -0,0 +1,29 @@
+package com.loopers.interfaces.api.like;
+
+import com.loopers.application.like.LikeInfo;
+import com.loopers.interfaces.api.ApiResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.web.bind.annotation.PathVariable;
+
+@Tag(name = "Like V1 API", description = "Loopers 예시 API 입니다.")
+public interface LikeV1ApiSpec {
+  @Operation(
+      summary = "좋아요",
+      description = "좋아요 합니다."
+  )
+  ApiResponse<LikeInfo> like(
+      @Schema(name = "사용자 ID", description = "조회할 사용자의 ID") Long userId,
+      @PathVariable(value = "productId") Long productId
+  );
+
+  @Operation(
+      summary = "좋아요 삭제",
+      description = "좋아요를 삭제합니다."
+  )
+  ApiResponse<LikeInfo> unlike(
+      @Schema(name = "사용자 ID", description = "조회할 사용자의 ID") Long userId,
+      @PathVariable(value = "productId") Long productId
+  );
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Controller.java
new file mode 100644
index 000000000..fbfc7ce89
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Controller.java
@@ -0,0 +1,38 @@
+package com.loopers.interfaces.api.like;
+
+import com.loopers.application.like.LikeFacade;
+import com.loopers.application.like.LikeInfo;
+import com.loopers.interfaces.api.ApiResponse;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/api/v1/likes")
+public class LikeV1Controller implements LikeV1ApiSpec {
+
+  private final LikeFacade likeFacade;
+
+  @PostMapping("/{productId}")
+  @ResponseStatus(HttpStatus.CREATED)
+  @Override
+  public ApiResponse<LikeInfo> like(
+      @Schema(name = "사용자 ID", description = "조회할 사용자의 ID") Long userId,
+      @PathVariable(value = "productId") Long productId
+  ) {
+    LikeInfo info = likeFacade.like(userId, productId);
+    return ApiResponse.success(info);
+  }
+
+  @DeleteMapping("/{productId}")
+  @Override
+  public ApiResponse<LikeInfo> unlike(
+      @Schema(name = "사용자 ID", description = "조회할 사용자의 ID") Long userId,
+      @PathVariable(value = "productId") Long productId
+  ) {
+    LikeInfo info = likeFacade.unlike(userId, productId);
+    return ApiResponse.success(info);
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeConcurrencyTest.java b/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeConcurrencyTest.java
new file mode 100644
index 000000000..e6a9c7031
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeConcurrencyTest.java
@@ -0,0 +1,182 @@
+package com.loopers.application.like;
+
+import com.loopers.application.order.CreateOrderCommand;
+import com.loopers.application.order.OrderFacade;
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.brand.BrandService;
+import com.loopers.domain.like.LikeService;
+import com.loopers.domain.order.Money;
+import com.loopers.domain.order.Order;
+import com.loopers.domain.order.OrderService;
+import com.loopers.domain.point.PointService;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductFixture;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.stock.Stock;
+import com.loopers.domain.stock.StockFixture;
+import com.loopers.domain.stock.StockService;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
+import com.loopers.domain.user.UserRepository;
+import com.loopers.domain.user.UserService;
+import com.loopers.interfaces.api.order.OrderCreateV1Dto;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+@SpringBootTest
+class LikeFacadeConcurrencyTest {
+  @Autowired
+  private LikeFacade sut;
+  @MockitoSpyBean
+  private UserRepository userRepository;
+  @MockitoSpyBean
+  private UserService userService;
+  @MockitoSpyBean
+  private PointService pointService;
+  @MockitoSpyBean
+  private BrandService brandService;
+  @MockitoSpyBean
+  private ProductService productService;
+  @MockitoSpyBean
+  private LikeService likeService;
+
+  @MockitoSpyBean
+  private OrderService orderService;
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+  List<User> savedUsers;
+  List<Brand> savedBrands;
+  List<Product> savedProducts;
+
+  @BeforeEach
+  void setup() {
+    // arrange
+    List<User> userList = List.of(UserFixture.createUserWithLoginId("user1"), UserFixture.createUserWithLoginId("user2"));
+    savedUsers = List.of(userService.join(userList.get(0)), userService.join(userList.get(1)));
+
+    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
+    savedBrands = brandService.saveAll(brandList);
+
+    List<Product> productList = List.of(ProductFixture.createProductWith("product1", Money.wons(1))
+        , ProductFixture.createProductWith("product2", Money.wons(4))
+        , ProductFixture.createProduct(savedBrands.get(1)));
+    savedProducts = productService.saveAll(productList);
+
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("동시에 좋아요해도 정상적으로 처리된다.")
+  @Test
+  void 성공_좋아요_쓰레드10() throws InterruptedException {
+    Long userId = savedUsers.get(0).getId();
+    Long productId = savedProducts.get(0).getId();
+
+    int threadCount = 10;
+    ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+    CountDownLatch latch = new CountDownLatch(threadCount);
+
+    for (int i = 0; i < threadCount; i++) {
+      executor.submit(() -> {
+        try {
+          sut.like(userId, productId);
+        } catch (Exception e) {
+          System.out.println("실패: " + e.getMessage());
+        } finally {
+          latch.countDown();
+        }
+      });
+    }
+
+    latch.await();
+
+    Long likeCount = likeService.getLikeCount(productId);
+    assertThat(likeCount).isEqualTo(1);
+  }
+
+  @DisplayName("동시에 좋아요 취소해도 정상적으로 처리된다.")
+  @Test
+  void 성공_좋아요_취소_쓰레드10() throws InterruptedException {
+    Long userId = savedUsers.get(0).getId();
+    Long productId = savedProducts.get(0).getId();
+
+    int threadCount = 10;
+    ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+    CountDownLatch latch = new CountDownLatch(threadCount);
+    AtomicInteger errorCount = new AtomicInteger();
+
+    for (int i = 0; i < threadCount; i++) {
+      executor.submit(() -> {
+        try {
+          sut.unlike(userId, productId);
+        } catch (Exception e) {
+          errorCount.getAndIncrement();
+          System.out.println("실패: " + e.getMessage());
+        } finally {
+          latch.countDown();
+        }
+      });
+    }
+
+    latch.await();
+    assertThat(errorCount.get()).isZero();
+    Long likeCount = likeService.getLikeCount(productId);
+    assertThat(likeCount).isZero();
+  }
+
+  @DisplayName("동일한 상품에 대해 여러명이 좋아요/싫어요를 요청해도, 상품의 좋아요 개수가 정상 반영되어야 한다.")
+  @Test
+  void 성공_동일한_상품_여러명_쓰레드10() throws InterruptedException {
+    Long productId = savedProducts.get(0).getId();
+
+    int threadCount = 10;
+    ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+    CountDownLatch latch = new CountDownLatch(threadCount);
+    AtomicInteger errorCount = new AtomicInteger();
+
+    for (int i = 0; i < threadCount; i++) {
+      final int idx = i;
+      executor.submit(() -> {
+        try {
+          Long userId = savedUsers.get(idx % savedUsers.size()).getId();
+          if (idx % 2 == 0) {
+            sut.like(userId, productId);
+          } else {
+            sut.unlike(userId, productId);
+          }
+        } catch (Exception e) {
+          errorCount.incrementAndGet();
+          System.out.println("스레드 실패 (idx=" + idx + "): " + e.getMessage());
+        } finally {
+          latch.countDown();
+        }
+      });
+    }
+
+    latch.await();
+    assertThat(errorCount.get()).isZero();
+    Long likeCount = likeService.getLikeCount(productId);
+    assertThat(likeCount).isBetween(0L, (long) savedUsers.size());
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeIntegrationTest.java
new file mode 100644
index 000000000..6e42eef65
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeIntegrationTest.java
@@ -0,0 +1,149 @@
+package com.loopers.application.like;
+
+import com.loopers.application.order.CreateOrderCommand;
+import com.loopers.application.order.OrderInfo;
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.brand.BrandService;
+import com.loopers.domain.like.LikeService;
+import com.loopers.domain.order.Money;
+import com.loopers.domain.order.OrderService;
+import com.loopers.domain.point.PointService;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductFixture;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.stock.Stock;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
+import com.loopers.domain.user.UserRepository;
+import com.loopers.domain.user.UserService;
+import com.loopers.interfaces.api.order.OrderCreateV1Dto;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+
+@SpringBootTest
+class LikeFacadeIntegrationTest {
+  @Autowired
+  private LikeFacade sut;
+  @MockitoSpyBean
+  private UserRepository userRepository;
+  @MockitoSpyBean
+  private UserService userService;
+  @MockitoSpyBean
+  private PointService pointService;
+  @MockitoSpyBean
+  private BrandService brandService;
+  @MockitoSpyBean
+  private ProductService productService;
+  @MockitoSpyBean
+  private LikeService likeService;
+
+  @MockitoSpyBean
+  private OrderService orderService;
+  @Autowired
+  private DatabaseCleanUp databaseCleanUp;
+  List<User> savedUsers;
+  List<Brand> savedBrands;
+  List<Product> savedProducts;
+
+  @BeforeEach
+  void setup() {
+    // arrange
+    List<User> userList = List.of(UserFixture.createUserWithLoginId("user1"), UserFixture.createUserWithLoginId("user2"));
+    savedUsers = List.of(userService.join(userList.get(0)), userService.join(userList.get(1)));
+
+    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
+    savedBrands = brandService.saveAll(brandList);
+
+    List<Product> productList = List.of(ProductFixture.createProductWith("product1", Money.wons(1))
+        , ProductFixture.createProductWith("product2", Money.wons(4))
+        , ProductFixture.createProduct(savedBrands.get(1)));
+    savedProducts = productService.saveAll(productList);
+
+  }
+
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("좋아요를 할 때,")
+  @Nested
+  class Post {
+    @DisplayName("좋아요가 성공한다.")
+    @Test
+    void 성공_좋아요() {
+      Long userId = savedUsers.get(0).getId();
+      Long productId = savedProducts.get(0).getId();
+
+      // act
+      LikeInfo savedLike = sut.like(userId, productId);
+      // assert
+      assertThat(savedLike).isNotNull();
+      assertThat(savedLike.isLiked()).isTrue();
+      assertThat(savedLike.likeCount()).isEqualTo(1);
+    }
+
+    @DisplayName("좋아요가 성공한다.")
+    @Test
+    void 성공_좋아요_중복() {
+      Long userId = savedUsers.get(0).getId();
+      Long productId = savedProducts.get(0).getId();
+      sut.like(userId, productId);
+      // act
+      LikeInfo savedLike = sut.like(userId, productId);
+      // assert
+      assertThat(savedLike).isNotNull();
+      assertThat(savedLike.isLiked()).isTrue();
+      assertThat(savedLike.likeCount()).isEqualTo(1);
+    }
+  }
+
+  @DisplayName("좋아요 취소할 때,")
+  @Nested
+  class Delete {
+    @DisplayName("좋아요 취소가 성공한다.")
+    @Test
+    void 성공_좋아요_취소() {
+      Long userId = savedUsers.get(0).getId();
+      Long productId = savedProducts.get(0).getId();
+
+      // act
+      LikeInfo savedLike = sut.unlike(userId, productId);
+      // assert
+      assertThat(savedLike).isNotNull();
+      assertThat(savedLike.isLiked()).isFalse();
+      assertThat(savedLike.likeCount()).isZero();
+    }
+
+    @DisplayName("좋아요 취소가 성공한다.")
+    @Test
+    void 성공_좋아요_취소_중복() {
+      Long userId = savedUsers.get(0).getId();
+      Long productId = savedProducts.get(0).getId();
+      sut.unlike(userId, productId);
+      // act
+      LikeInfo savedLike = sut.unlike(userId, productId);
+      // assert
+      assertThat(savedLike).isNotNull();
+      assertThat(savedLike.isLiked()).isFalse();
+      assertThat(savedLike.likeCount()).isZero();
+    }
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java
index c55ce955d..af4eb4eea 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeConcurrencyTest.java
@@ -66,28 +66,31 @@ class OrderFacadeConcurrencyTest {
   List<User> savedUsers;
   List<Brand> savedBrands;
   List<Product> savedProducts;
-  Order savedOrder;
-  Stock savedStock;
 
   @BeforeEach
   void setup() {
-    // arrange
-    List<User> userList = List.of(UserFixture.createUserWithLoginId("user1"), UserFixture.createUserWithLoginId("user2"));
-    savedUsers = List.of(userService.join(userList.get(0)), userService.join(userList.get(1)));
-
-    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
-    savedBrands = brandService.saveAll(brandList);
-
-    List<Product> productList = List.of(ProductFixture.createProductWith("product1", Money.wons(1))
-        , ProductFixture.createProductWith("product2", Money.wons(4))
-        , ProductFixture.createProduct(savedBrands.get(1)));
-    savedProducts = productService.saveAll(productList);
-
-    Stock stock = StockFixture.createStockWith(savedProducts.get(0).getId(), 10);
-    savedStock = stockService.save(stock);
-    stock = StockFixture.createStockWith(savedProducts.get(1).getId(), 10);
-    savedStock = stockService.save(stock);
-
+    // 사용자 생성
+    savedUsers = List.of(
+        userService.join(UserFixture.createUserWithLoginId("user1")),
+        userService.join(UserFixture.createUserWithLoginId("user2"))
+    );
+
+    // 브랜드 생성
+    savedBrands = brandService.saveAll(List.of(
+        BrandFixture.createBrand(),
+        BrandFixture.createBrand()
+    ));
+
+    // 상품 생성
+    savedProducts = productService.saveAll(List.of(
+        ProductFixture.createProductWith("product1", Money.wons(1)),
+        ProductFixture.createProductWith("product2", Money.wons(4)),
+        ProductFixture.createProduct(savedBrands.get(1))
+    ));
+
+    // 재고 생성
+    stockService.save(StockFixture.createStockWith(savedProducts.get(0).getId(), 10));
+    stockService.save(StockFixture.createStockWith(savedProducts.get(1).getId(), 10));
   }
 
   @AfterEach
@@ -95,14 +98,11 @@ void tearDown() {
     databaseCleanUp.truncateAllTables();
   }
 
-  @DisplayName("동시에 주문해도 재고가 정상적으로 차감된다.")
+  @DisplayName("동일한 상품에 대해 여러 주문이 동시에 요청되어도, 재고가 정상적으로 차감된다.")
   @Test
-  void 성공_재고10_단가1원_쓰레드10() throws InterruptedException {
+  void 재고_동시성_테스트() throws InterruptedException {
     Long productId = savedProducts.get(0).getId();
-    List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
-    items.add(new OrderCreateV1Dto.OrderItemRequest(productId, 1));
-    OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
-    CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUsers.get(0).getId(), request);
+    CreateOrderCommand orderCommand = createOrderCommand(savedUsers.get(0).getId(), productId, 1);
 
 
     int threadCount = 10;
@@ -127,18 +127,15 @@ void tearDown() {
     assertThat(stock.getAvailable()).isZero();
   }
 
-  @DisplayName("동시에 주문해도 포인트가 정상적으로 차감된다.")
+  @DisplayName("동일한 유저가 여러 주문을 동시에 수행해도, 포인트가 정상적으로 차감된다.")
   @Test
-  void 실패_재고10_단가4원_쓰레드10() throws InterruptedException {
+  void 포인트_동시성_테스트() throws InterruptedException {
     Long userId = savedUsers.get(0).getId();
     Long productId = savedProducts.get(1).getId();
-    List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
-    items.add(new OrderCreateV1Dto.OrderItemRequest(productId, 1));
-    OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
-    CreateOrderCommand orderCommand = CreateOrderCommand.from(userId, request);
+    CreateOrderCommand orderCommand = createOrderCommand(userId, productId, 1);
 
-    AtomicInteger errorCount = new AtomicInteger();
     int threadCount = 10;
+    AtomicInteger errorCount = new AtomicInteger();
     ExecutorService executor = Executors.newFixedThreadPool(threadCount);
     CountDownLatch latch = new CountDownLatch(threadCount);
 
@@ -147,8 +144,7 @@ void tearDown() {
         try {
           sut.createOrder(orderCommand);
         } catch (Exception e) {
-          System.out.println("실패: " + e.getMessage());
-          errorCount.getAndIncrement();
+          errorCount.incrementAndGet();
         } finally {
           latch.countDown();
         }
@@ -157,11 +153,71 @@ void tearDown() {
 
     latch.await();
 
+    // 재고 10개, 단가 4원 → 2개의 주문 성공, 나머지는 실패
     assertThat(errorCount.get()).isEqualTo(8);
+
     Stock stock = stockService.findByProductId(productId);
     assertThat(stock.getAvailable()).isEqualTo(8);
-    BigDecimal bigDecimal = pointService.getAmount(userId);
-    assertThat(bigDecimal).isEqualByComparingTo(new BigDecimal(2));
+
+    BigDecimal remainingPoint = pointService.getAmount(userId);
+    assertThat(remainingPoint).isEqualByComparingTo(new BigDecimal(2));
   }
 
+  @DisplayName("동일한 유저가 서로 다른 주문을 동시에 수행해도, 포인트가 정상적으로 차감된다.")
+  @Test
+  void 동일유저_다른상품_동시주문_포인트차감() throws InterruptedException {
+    Long userId = savedUsers.get(0).getId();
+    pointService.charge(savedUsers.get(0), new BigDecimal(30));
+    // 주문 1: 상품1, 단가 1원
+    Long productId1 = savedProducts.get(0).getId();
+    CreateOrderCommand orderCommand1 = createOrderCommand(userId, productId1, 1);
+
+    // 주문 2: 상품2, 단가 4원
+    Long productId2 = savedProducts.get(1).getId();
+    CreateOrderCommand orderCommand2 = createOrderCommand(userId, productId2, 1);
+
+    // 동시성 테스트: 상품1, 상품2를 각각 5개씩 병렬 주문
+    int threadCount = 10;
+    ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+    CountDownLatch latch = new CountDownLatch(threadCount);
+    AtomicInteger errorCount = new AtomicInteger();
+
+    for (int i = 0; i < threadCount; i++) {
+      final CreateOrderCommand command = (i % 2 == 0) ? orderCommand1 : orderCommand2;
+      executor.submit(() -> {
+        try {
+          sut.createOrder(command);
+        } catch (Exception e) {
+          // 포인트 부족 등 실패 카운트
+          errorCount.incrementAndGet();
+        } finally {
+          latch.countDown();
+        }
+      });
+    }
+
+    latch.await();
+
+    // 재고 검증
+    Stock stock1 = stockService.findByProductId(productId1);
+    Stock stock2 = stockService.findByProductId(productId2);
+
+    assertThat(stock1.getAvailable()).isEqualTo(5); // 상품1
+    assertThat(stock2.getAvailable()).isEqualTo(5); // 상품2ㄴ
+
+    // 포인트 검증: 초기 포인트 10 + 충전포인트 30, 상품1 단가1 + 상품2 단가4 → 성공 주문만 차감
+    BigDecimal remainingPoint = pointService.getAmount(userId);
+    assertThat(remainingPoint).isEqualByComparingTo(new BigDecimal(15));
+
+    // 실패 건수 검증
+    assertThat(errorCount.get()).isZero();
+  }
+
+  private CreateOrderCommand createOrderCommand(Long userId, Long productId, int quantity) {
+    List<OrderCreateV1Dto.OrderItemRequest> items = List.of(
+        new OrderCreateV1Dto.OrderItemRequest(productId, quantity)
+    );
+    OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
+    return CreateOrderCommand.from(userId, request);
+  }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
index 6901bae12..730ed2dc2 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
@@ -3,17 +3,16 @@
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.brand.BrandFixture;
 import com.loopers.domain.brand.BrandService;
-import com.loopers.domain.brand.BrandService;
-import com.loopers.domain.order.*;
+import com.loopers.domain.order.Money;
 import com.loopers.domain.order.Order;
+import com.loopers.domain.order.OrderItem;
+import com.loopers.domain.order.OrderService;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductFixture;
 import com.loopers.domain.product.ProductService;
-import com.loopers.domain.product.ProductService;
 import com.loopers.domain.stock.Stock;
 import com.loopers.domain.stock.StockFixture;
 import com.loopers.domain.stock.StockService;
-import com.loopers.domain.stock.StockService;
 import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserFixture;
 import com.loopers.domain.user.UserService;
@@ -46,7 +45,6 @@ class OrderFacadeIntegrationTest {
   private ProductService productService;
   @MockitoSpyBean
   private StockService stockService;
-
   @MockitoSpyBean
   private OrderService orderService;
   @Autowired
@@ -56,26 +54,30 @@ class OrderFacadeIntegrationTest {
   List<Brand> savedBrands;
   List<Product> savedProducts;
   Order savedOrder;
-  Stock savedStock;
 
   @BeforeEach
   void setup() {
-    // arrange
-    List<User> userList = List.of(UserFixture.createUserWithLoginId("user1"), UserFixture.createUserWithLoginId("user2"));
-    savedUsers = List.of(userService.join(userList.get(0)), userService.join(userList.get(1)));
-
-    List<Brand> brandList = List.of(BrandFixture.createBrand(), BrandFixture.createBrand());
-    savedBrands = brandService.saveAll(brandList);
-
-    List<Product> productList = List.of(ProductFixture.createProductWith("product1", Money.wons(8))
-        , ProductFixture.createProductWith("product2", Money.wons(4))
-        , ProductFixture.createProduct(savedBrands.get(1)));
-    savedProducts = productService.saveAll(productList);
-
-    Stock stock = StockFixture.createStockWith(savedProducts.get(0).getId(), 10);
-    savedStock = stockService.save(stock);
-    stock = StockFixture.createStockWith(savedProducts.get(1).getId(), 10);
-    savedStock = stockService.save(stock);
+    // 사용자 생성
+    savedUsers = List.of(
+        userService.join(UserFixture.createUserWithLoginId("user1")),
+        userService.join(UserFixture.createUserWithLoginId("user2"))
+    );
+
+    // 브랜드 생성
+    savedBrands = brandService.saveAll(List.of(
+        BrandFixture.createBrand(),
+        BrandFixture.createBrand()
+    ));
+
+    // 상품 생성
+    savedProducts = productService.saveAll(List.of(
+        ProductFixture.createProductWith("product1", Money.wons(8)),
+        ProductFixture.createProductWith("product2", Money.wons(4)),
+        ProductFixture.createProduct(savedBrands.get(1))
+    ));
+    // 재고 생성
+    stockService.save(StockFixture.createStockWith(savedProducts.get(0).getId(), 10));
+    stockService.save(StockFixture.createStockWith(savedProducts.get(1).getId(), 10));
 
     List<OrderItem> orderItems = new ArrayList<>();
     orderItems.add(OrderItem.create(savedProducts.get(0).getId(), 2L, Money.wons(5_000)));
@@ -137,7 +139,7 @@ class Get {
   @DisplayName("주문을 생성할 때,")
   @Nested
   class Post {
-    @DisplayName("10재고가 있는 8원의 'product1' 상품 1건 주문시, 기본 포인트10으로 결제가 된다.")
+    @DisplayName("주문 성공 시, 모든 처리는 정상 반영되어야 한다.")
     @Test
     void 성공_단건주문생성() {
       List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
@@ -152,19 +154,23 @@ class Post {
       assertThat(savedOrder.orderItemInfo()).hasSize(1);
     }
 
-    @DisplayName("10재고가 있는 8원의 'product1'상품 20건 주문시, 재고 없음 오류가 발생한다.")
+    @DisplayName("재고가 존재하지 않거나 부족할 경우 주문은 실패해야 한다. 모두 롤백")
     @Test
     void 실패_재고없음오류() {
+      long productId = savedProducts.get(1).getId();
+      long quantity = 20L;
       List<OrderCreateV1Dto.OrderItemRequest> items = new ArrayList<>();
-      items.add(new OrderCreateV1Dto.OrderItemRequest(savedProducts.get(0).getId(), 20));
+      items.add(new OrderCreateV1Dto.OrderItemRequest(productId, quantity));
       OrderCreateV1Dto.OrderRequest request = new OrderCreateV1Dto.OrderRequest(items);
       CreateOrderCommand orderCommand = CreateOrderCommand.from(savedUsers.get(0).getId(), request);
       // act
       // assert
       assertThrows(CoreException.class, () -> sut.createOrder(orderCommand)).getErrorType().equals(ErrorType.INSUFFICIENT_STOCK);
+      Stock deductedStock = stockService.findByProductId(productId);
+      assertThat(deductedStock.getAvailable()).isEqualTo(10);
     }
 
-    @DisplayName("10재고가 있는 4원의 'product2'상품 3건 주문시, 포인트 부족 오류가 발생한다.")
+    @DisplayName("주문 시 유저의 포인트 잔액이 부족할 경우 주문은 실패해야 한다. 모두 롤백")
     @Test
     void 실패_포인트부족오류() {
       long productId = savedProducts.get(1).getId();
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
index 23974fbc7..254141aae 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
@@ -1,5 +1,8 @@
 package com.loopers.domain.like;
 
+import com.loopers.domain.product.ProductFixture;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
 import lombok.RequiredArgsConstructor;
@@ -29,22 +32,23 @@ public Optional<Like> findById(Long userId, Long productId) {
   }
 
   @Override
-  public Like save(Like entity) {
+  public int save(Long userId, Long productId) {
     for (Long key : list.keySet()) {
       Like like = list.get(key);
-      if (like.getUser().getId().equals(entity.getUser().getId())
-          && like.getProduct().getId().equals(entity.getProduct().getId())) {
+      if (like.getUser().getId().equals(userId)
+          && like.getProduct().getId().equals(productId)) {
         throw new CoreException(ErrorType.BAD_REQUEST, "중복 좋아요 요청입니다.");
       }
     }
     Long id = list.keySet().stream().max(Long::compareTo).orElse(0L) + 1;
-    list.put(id, entity);
-    return entity;
+    Like like = Like.create(UserFixture.createUser(), ProductFixture.createProduct());
+
+    list.put(id, like);
+    return 1;
   }
 
   @Override
-  public long remove(Long userId, Long productId) {
-    long cnt = 0;
+  public void remove(Long userId, Long productId) {
     Iterator<Map.Entry<Long, Like>> iterator = list.entrySet().iterator();
     while (iterator.hasNext()) {
       Map.Entry<Long, Like> entry = iterator.next();
@@ -52,10 +56,8 @@ public long remove(Long userId, Long productId) {
       if (like.getUser().getId().equals(userId)
           && like.getProduct().getId().equals(productId)) {
         iterator.remove();
-        cnt++;
       }
     }
-    return cnt;
   }
 
   @Override
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
index 6ec390a2a..cdd88f405 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
@@ -21,6 +21,9 @@
 
 import static com.loopers.domain.like.LikeAssertions.assertLike;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -63,29 +66,40 @@ void tearDown() {
   @DisplayName("좋아요를 조회할 때,")
   @Nested
   class Create {
-    @DisplayName("존재하는 좋아요 ID를 주면, 해당 좋아요 정보를 반환한다.")
+    @DisplayName("존재하는 좋아요 ID를 주면, 좋아요 성공한다.")
     @Test
     void 성공_존재하는_좋아요ID() {
       // arrange
-
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
       // act
-      Like result = likeService.save(Like.create(savedUser, savedProduct));
-      Optional<Like> savedLike = likeJpaRepository.findById(result.getId());
+      likeService.save(userId, productId);
+      Optional<Like> savedLike = likeJpaRepository.findByUserIdAndProductId(userId, productId);
       // assert
-      assertLike(result, savedLike.get());
+      assertThat(savedLike).isPresent();
+      assertThat(savedLike.get().getUser().getId()).isEqualTo(userId);
+      assertThat(savedLike.get().getProduct().getId()).isEqualTo(productId);
+
     }
 
     @DisplayName("동일한 좋아요를 중복 저장해도 일관된 결과를 반환한다")
     @Test
     void 성공_이미_존재하는_좋아요ID() {
       // arrange
-      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
+
+      likeService.save(userId, productId);
+      Optional<Like> result1 = likeJpaRepository.findByUserIdAndProductId(userId, productId);
 
       // act
-      Like result2 = likeService.save(Like.create(savedUser, savedProduct));
+      likeService.save(userId, productId);
+      Optional<Like> result2 = likeJpaRepository.findByUserIdAndProductId(userId, productId);
 
       // assert
-      assertLike(result1, result2);
+      assertThat(result1).isPresent();
+      assertThat(result2).isPresent();
+      assertLike(result1.get(), result2.get());
     }
   }
 
@@ -96,13 +110,17 @@ class Delete {
     @Test
     void 성공_존재하는_좋아요ID() {
       // arrange
-      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
+
+      likeService.save(userId, productId);
+      Like like = likeJpaRepository.findByUserIdAndProductId(userId, productId).orElseThrow();
 
       // act
       likeService.remove(savedUser.getId(), savedProduct.getId());
 
       // assert
-      verify(likeJpaRepository, times(1)).deleteByUserIdAndProductId(savedUser.getId(), savedProduct.getId());
+      verify(likeJpaRepository, times(1)).delete(userId, productId);
       //assertThrows(NotFoundException.class, () -> service.read(id));
     }
 
@@ -110,15 +128,17 @@ class Delete {
     @Test
     void 성공_이미_삭제된_좋아요() {
       // arrange
-      Like savedLike = likeService.save(Like.create(savedUser, savedProduct));
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
 
-      // act
-      likeService.remove(savedUser.getId(), savedProduct.getId());
+      likeService.save(userId, productId);
       likeService.remove(savedUser.getId(), savedProduct.getId());
+      likeJpaRepository.flush(); // 즉시 삭제 반영
+      // act
 
       // assert
-      verify(likeJpaRepository, times(2)).deleteByUserIdAndProductId(savedUser.getId(), savedProduct.getId());
-
+      assertThatCode(() -> likeService.remove(userId, productId))
+          .doesNotThrowAnyException();
     }
   }
 
@@ -129,7 +149,10 @@ class GetList {
     @Test
     void 성공_좋아요_상품목록조회() {
       // arrange
-      Like savedLike = likeService.save(Like.create(savedUser, savedProduct));
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
+
+      likeService.save(userId, productId);
 
       // act
       Page<Product> productsPage = likeService.getLikedProducts(savedUser.getId(), "latest", 0, 20);
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
index 0b47777e6..cf6f718c8 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
@@ -78,26 +78,30 @@ class Create {
     @Test
     void 성공_좋아요_생성() {
       // arrange
-
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
       // act
-      Like result = likeService.save(Like.create(savedUser, savedProduct));
+      likeService.save(userId, productId);
 
       // assert
-      assertThat(result).isNotNull();
-      assertThat(result.getUser()).isNotNull();
+      Long result = likeService.getLikeCount(productId);
+      assertThat(result).isEqualTo(1);
     }
 
     @DisplayName("동일한 좋아요를 중복 생성하면, 멱등성이 보장된다.")
     @Test
     void 성공_중복_좋아요_멱등성() {
       // arrange
-      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
+      likeService.save(userId, productId);
 
       // act
-      Like result2 = likeService.save(Like.create(savedUser, savedProduct));
+      likeService.save(userId, productId);
 
       // assert
-      assertLike(result1, result2);
+      Long result = likeService.getLikeCount(productId);
+      assertThat(result).isEqualTo(1);
     }
   }
 
@@ -108,7 +112,9 @@ class Delete {
     @Test
     void 성공_존재하는_좋아요ID() {
       // arrange
-      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
+      likeService.save(userId, productId);
 
       // act
       likeService.remove(savedUser.getId(), savedProduct.getId());
@@ -121,14 +127,16 @@ class Delete {
     @Test
     void 성공_이미_삭제된_좋아요() {
       // arrange
-      Like result1 = likeService.save(Like.create(savedUser, savedProduct));
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
+      likeService.save(userId, productId);
 
       // act
-      likeService.remove(savedUser.getId(), savedProduct.getId());
-      likeService.remove(savedUser.getId(), savedProduct.getId());
+      likeService.remove(savedUser.getId(), productId);
+      likeService.remove(savedUser.getId(), productId);
 
       // assert
-      boolean result = likeService.isLiked(savedUser.getId(), savedProduct.getId());
+      boolean result = likeService.isLiked(savedUser.getId(), productId);
       assertThat(result).isFalse();
     }
   }
@@ -140,7 +148,9 @@ class GetList {
     @Test
     void 성공_좋아요_상품목록조회() {
       // arrange
-      Like savedLike = likeService.save(Like.create(savedUser, savedProduct));
+      Long userId = savedUser.getId();
+      Long productId = savedProduct.getId();
+      likeService.save(userId, productId);
 
       // act
       Page<Product> productsPage = likeService.getLikedProducts(savedUser.getId(), "latest", 0, 20);

From aab31be407528427012dcc93c8c7a393c1266c94 Mon Sep 17 00:00:00 2001
From: sieun0322 <lce322@naver.com>
Date: Fri, 21 Nov 2025 02:31:05 +0900
Subject: [PATCH 10/11] =?UTF-8?q?test:=20=EC=83=81=ED=92=88=EB=AA=A9?=
 =?UTF-8?q?=EB=A1=9D=20=EC=B4=9D=20=EC=A2=8B=EC=95=84=EC=9A=94=EC=88=98=20?=
 =?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/product/ProductFacade.java    | 43 ++++++++++++++++++-
 .../product/ProductWithLikeCount.java         | 20 +++++++++
 .../loopers/domain/like/LikeRepository.java   |  3 ++
 .../com/loopers/domain/like/LikeService.java  |  6 +++
 .../domain/like/ProductIdAndLikeCount.java    | 14 ++++++
 .../like/LikeJpaRepository.java               | 10 +++++
 .../like/LikeRepositoryImpl.java              |  7 +++
 .../product/ProductFacadeIntegrationTest.java | 21 ++++++---
 .../domain/like/FakeLikeRepository.java       | 16 +++++++
 9 files changed, 131 insertions(+), 9 deletions(-)
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/application/product/ProductWithLikeCount.java
 create mode 100644 apps/commerce-api/src/main/java/com/loopers/domain/like/ProductIdAndLikeCount.java

diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
index cd49b7fbe..b3d32032a 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
@@ -2,15 +2,21 @@
 
 import com.loopers.application.like.LikeInfo;
 import com.loopers.domain.like.LikeService;
+import com.loopers.domain.like.ProductIdAndLikeCount;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductService;
 import com.loopers.domain.stock.Stock;
 import com.loopers.domain.stock.StockService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
 @RequiredArgsConstructor
 @Component
 public class ProductFacade {
@@ -19,11 +25,44 @@ public class ProductFacade {
   private final LikeService likeService;
 
   @Transactional(readOnly = true)
-  public Page<Product> getProductList(Long brandId,
+  public Page<ProductWithLikeCount> getProductList(Long brandId,
                                       String sortType,
                                       int page,
                                       int size) {
-    return productService.getProducts(brandId, sortType, page, size);
+    Page<Product> productPage = productService.getProducts(brandId, sortType, page, size);
+    List<Product> products = productPage.getContent();
+
+    List<Long> productIds = products.stream()
+        .map(Product::getId)
+        .toList();
+
+    List<ProductIdAndLikeCount> likeCountList = likeService.getLikeCount(productIds);
+
+    // 총 좋아요 정보
+    Map<Long, Long> likeCountMap = likeCountList.stream()
+        .collect(Collectors.toMap(
+            ProductIdAndLikeCount::getProductId,
+            ProductIdAndLikeCount::getLikeCount
+        ));
+
+    //
+
+    //
+    List<ProductWithLikeCount> dtoList = products.stream()
+        .map(product -> {
+          Long productId = product.getId();
+          Long totalLikeCount = likeCountMap.getOrDefault(productId, 0L);
+
+          return new ProductWithLikeCount(
+              productId,
+              product.getName(),
+              product.getPrice().getAmount(),
+              totalLikeCount
+          );
+        })
+        .toList();
+
+    return new PageImpl<>(dtoList, productPage.getPageable(), productPage.getTotalElements());
   }
 
   @Transactional(readOnly = true)
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductWithLikeCount.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductWithLikeCount.java
new file mode 100644
index 000000000..32c811df5
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductWithLikeCount.java
@@ -0,0 +1,20 @@
+package com.loopers.application.product;
+
+import com.loopers.domain.product.Product;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+
+import java.math.BigDecimal;
+
+public record ProductWithLikeCount(Long id, String name, BigDecimal price
+    , long likeCount) {
+  public static ProductWithLikeCount from(Product model, long likeCount) {
+    if (model == null) throw new CoreException(ErrorType.NOT_FOUND, "상품정보를 찾을수 없습니다.");
+    return new ProductWithLikeCount(
+        model.getId(),
+        model.getName(),
+        model.getPrice().getAmount(),
+        likeCount
+    );
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
index efbc04f58..a0eb9358a 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
@@ -3,6 +3,7 @@
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 
+import java.util.List;
 import java.util.Optional;
 
 public interface LikeRepository {
@@ -18,4 +19,6 @@ public interface LikeRepository {
   long getLikeCount(Long productId);
 
   Page<Like> getLikedProducts(Long userId, Pageable pageable);
+
+  List<ProductIdAndLikeCount> getLikeCountWithProductId(List<Long> productIds);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
index 7d6609ce6..5304fdb88 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
@@ -41,6 +41,12 @@ public long getLikeCount(Long productId) {
     return likeRepository.getLikeCount(productId);
   }
 
+
+  @Transactional(readOnly = true)
+  public List<ProductIdAndLikeCount> getLikeCount(List<Long> productIds) {
+    return likeRepository.getLikeCountWithProductId(productIds);
+  }
+
   public Page<Product> getLikedProducts(Long userId, String sortType, int page, int size) {
     if (userId == null) {
       throw new CoreException(ErrorType.BAD_REQUEST, "ID가 없습니다.");
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/ProductIdAndLikeCount.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/ProductIdAndLikeCount.java
new file mode 100644
index 000000000..003b0f1fb
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/ProductIdAndLikeCount.java
@@ -0,0 +1,14 @@
+package com.loopers.domain.like;
+
+import lombok.Getter;
+
+@Getter
+public class ProductIdAndLikeCount {
+  private final Long productId;
+  private final Long likeCount;
+
+  public ProductIdAndLikeCount(Long productId, Long likeCount) {
+    this.productId = productId;
+    this.likeCount = likeCount;
+  }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
index ce7416cf6..3d86377df 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
@@ -1,6 +1,7 @@
 package com.loopers.infrastructure.like;
 
 import com.loopers.domain.like.Like;
+import com.loopers.domain.like.ProductIdAndLikeCount;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
@@ -8,6 +9,7 @@
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
 
+import java.util.List;
 import java.util.Optional;
 
 public interface LikeJpaRepository extends JpaRepository<Like, Long> {
@@ -30,4 +32,12 @@ public interface LikeJpaRepository extends JpaRepository<Like, Long> {
       countQuery = "SELECT COUNT(l) FROM Like l WHERE l.user.id = :userId"
   )
   Page<Like> getLikedProducts(@Param("userId") Long userId, Pageable pageable);
+
+
+  @Query("SELECT NEW com.loopers.domain.like.ProductIdAndLikeCount(l.product.id, COUNT(l.id)) " +
+      "FROM Like l " +
+      "WHERE l.product.id IN :productIds " +
+      "GROUP BY l.product.id")
+  List<ProductIdAndLikeCount> countLikesByProductIds(@Param("productIds") List<Long> productIds);
+
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
index f8e138b69..4641766f4 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
@@ -2,12 +2,14 @@
 
 import com.loopers.domain.like.Like;
 import com.loopers.domain.like.LikeRepository;
+import com.loopers.domain.like.ProductIdAndLikeCount;
 import lombok.RequiredArgsConstructor;
 import org.springframework.context.annotation.Primary;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
 import java.util.Optional;
 
 @RequiredArgsConstructor
@@ -46,4 +48,9 @@ public Page<Like> getLikedProducts(Long userId, Pageable pageable) {
     return jpaRepository.getLikedProducts(userId, pageable);
   }
 
+  @Override
+  public List<ProductIdAndLikeCount> getLikeCountWithProductId(List<Long> productIds) {
+    return jpaRepository.countLikesByProductIds(productIds);
+  }
+
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
index 9f7d66b8c..e0ea35488 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeIntegrationTest.java
@@ -3,6 +3,7 @@
 import com.loopers.domain.brand.Brand;
 import com.loopers.domain.brand.BrandFixture;
 import com.loopers.domain.brand.BrandRepository;
+import com.loopers.domain.like.LikeRepository;
 import com.loopers.domain.product.Product;
 import com.loopers.domain.product.ProductFixture;
 import com.loopers.domain.product.ProductRepository;
@@ -15,6 +16,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.data.domain.Page;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 
@@ -24,6 +26,7 @@
 
 
 @SpringBootTest
+@Transactional
 class ProductFacadeIntegrationTest {
   @Autowired
   private ProductFacade sut;
@@ -33,7 +36,8 @@ class ProductFacadeIntegrationTest {
   private BrandRepository brandRepository;
   @Autowired
   private ProductRepository productRepository;
-
+  @Autowired
+  private LikeRepository likeRepository;
   @Autowired
   private DatabaseCleanUp databaseCleanUp;
 
@@ -66,11 +70,14 @@ class GetList {
     void 성공_상품목록조회() {
       // arrange
       Long brandId = null;
+      likeRepository.save(savedUser.getId(), savedProducts.get(0).getId());
       // act
-      Page<Product> productsPage = sut.getProductList(brandId, "latest", 0, 20);
-      List<Product> products = productsPage.getContent();
+      Page<ProductWithLikeCount> productsPage = sut.getProductList(brandId, "latest", 0, 20);
+      List<ProductWithLikeCount> products = productsPage.getContent();
       // assert
       assertThat(products).isNotEmpty().hasSize(3);
+      assertThat(products.get(2).name()).isEqualTo(savedProducts.get(0).getName());
+      assertThat(products.get(2).likeCount()).isEqualTo(1);
     }
 
     @DisplayName("브랜드ID 검색조건 포함시, 해당 브랜드의 상품 목록이 조회된다.")
@@ -79,13 +86,13 @@ class GetList {
       // arrange
       Long brandId = savedProducts.get(0).getBrand().getId();
       // act
-      Page<Product> productsPage = sut.getProductList(brandId, null, 0, 20);
-      List<Product> resultList = productsPage.getContent();
+      Page<ProductWithLikeCount> productsPage = sut.getProductList(brandId, null, 0, 20);
+      List<ProductWithLikeCount> resultList = productsPage.getContent();
 
       // assert
       assertThat(resultList).isNotEmpty().hasSize(2);
-      assertProduct(resultList.get(0), savedProducts.get(1));
-      assertProduct(resultList.get(1), savedProducts.get(0));
+      assertThat(resultList.get(0).name()).isEqualTo(savedProducts.get(1).getName());
+      assertThat(resultList.get(1).name()).isEqualTo(savedProducts.get(0).getName());
     }
   }
 
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
index 254141aae..4ef0dd5a4 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/FakeLikeRepository.java
@@ -90,4 +90,20 @@ public Page<Like> getLikedProducts(Long userId, Pageable pageable) {
         .collect(Collectors.toList()), pageable, list.size());
   }
 
+  @Override
+  public List<ProductIdAndLikeCount> getLikeCountWithProductId(List<Long> productIds) {
+    Map<Long, Long> likeCountsByProduct = list.values().stream()
+        .filter(like -> productIds.contains(like.getProduct().getId()))
+        .collect(Collectors.groupingBy(
+            like -> like.getProduct().getId(),
+            Collectors.counting()
+        ));
+
+    return likeCountsByProduct.entrySet().stream()
+        .map(entry -> new ProductIdAndLikeCount(
+            entry.getKey(), // Product ID
+            entry.getValue() // Like Count
+        ))
+        .collect(Collectors.toList());
+  }
 }

From f1bab1cb172c97dc5fe0421c247f8965610209a4 Mon Sep 17 00:00:00 2001
From: sieun0322 <lce95322@gmail.com>
Date: Fri, 21 Nov 2025 12:30:36 +0900
Subject: [PATCH 11/11] =?UTF-8?q?test:=20=EC=A3=BC=EB=AC=B8=20E2E=20?=
 =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../api/order/OrderV1Controller.java          |   2 +
 .../api/point/PointV1Controller.java          |   2 +-
 .../interfaces/api/user/UserV1Controller.java |   2 +-
 .../interfaces/api/OrderV1ApiE2ETest.java     | 185 ++++++++++++++++++
 .../interfaces/api/PointV1ApiE2ETest.java     |   8 +-
 .../interfaces/api/UserV1ApiE2ETest.java      |   4 +-
 6 files changed, 195 insertions(+), 8 deletions(-)
 create mode 100644 apps/commerce-api/src/test/java/com/loopers/interfaces/api/OrderV1ApiE2ETest.java

diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
index b42521578..3df5b27e7 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
@@ -7,6 +7,7 @@
 import com.loopers.interfaces.api.ApiResponse;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
 
 @RequiredArgsConstructor
@@ -29,6 +30,7 @@ public ApiResponse<Page<Order>> getOrderList(
   }
 
   @PostMapping("")
+  @ResponseStatus(HttpStatus.CREATED)
   @Override
   public ApiResponse<OrderCreateV1Dto.OrderResponse> createOrder(@RequestHeader(value = "X-USER-ID", required = false) Long userId
       , @RequestBody OrderCreateV1Dto.OrderRequest request
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java
index 5adeadb43..75e0ac9cf 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java
@@ -9,7 +9,7 @@
 
 @RequiredArgsConstructor
 @RestController
-@RequestMapping("/api/v1/user/point")
+@RequestMapping("/api/v1/users/point")
 public class PointV1Controller implements PointV1ApiSpec {
 
   private final PointFacade pointFacade;
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Controller.java
index b511e79cd..280bf2c19 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Controller.java
@@ -9,7 +9,7 @@
 
 @RequiredArgsConstructor
 @RestController
-@RequestMapping("/api/v1/user")
+@RequestMapping("/api/v1/users")
 public class UserV1Controller implements UserV1ApiSpec {
 
   private final UserFacade userFacade;
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/OrderV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/OrderV1ApiE2ETest.java
new file mode 100644
index 000000000..94426ffed
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/OrderV1ApiE2ETest.java
@@ -0,0 +1,185 @@
+package com.loopers.interfaces.api;
+
+import com.loopers.application.order.OrderFacade;
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandFixture;
+import com.loopers.domain.brand.BrandService;
+import com.loopers.domain.order.Money;
+import com.loopers.domain.order.OrderService;
+import com.loopers.domain.point.PointService;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductFixture;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.stock.StockFixture;
+import com.loopers.domain.stock.StockService;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserFixture;
+import com.loopers.domain.user.UserRepository;
+import com.loopers.domain.user.UserService;
+import com.loopers.interfaces.api.order.OrderCreateV1Dto;
+import com.loopers.interfaces.api.user.UserCreateV1Dto;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.util.List;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class OrderV1ApiE2ETest {
+
+  private static final String ENDPOINT = "/api/v1/orders";
+  private static final Function<String, String> ENDPOINT_GET = id -> "/api/v1/orders/" + id;
+
+  private final TestRestTemplate testRestTemplate;
+  private final UserService userService;
+  private final DatabaseCleanUp databaseCleanUp;
+
+    @Autowired
+    private OrderFacade sut;
+    @MockitoSpyBean
+    private UserRepository userRepository;
+    @MockitoSpyBean
+    private PointService pointService;
+    @MockitoSpyBean
+    private BrandService brandService;
+    @MockitoSpyBean
+    private ProductService productService;
+    @MockitoSpyBean
+    private StockService stockService;
+
+    @MockitoSpyBean
+    private OrderService orderService;
+    List<User> savedUsers;
+    List<Brand> savedBrands;
+    List<Product> savedProducts;
+
+    @Autowired
+  public OrderV1ApiE2ETest(
+      TestRestTemplate testRestTemplate,
+      UserService userService,
+      DatabaseCleanUp databaseCleanUp
+  ) {
+    this.testRestTemplate = testRestTemplate;
+    this.userService = userService;
+    this.databaseCleanUp = databaseCleanUp;
+  }
+    @BeforeEach
+    void setup() {
+        // 사용자 생성
+        savedUsers = List.of(
+                userService.join(UserFixture.createUserWithLoginId("user1")),
+                userService.join(UserFixture.createUserWithLoginId("user2"))
+        );
+
+        // 브랜드 생성
+        savedBrands = brandService.saveAll(List.of(
+                BrandFixture.createBrand(),
+                BrandFixture.createBrand()
+        ));
+
+        // 상품 생성
+        savedProducts = productService.saveAll(List.of(
+                ProductFixture.createProductWith("product1", Money.wons(1)),
+                ProductFixture.createProductWith("product2", Money.wons(4)),
+                ProductFixture.createProduct(savedBrands.get(1))
+        ));
+
+        // 재고 생성
+        stockService.save(StockFixture.createStockWith(savedProducts.get(0).getId(), 10));
+        stockService.save(StockFixture.createStockWith(savedProducts.get(1).getId(), 10));
+    }
+  @AfterEach
+  void tearDown() {
+    databaseCleanUp.truncateAllTables();
+  }
+
+  @DisplayName("주문 생성")
+  @Nested
+  class Post {
+    @DisplayName("주문 생성-E2E테스트1")
+    @Test
+    void 성공_주문생성() {
+      //given
+        HttpHeaders headers = new HttpHeaders();
+        headers.set("X-USER-ID", savedUsers.get(0).getId().toString());
+        List<OrderCreateV1Dto.OrderItemRequest> items = List.of(
+                new OrderCreateV1Dto.OrderItemRequest(savedProducts.get(0).getId(), 1L)
+        );
+        OrderCreateV1Dto.OrderRequest req = new OrderCreateV1Dto.OrderRequest(items);
+      //when
+      ParameterizedTypeReference<ApiResponse<OrderCreateV1Dto.OrderResponse>> resType = new ParameterizedTypeReference<>() {
+      };
+      ResponseEntity<ApiResponse<OrderCreateV1Dto.OrderResponse>> res = testRestTemplate.exchange(ENDPOINT, HttpMethod.POST, new HttpEntity<>(req,headers), resType);
+
+      //then
+      assertThat(res.getStatusCode()).isEqualTo(HttpStatus.CREATED);
+      assertThat(res.getBody().data()).isNotNull();
+      assertThat(res.getBody().data().totalPrice()).isEqualByComparingTo(savedProducts.get(0).getPrice().getAmount());
+    }
+  }
+
+  @DisplayName("주문 정보 조회")
+  @Nested
+  class Get {
+    @DisplayName("E2E테스트1")
+    @Test
+    void 성공_주문정보조회() {
+        HttpHeaders headers = new HttpHeaders();
+        headers.set("X-USER-ID", savedUsers.get(0).getId().toString());
+        List<OrderCreateV1Dto.OrderItemRequest> orderitems = List.of(
+                new OrderCreateV1Dto.OrderItemRequest(savedProducts.get(0).getId(), 1L)
+        );
+        OrderCreateV1Dto.OrderRequest orderRequest = new OrderCreateV1Dto.OrderRequest(orderitems);
+        //when
+        ParameterizedTypeReference<ApiResponse<OrderCreateV1Dto.OrderResponse>> orderResType = new ParameterizedTypeReference<>() {
+        };
+        ResponseEntity<ApiResponse<OrderCreateV1Dto.OrderResponse>> orderRes = testRestTemplate.exchange(ENDPOINT, HttpMethod.POST, new HttpEntity<>(orderRequest,headers), orderResType);
+
+
+        //given
+        List<OrderCreateV1Dto.OrderItemRequest> items = List.of(
+                new OrderCreateV1Dto.OrderItemRequest(savedProducts.get(0).getId(), 1L)
+        );
+        OrderCreateV1Dto.OrderRequest req = new OrderCreateV1Dto.OrderRequest(items);
+
+        //when
+      String url = ENDPOINT_GET.apply(savedUsers.get(0).getId().toString());
+      ParameterizedTypeReference<ApiResponse<OrderCreateV1Dto.OrderResponse>> resType = new ParameterizedTypeReference<>() {
+      };
+      ResponseEntity<ApiResponse<OrderCreateV1Dto.OrderResponse>> res = testRestTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null,headers), resType);
+
+      //then
+      assertThat(res.getStatusCode()).isEqualTo(HttpStatus.OK);
+        assertThat(res.getBody().data()).isNotNull();
+        assertThat(res.getBody().data().totalPrice()).isEqualByComparingTo(savedProducts.get(0).getPrice().getAmount());
+    }
+
+    @DisplayName("E2E테스트2")
+    @Test
+    void 실패_존재하지_않는_주문ID() {
+      //given
+        HttpHeaders headers = new HttpHeaders();
+        headers.set("X-USER-ID", savedUsers.get(0).getId().toString());
+        String orderId = "-1";
+
+      //when
+      String url = ENDPOINT_GET.apply(orderId);
+      ParameterizedTypeReference<ApiResponse<OrderCreateV1Dto.OrderResponse>> resType = new ParameterizedTypeReference<>() {
+      };
+      ResponseEntity<ApiResponse<OrderCreateV1Dto.OrderResponse>> res = testRestTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null,headers), resType);
+
+      //then
+      assertThat(res.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+    }
+
+  }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
index 50b587844..ba58bf894 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointV1ApiE2ETest.java
@@ -57,7 +57,7 @@ class Get {
       HttpHeaders headers = new HttpHeaders();
       headers.set("X-USER-ID", savedUser.getId().toString());
 
-      String url = "/api/v1/user/point";
+      String url = "/api/v1/users/point";
       ParameterizedTypeReference<ApiResponse<BigDecimal>> resType = new ParameterizedTypeReference<>() {
       };
       ResponseEntity<ApiResponse<BigDecimal>> res = testRestTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), resType);
@@ -74,7 +74,7 @@ class Get {
       //given
 
       //when
-      String url = "/api/v1/user/point";
+      String url = "/api/v1/users/point";
       ParameterizedTypeReference<ApiResponse<BigDecimal>> resType = new ParameterizedTypeReference<>() {
       };
       ResponseEntity<ApiResponse<BigDecimal>> res = testRestTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null), resType);
@@ -98,7 +98,7 @@ class Charge {
       headers.set("X-USER-ID", savedUser.getId().toString());
 
       //when
-      String url = "/api/v1/user/point/charge";
+      String url = "/api/v1/users/point/charge";
       ParameterizedTypeReference<ApiResponse<BigDecimal>> resType = new ParameterizedTypeReference<>() {
       };
       ResponseEntity<ApiResponse<BigDecimal>> res = testRestTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(chargeAmt, headers), resType);
@@ -117,7 +117,7 @@ class Charge {
       headers.set("X-USER-ID", "999999");
 
       //when
-      String url = "/api/v1/user/point/charge";
+      String url = "/api/v1/users/point/charge";
       ParameterizedTypeReference<ApiResponse<BigDecimal>> resType = new ParameterizedTypeReference<>() {
       };
       ResponseEntity<ApiResponse<BigDecimal>> res = testRestTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(BigDecimal.TEN, headers), resType);
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
index 0d226bb10..c9860724f 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserV1ApiE2ETest.java
@@ -26,8 +26,8 @@
 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
 public class UserV1ApiE2ETest {
 
-  private static final String ENDPOINT = "/api/v1/user";
-  private static final Function<String, String> ENDPOINT_GET = id -> "/api/v1/user/" + id;
+  private static final String ENDPOINT = "/api/v1/users";
+  private static final Function<String, String> ENDPOINT_GET = id -> "/api/v1/users/" + id;
 
   private final TestRestTemplate testRestTemplate;
   private final UserService userService;
