diff --git a/apps/commerce-api/build.gradle.kts b/apps/commerce-api/build.gradle.kts
index a5f94f5ad..6854f99a5 100644
--- a/apps/commerce-api/build.gradle.kts
+++ b/apps/commerce-api/build.gradle.kts
@@ -8,6 +8,8 @@ dependencies {
 
     //service
     implementation(project(":core:service:user-service"))
+    implementation(project(":core:service:order-service"))
+    implementation(project(":core:service:product-service"))
 
     //domain
     implementation(project(":core:domain"))
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/brand/BrandV1Api.java b/apps/commerce-api/src/main/java/com/loopers/application/api/brand/BrandV1Api.java
new file mode 100644
index 000000000..5f7101c3c
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/brand/BrandV1Api.java
@@ -0,0 +1,29 @@
+package com.loopers.application.api.brand;
+
+import com.loopers.application.api.common.dto.ApiResponse;
+import com.loopers.core.domain.brand.Brand;
+import com.loopers.core.service.brand.BrandQueryService;
+import com.loopers.core.service.brand.query.GetBrandQuery;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static com.loopers.application.api.brand.BrandV1Dto.GetBrandResponse;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/v1/brands")
+public class BrandV1Api implements BrandV1ApiSpec {
+
+    private final BrandQueryService queryService;
+
+    @Override
+    @GetMapping("/{brandId}")
+    public ApiResponse<GetBrandResponse> getBrand(@PathVariable String brandId) {
+        Brand brand = queryService.getBrandBy(new GetBrandQuery(brandId));
+
+        return ApiResponse.success(GetBrandResponse.from(brand));
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/brand/BrandV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/application/api/brand/BrandV1ApiSpec.java
new file mode 100644
index 000000000..ca8a2566b
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/brand/BrandV1ApiSpec.java
@@ -0,0 +1,17 @@
+package com.loopers.application.api.brand;
+
+import com.loopers.application.api.common.dto.ApiResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+import static com.loopers.application.api.brand.BrandV1Dto.GetBrandResponse;
+
+@Tag(name = "Brand V1 API", description = "브랜드 API 입니다.")
+public interface BrandV1ApiSpec {
+
+    @Operation(
+            summary = "브랜드 조회",
+            description = "ID로 브랜드를 조회합니다."
+    )
+    ApiResponse<GetBrandResponse> getBrand(String brandId);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/brand/BrandV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/application/api/brand/BrandV1Dto.java
new file mode 100644
index 000000000..39b8508fb
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/brand/BrandV1Dto.java
@@ -0,0 +1,18 @@
+package com.loopers.application.api.brand;
+
+import com.loopers.core.domain.brand.Brand;
+
+public class BrandV1Dto {
+
+    public record GetBrandResponse(
+            String name,
+            String description
+    ) {
+        public static GetBrandResponse from(Brand brand) {
+            return new GetBrandResponse(
+                    brand.getName().value(),
+                    brand.getDescription().value()
+            );
+        }
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/error/ApiControllerAdvice.java b/apps/commerce-api/src/main/java/com/loopers/application/api/error/ApiControllerAdvice.java
index 9b6fe4638..bb6e6f70f 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/api/error/ApiControllerAdvice.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/error/ApiControllerAdvice.java
@@ -6,6 +6,7 @@
 import com.loopers.application.api.common.dto.ApiResponse;
 import com.loopers.core.domain.error.NotFoundException;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.dao.OptimisticLockingFailureException;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.converter.HttpMessageNotReadableException;
 import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -143,6 +144,11 @@ public ResponseEntity<ApiResponse<?>> handleNotFound(NotFoundException e) {
         return failureResponse(ApiErrorType.NOT_FOUND, e.getMessage());
     }
 
+    @ExceptionHandler
+    public ResponseEntity<ApiResponse<?>> handleOptimisticLock(OptimisticLockingFailureException exception) {
+        return failureResponse(ApiErrorType.CONFLICT, exception.getMessage());
+    }
+
     private String extractMissingParameter(String message) {
         Pattern pattern = Pattern.compile("'(.+?)'");
         Matcher matcher = pattern.matcher(message);
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/error/ApiErrorType.java b/apps/commerce-api/src/main/java/com/loopers/application/api/error/ApiErrorType.java
index fa50edbd5..599a5b32d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/api/error/ApiErrorType.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/error/ApiErrorType.java
@@ -7,7 +7,9 @@
 @Getter
 @RequiredArgsConstructor
 public enum ApiErrorType {
-    /** 범용 에러 */
+    /**
+     * 범용 에러
+     */
     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(), "존재하지 않는 요청입니다."),
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/order/OrderV1Api.java b/apps/commerce-api/src/main/java/com/loopers/application/api/order/OrderV1Api.java
new file mode 100644
index 000000000..214ad23ca
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/order/OrderV1Api.java
@@ -0,0 +1,57 @@
+package com.loopers.application.api.order;
+
+import com.loopers.application.api.common.dto.ApiResponse;
+import com.loopers.core.domain.order.Order;
+import com.loopers.core.domain.order.OrderDetail;
+import com.loopers.core.domain.order.OrderListView;
+import com.loopers.core.service.order.OrderQueryService;
+import com.loopers.core.service.order.OrderService;
+import com.loopers.core.service.order.query.GetOrderDetailQuery;
+import com.loopers.core.service.order.query.GetOrderListQuery;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import static com.loopers.application.api.order.OrderV1Dto.*;
+
+@RestController
+@RequestMapping("/api/v1/orders")
+@RequiredArgsConstructor
+public class OrderV1Api implements OrderV1ApiSpec {
+
+    private final OrderService orderService;
+    private final OrderQueryService queryService;
+
+    @Override
+    @PostMapping
+    public ApiResponse<OrderResponse> order(
+            @RequestHeader(name = "X-USER-ID") String userIdentifier,
+            @RequestBody OrderRequest request
+    ) {
+        Order savedOrder = orderService.order(request.toCommand(userIdentifier));
+
+        return ApiResponse.success(new OrderResponse(savedOrder.getId().value()));
+    }
+
+    @Override
+    @GetMapping
+    public ApiResponse<OrderListResponse> getOrderList(
+            @RequestHeader(name = "X-USER-ID") String userIdentifier,
+            String createdAtSort,
+            int pageNo,
+            int pageSize
+    ) {
+        OrderListView view = queryService.getOrderListWithCondition(new GetOrderListQuery(userIdentifier, createdAtSort, pageNo, pageSize));
+
+        return ApiResponse.success(OrderListResponse.from(view));
+    }
+
+    @Override
+    @GetMapping("/{orderId}")
+    public ApiResponse<OrderDetailResponse> getOrderDetail(
+            @PathVariable String orderId
+    ) {
+        OrderDetail orderDetail = queryService.getOrderDetail(new GetOrderDetailQuery(orderId));
+        
+        return ApiResponse.success(OrderDetailResponse.from(orderDetail));
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/order/OrderV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/application/api/order/OrderV1ApiSpec.java
new file mode 100644
index 000000000..526c94a5a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/order/OrderV1ApiSpec.java
@@ -0,0 +1,34 @@
+package com.loopers.application.api.order;
+
+import com.loopers.application.api.common.dto.ApiResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+import static com.loopers.application.api.order.OrderV1Dto.*;
+
+@Tag(name = "Order V1 API", description = "주문 API 입니다.")
+public interface OrderV1ApiSpec {
+
+    @Operation(
+            summary = "주문 요청",
+            description = "상품 목록으로 주문을 요청합니다."
+    )
+    ApiResponse<OrderResponse> order(String userIdentifier, OrderRequest request);
+
+    @Operation(
+            summary = "주문 목록 조회",
+            description = "주문의 목록을 조회합니다."
+    )
+    ApiResponse<OrderListResponse> getOrderList(
+            String userIdentifier,
+            String createdAtSort,
+            int pageNo,
+            int pageSize
+    );
+
+    @Operation(
+            summary = "주문 상세 조회",
+            description = "주문의 상세 정보를 조회합니다."
+    )
+    ApiResponse<OrderDetailResponse> getOrderDetail(String orderId);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/order/OrderV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/application/api/order/OrderV1Dto.java
new file mode 100644
index 000000000..4f83b0cbb
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/order/OrderV1Dto.java
@@ -0,0 +1,104 @@
+package com.loopers.application.api.order;
+
+import com.loopers.core.domain.order.OrderDetail;
+import com.loopers.core.domain.order.OrderItem;
+import com.loopers.core.domain.order.OrderListItem;
+import com.loopers.core.domain.order.OrderListView;
+import com.loopers.core.service.order.command.OrderProductsCommand;
+
+import java.util.List;
+
+public class OrderV1Dto {
+
+    public record OrderRequest(
+            List<OrderItemRequest> items,
+            String couponId
+    ) {
+
+        public OrderProductsCommand toCommand(String userIdentifier) {
+            return new OrderProductsCommand(
+                    userIdentifier,
+                    this.items.stream()
+                            .map(OrderItemRequest::toCommand)
+                            .toList(),
+                    this.couponId
+            );
+        }
+
+        public record OrderItemRequest(
+                String productId,
+                Long quantity
+        ) {
+
+            public OrderProductsCommand.OrderItem toCommand() {
+                return new OrderProductsCommand.OrderItem(productId, quantity);
+            }
+        }
+    }
+
+    public record OrderResponse(
+            String orderId
+    ) {
+    }
+
+    public record OrderListResponse(
+            List<OrderListItemResponse> items,
+            long totalElements,
+            int totalPages,
+            boolean hasNext,
+            boolean hasPrevious
+    ) {
+        public static OrderListResponse from(OrderListView view) {
+            return new OrderListResponse(
+                    view.getItems().stream()
+                            .map(OrderListItemResponse::from)
+                            .toList(),
+                    view.getTotalElements(),
+                    view.getTotalPages(),
+                    view.isHasNext(),
+                    view.isHasPrevious()
+            );
+        }
+
+        public record OrderListItemResponse(
+                String orderId,
+                String userId
+        ) {
+            public static OrderListItemResponse from(OrderListItem item) {
+                return new OrderListItemResponse(
+                        item.getOrderId().value(),
+                        item.getUserId().value()
+                );
+            }
+        }
+    }
+
+    public record OrderDetailResponse(
+            String orderId,
+            String userId,
+            List<OrderDetailItemResponse> items
+    ) {
+
+        public static OrderDetailResponse from(OrderDetail detail) {
+            return new OrderDetailResponse(
+                    detail.getOrder().getId().value(),
+                    detail.getOrder().getUserId().value(),
+                    detail.getOrderItems().stream()
+                            .map(OrderDetailItemResponse::from)
+                            .toList()
+            );
+        }
+
+        public record OrderDetailItemResponse(
+                String productId,
+                Long quantity
+        ) {
+            public static OrderDetailItemResponse from(OrderItem item) {
+                return new OrderDetailItemResponse(
+                        item.getProductId().value(),
+                        item.getQuantity().value()
+                );
+            }
+        }
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/product/ProductV1Api.java b/apps/commerce-api/src/main/java/com/loopers/application/api/product/ProductV1Api.java
new file mode 100644
index 000000000..9e17bc72f
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/product/ProductV1Api.java
@@ -0,0 +1,41 @@
+package com.loopers.application.api.product;
+
+import com.loopers.application.api.common.dto.ApiResponse;
+import com.loopers.core.domain.product.Product;
+import com.loopers.core.domain.product.ProductListView;
+import com.loopers.core.service.product.ProductQueryService;
+import com.loopers.core.service.product.query.GetProductListQuery;
+import com.loopers.core.service.product.query.GetProductQuery;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/v1/products")
+public class ProductV1Api implements ProductV1ApiSpec {
+
+    private final ProductQueryService queryService;
+
+    @Override
+    @GetMapping("/{productId}")
+    public ApiResponse<ProductV1Dto.GetProductResponse> getProduct(@PathVariable String productId) {
+        Product product = queryService.getProductBy(new GetProductQuery(productId));
+        return ApiResponse.success(ProductV1Dto.GetProductResponse.from(product));
+    }
+
+    @Override
+    @GetMapping
+    public ApiResponse<ProductV1Dto.GetProductListResponse> getProductList(
+            @RequestParam String brandId,
+            @RequestParam String createdAtSort,
+            @RequestParam String priceSort,
+            @RequestParam String likeCountSort,
+            @RequestParam int pageNo,
+            @RequestParam int pageSize
+    ) {
+        ProductListView productList = queryService.getProductList(new GetProductListQuery(
+                brandId, createdAtSort, priceSort, likeCountSort, pageNo, pageSize
+        ));
+        return ApiResponse.success(ProductV1Dto.GetProductListResponse.from(productList));
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/product/ProductV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/application/api/product/ProductV1ApiSpec.java
new file mode 100644
index 000000000..65a2e39a0
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/product/ProductV1ApiSpec.java
@@ -0,0 +1,29 @@
+package com.loopers.application.api.product;
+
+import com.loopers.application.api.common.dto.ApiResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+@Tag(name = "Product V1 API", description = "상품 API 입니다.")
+public interface ProductV1ApiSpec {
+
+    @Operation(
+            summary = "상품 정보 조회",
+            description = "상품 정보를 조회합니다."
+    )
+    ApiResponse<ProductV1Dto.GetProductResponse> getProduct(String productId);
+
+    @Operation(
+            summary = "상품 목록 조회",
+            description = "상품 목록을 조회합니다."
+    )
+    ApiResponse<ProductV1Dto.GetProductListResponse> getProductList(
+            String brandId,
+            String createdAtSort,
+            String priceSort,
+            String likeCountSort,
+            int pageNo,
+            int pageSize
+    );
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/product/ProductV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/application/api/product/ProductV1Dto.java
new file mode 100644
index 000000000..1b7aa2eb6
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/product/ProductV1Dto.java
@@ -0,0 +1,71 @@
+package com.loopers.application.api.product;
+
+import com.loopers.core.domain.product.Product;
+import com.loopers.core.domain.product.ProductListItem;
+import com.loopers.core.domain.product.ProductListView;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public class ProductV1Dto {
+
+    public record GetProductListResponse(
+            List<GetProductListItemResponse> items,
+            long totalElements,
+            int totalPages,
+            boolean hasNext,
+            boolean hasPrevious
+    ) {
+        public static GetProductListResponse from(ProductListView view) {
+            return new GetProductListResponse(
+                    view.getItems().stream()
+                            .map(GetProductListItemResponse::from)
+                            .toList(),
+                    view.getTotalElements(),
+                    view.getTotalPages(),
+                    view.isHasNext(),
+                    view.isHasPrevious()
+            );
+        }
+
+        public record GetProductListItemResponse(
+                String productId,
+                String brandId,
+                String name,
+                BigDecimal price,
+                Long stock,
+                Long likeCount
+        ) {
+            public static GetProductListItemResponse from(ProductListItem item) {
+                return new GetProductListItemResponse(
+                        item.getProductId().value(),
+                        item.getBrandId().value(),
+                        item.getName().value(),
+                        item.getPrice().value(),
+                        item.getStock().value(),
+                        item.getLikeCount().value()
+                );
+            }
+        }
+    }
+
+    public record GetProductResponse(
+            String productId,
+            String brandId,
+            String name,
+            BigDecimal price,
+            Long stock,
+            Long likeCount
+    ) {
+        public static GetProductResponse from(Product product) {
+            return new GetProductResponse(
+                    product.getId().value(),
+                    product.getBrandId().value(),
+                    product.getName().value(),
+                    product.getPrice().value(),
+                    product.getStock().value(),
+                    product.getLikeCount().value()
+            );
+        }
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/productlike/ProductLikeV1Api.java b/apps/commerce-api/src/main/java/com/loopers/application/api/productlike/ProductLikeV1Api.java
new file mode 100644
index 000000000..8e55ae350
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/productlike/ProductLikeV1Api.java
@@ -0,0 +1,59 @@
+package com.loopers.application.api.productlike;
+
+import com.loopers.application.api.common.dto.ApiResponse;
+import com.loopers.core.domain.productlike.LikeProductListView;
+import com.loopers.core.service.productlike.ProductLikeQueryService;
+import com.loopers.core.service.productlike.ProductLikeService;
+import com.loopers.core.service.productlike.command.ProductLikeCommand;
+import com.loopers.core.service.productlike.command.ProductUnlikeCommand;
+import com.loopers.core.service.productlike.query.GetLikeProductsListQuery;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import static com.loopers.application.api.productlike.ProductLikeV1Dto.LikeProductsResponse;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/v1/like/products")
+public class ProductLikeV1Api implements ProductLikeV1ApiSpec {
+
+    private final ProductLikeService service;
+    private final ProductLikeQueryService queryService;
+
+    @Override
+    @PostMapping("/{productId}")
+    public ApiResponse<Void> likeProduct(
+            @PathVariable String productId,
+            @RequestHeader(name = "X-USER-ID") String userIdentifier
+    ) {
+        service.like(new ProductLikeCommand(userIdentifier, productId));
+        return ApiResponse.success(null);
+    }
+
+    @Override
+    @DeleteMapping("/{productId}")
+    public ApiResponse<Void> unlikeProduct(
+            @PathVariable String productId,
+            @RequestHeader(name = "X-USER-ID") String userIdentifier
+    ) {
+        service.unlike(new ProductUnlikeCommand(userIdentifier, productId));
+        return ApiResponse.success(null);
+    }
+
+    @Override
+    @GetMapping
+    public ApiResponse<LikeProductsResponse> getLikeProducts(
+            @RequestHeader(name = "X-USER-ID") String userId,
+            @RequestParam String brandId,
+            @RequestParam String createdAtSort,
+            @RequestParam String priceSort,
+            @RequestParam String likeCountSort,
+            @RequestParam int pageNo,
+            @RequestParam int pageSize
+    ) {
+        LikeProductListView likeProductsListView = queryService.getLikeProductsListView(
+                new GetLikeProductsListQuery(userId, brandId, createdAtSort, priceSort, likeCountSort, pageNo, pageSize)
+        );
+        return ApiResponse.success(LikeProductsResponse.from(likeProductsListView));
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/productlike/ProductLikeV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/application/api/productlike/ProductLikeV1ApiSpec.java
new file mode 100644
index 000000000..19095b89f
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/productlike/ProductLikeV1ApiSpec.java
@@ -0,0 +1,36 @@
+package com.loopers.application.api.productlike;
+
+import com.loopers.application.api.common.dto.ApiResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+@Tag(name = "ProductLike V1 API", description = "상품 좋아요 API 입니다.")
+public interface ProductLikeV1ApiSpec {
+
+    @Operation(
+            summary = "상품 좋아요 등록",
+            description = "상품에 좋아요를 등록합니다."
+    )
+    ApiResponse<Void> likeProduct(String productId, String userIdentifier);
+
+    @Operation(
+            summary = "상품 좋아요 취소",
+            description = "상품의 좋아요를 취소합니다."
+    )
+    ApiResponse<Void> unlikeProduct(String productId, String userIdentifier);
+
+    @Operation(
+            summary = "좋아요 한 상품 목록 조회",
+            description = "사용자가 좋아요한 상품 목록을 조회합니다."
+    )
+    ApiResponse<ProductLikeV1Dto.LikeProductsResponse> getLikeProducts(
+            String userId,
+            String brandId,
+            String createdAtSort,
+            String priceSort,
+            String likeCountSort,
+            int pageNo,
+            int pageSize
+    );
+
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/productlike/ProductLikeV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/application/api/productlike/ProductLikeV1Dto.java
new file mode 100644
index 000000000..dd6c9cb82
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/productlike/ProductLikeV1Dto.java
@@ -0,0 +1,51 @@
+package com.loopers.application.api.productlike;
+
+import com.loopers.core.domain.productlike.LikeProductListItem;
+import com.loopers.core.domain.productlike.LikeProductListView;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public class ProductLikeV1Dto {
+
+    public record LikeProductsResponse(
+            List<LikeProductResponse> items,
+            long totalElements,
+            int totalPages,
+            boolean hasNext,
+            boolean hasPrevious
+    ) {
+        public static LikeProductsResponse from(LikeProductListView view) {
+            return new LikeProductsResponse(
+                    view.getItems().stream()
+                            .map(LikeProductResponse::from)
+                            .toList(),
+                    view.getTotalElements(),
+                    view.getTotalPages(),
+                    view.isHasNext(),
+                    view.isHasPrevious()
+            );
+        }
+
+
+        public record LikeProductResponse(
+                String productId,
+                String brandId,
+                String name,
+                BigDecimal price,
+                Long stock,
+                Long likeCount
+        ) {
+            public static LikeProductResponse from(LikeProductListItem item) {
+                return new LikeProductResponse(
+                        item.getProductId().value(),
+                        item.getBrandId().value(),
+                        item.getName().value(),
+                        item.getPrice().value(),
+                        item.getStock().value(),
+                        item.getLikeCount().value()
+                );
+            }
+        }
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/user/UserV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/application/api/user/UserV1ApiSpec.java
index b1f6899fb..d2d784bd2 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/api/user/UserV1ApiSpec.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/user/UserV1ApiSpec.java
@@ -7,7 +7,7 @@
 
 import static com.loopers.application.api.user.UserV1Dto.*;
 
-@Tag(name = "User V1 API", description = "User API 입니다.")
+@Tag(name = "User V1 API", description = "사용자 API 입니다.")
 public interface UserV1ApiSpec {
 
     @Operation(
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/api/user/UserV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/application/api/user/UserV1Dto.java
index edbf2c9bf..50b22c5c4 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/api/user/UserV1Dto.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/api/user/UserV1Dto.java
@@ -33,7 +33,7 @@ public record GetUserResponse(
 
         public static GetUserResponse from(User user) {
             return new GetUserResponse(
-                    user.getUserId().value(),
+                    user.getId().value(),
                     user.getIdentifier().value(),
                     user.getEmail().value(),
                     user.getBirthDay().value().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
@@ -52,7 +52,7 @@ public record JoinUserResponse(
 
         public static JoinUserResponse from(User user) {
             return new JoinUserResponse(
-                    user.getUserId().value(),
+                    user.getId().value(),
                     user.getIdentifier().value(),
                     user.getEmail().value(),
                     user.getBirthDay().value().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
diff --git a/apps/commerce-api/src/main/resources/http/brand/get-brand.http b/apps/commerce-api/src/main/resources/http/brand/get-brand.http
new file mode 100644
index 000000000..df8f94a84
--- /dev/null
+++ b/apps/commerce-api/src/main/resources/http/brand/get-brand.http
@@ -0,0 +1,3 @@
+### 브랜드 정보 조회
+GET {{commerce-api}}/api/v1/brands/1
+Content-Type: application/json
diff --git a/apps/commerce-api/src/main/resources/http/like/create-product-like.http b/apps/commerce-api/src/main/resources/http/like/create-product-like.http
new file mode 100644
index 000000000..2a2bfe62d
--- /dev/null
+++ b/apps/commerce-api/src/main/resources/http/like/create-product-like.http
@@ -0,0 +1,4 @@
+### 상품 좋아요 추가
+POST {{commerce-api}}/api/v1/like/products/1
+X-USER-ID: {{identifier}}
+Content-Type: application/json
diff --git a/apps/commerce-api/src/main/resources/http/like/delete-product-like.http b/apps/commerce-api/src/main/resources/http/like/delete-product-like.http
new file mode 100644
index 000000000..be0b6a181
--- /dev/null
+++ b/apps/commerce-api/src/main/resources/http/like/delete-product-like.http
@@ -0,0 +1,4 @@
+### 상품 좋아요 취소
+DELETE {{commerce-api}}/api/v1/like/products/1
+X-USER-ID: {{identifier}}
+Content-Type: application/json
diff --git a/apps/commerce-api/src/main/resources/http/like/get-liked-product-list.http b/apps/commerce-api/src/main/resources/http/like/get-liked-product-list.http
new file mode 100644
index 000000000..78daebc99
--- /dev/null
+++ b/apps/commerce-api/src/main/resources/http/like/get-liked-product-list.http
@@ -0,0 +1,29 @@
+### 좋아요한 상품 목록 조회 (기본)
+GET {{commerce-api}}/api/v1/like/products
+X-USER-ID: {{identifier}}
+Content-Type: application/json
+
+### 좋아요한 상품 목록 조회 (브랜드별 필터)
+GET {{commerce-api}}/api/v1/like/products?brandId=1
+X-USER-ID: {{identifier}}
+Content-Type: application/json
+
+### 좋아요한 상품 목록 조회 (가격 정렬)
+GET {{commerce-api}}/api/v1/like/products?priceSort=ASC
+X-USER-ID: {{identifier}}
+Content-Type: application/json
+
+### 좋아요한 상품 목록 조회 (좋아요 수 정렬)
+GET {{commerce-api}}/api/v1/like/products?likeCountSort=DESC
+X-USER-ID: {{identifier}}
+Content-Type: application/json
+
+### 좋아요한 상품 목록 조회 (페이지네이션)
+GET {{commerce-api}}/api/v1/like/products?pageNo=0&pageSize=20
+X-USER-ID: {{identifier}}
+Content-Type: application/json
+
+### 좋아요한 상품 목록 조회 (조합된 필터)
+GET {{commerce-api}}/api/v1/like/products?brandId=1&priceSort=ASC&pageNo=0&pageSize=20
+X-USER-ID: {{identifier}}
+Content-Type: application/json
diff --git a/apps/commerce-api/src/main/resources/http/order/create-order.http b/apps/commerce-api/src/main/resources/http/order/create-order.http
new file mode 100644
index 000000000..fcdeacdc7
--- /dev/null
+++ b/apps/commerce-api/src/main/resources/http/order/create-order.http
@@ -0,0 +1,16 @@
+POST {{commerce-api}}/api/v1/orders
+Content-Type: application/json
+X-USER-ID: {{identifier}}
+
+{
+  "orderItems": [
+    {
+      "productId": 1,
+      "quantity": 2
+    },
+    {
+      "productId": 2,
+      "quantity": 1
+    }
+  ]
+}
diff --git a/apps/commerce-api/src/main/resources/http/order/get-order-detail.http b/apps/commerce-api/src/main/resources/http/order/get-order-detail.http
new file mode 100644
index 000000000..a64b5c56b
--- /dev/null
+++ b/apps/commerce-api/src/main/resources/http/order/get-order-detail.http
@@ -0,0 +1,3 @@
+### 주문 상세 조회
+GET {{commerce-api}}/api/v1/orders/1
+Content-Type: application/json
diff --git a/apps/commerce-api/src/main/resources/http/order/get-order-list.http b/apps/commerce-api/src/main/resources/http/order/get-order-list.http
new file mode 100644
index 000000000..79829d8c6
--- /dev/null
+++ b/apps/commerce-api/src/main/resources/http/order/get-order-list.http
@@ -0,0 +1,19 @@
+### 주문 목록 조회 (기본)
+GET {{commerce-api}}/api/v1/orders
+X-USER-ID: {{identifier}}
+Content-Type: application/json
+
+### 주문 목록 조회 (생성일자 정렬)
+GET {{commerce-api}}/api/v1/orders?createdAtSort=DESC
+X-USER-ID: {{identifier}}
+Content-Type: application/json
+
+### 주문 목록 조회 (페이지네이션)
+GET {{commerce-api}}/api/v1/orders?pageNo=0&pageSize=20
+X-USER-ID: {{identifier}}
+Content-Type: application/json
+
+### 주문 목록 조회 (정렬 + 페이지네이션)
+GET {{commerce-api}}/api/v1/orders?createdAtSort=DESC&pageNo=0&pageSize=20
+X-USER-ID: {{identifier}}
+Content-Type: application/json
diff --git a/apps/commerce-api/src/main/resources/http/product/get-product-detail.http b/apps/commerce-api/src/main/resources/http/product/get-product-detail.http
new file mode 100644
index 000000000..10e71506a
--- /dev/null
+++ b/apps/commerce-api/src/main/resources/http/product/get-product-detail.http
@@ -0,0 +1,3 @@
+### 상품 상세 조회
+GET {{commerce-api}}/api/v1/products/1
+Content-Type: application/json
diff --git a/apps/commerce-api/src/main/resources/http/product/get-product-list.http b/apps/commerce-api/src/main/resources/http/product/get-product-list.http
new file mode 100644
index 000000000..2aa7abcf1
--- /dev/null
+++ b/apps/commerce-api/src/main/resources/http/product/get-product-list.http
@@ -0,0 +1,27 @@
+### 상품 목록 조회 (기본)
+GET {{commerce-api}}/api/v1/products
+Content-Type: application/json
+
+### 상품 목록 조회 (브랜드별 필터)
+GET {{commerce-api}}/api/v1/products?brandId=1
+Content-Type: application/json
+
+### 상품 목록 조회 (생성일자 정렬)
+GET {{commerce-api}}/api/v1/products?createdAtSort=DESC
+Content-Type: application/json
+
+### 상품 목록 조회 (가격 정렬)
+GET {{commerce-api}}/api/v1/products?priceSort=ASC
+Content-Type: application/json
+
+### 상품 목록 조회 (좋아요 수 정렬)
+GET {{commerce-api}}/api/v1/products?likeCountSort=DESC
+Content-Type: application/json
+
+### 상품 목록 조회 (페이지네이션)
+GET {{commerce-api}}/api/v1/products?pageNo=0&pageSize=20
+Content-Type: application/json
+
+### 상품 목록 조회 (조합된 필터)
+GET {{commerce-api}}/api/v1/products?brandId=1&priceSort=ASC&pageNo=0&pageSize=20
+Content-Type: application/json
diff --git a/apps/commerce-api/src/main/resources/http/charge-user-point.http b/apps/commerce-api/src/main/resources/http/user/charge-user-point.http
similarity index 100%
rename from apps/commerce-api/src/main/resources/http/charge-user-point.http
rename to apps/commerce-api/src/main/resources/http/user/charge-user-point.http
diff --git a/apps/commerce-api/src/main/resources/http/get-user-by-identifier.http b/apps/commerce-api/src/main/resources/http/user/get-user-by-identifier.http
similarity index 100%
rename from apps/commerce-api/src/main/resources/http/get-user-by-identifier.http
rename to apps/commerce-api/src/main/resources/http/user/get-user-by-identifier.http
diff --git a/apps/commerce-api/src/main/resources/http/get-user-point-by-user-identifier.http b/apps/commerce-api/src/main/resources/http/user/get-user-point-by-user-identifier.http
similarity index 100%
rename from apps/commerce-api/src/main/resources/http/get-user-point-by-user-identifier.http
rename to apps/commerce-api/src/main/resources/http/user/get-user-point-by-user-identifier.http
diff --git a/apps/commerce-api/src/main/resources/http/join-user.http b/apps/commerce-api/src/main/resources/http/user/join-user.http
similarity index 100%
rename from apps/commerce-api/src/main/resources/http/join-user.http
rename to apps/commerce-api/src/main/resources/http/user/join-user.http
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/api/brand/BrandV1ApiIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/api/brand/BrandV1ApiIntegrationTest.java
new file mode 100644
index 000000000..2d5bb823b
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/application/api/brand/BrandV1ApiIntegrationTest.java
@@ -0,0 +1,85 @@
+package com.loopers.application.api.brand;
+
+import com.loopers.application.api.ApiIntegrationTest;
+import com.loopers.application.api.common.dto.ApiResponse;
+import com.loopers.core.domain.brand.Brand;
+import com.loopers.core.domain.brand.repository.BrandRepository;
+import com.loopers.core.domain.brand.vo.BrandId;
+import org.instancio.Instancio;
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import static com.loopers.application.api.brand.BrandV1Dto.GetBrandResponse;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.instancio.Select.field;
+
+class BrandV1ApiIntegrationTest extends ApiIntegrationTest {
+
+    @Autowired
+    BrandRepository brandRepository;
+
+    @Nested
+    @DisplayName("브랜드 조회")
+    class 브랜드_조회 {
+
+        @Nested
+        @DisplayName("정상 요청인 경우")
+        class 정상_요청인_경우 {
+
+            String brandId;
+
+            @BeforeEach
+            void setUp() {
+                brandId = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .create()
+                ).getId().value();
+            }
+
+            @Test
+            @DisplayName("Status 200")
+            void Status200() {
+                //given
+                String endPoint = "/api/v1/brands/" + brandId;
+                ParameterizedTypeReference<ApiResponse<GetBrandResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                //when
+                ResponseEntity<ApiResponse<GetBrandResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, HttpEntity.EMPTY, responseType);
+
+                //then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+            }
+        }
+
+        @Nested
+        @DisplayName("브랜드가 존재하지 않는 경우")
+        class 브랜드가_존재하지_않는_경우 {
+
+            @Test
+            @DisplayName("Status 404")
+            void Status404() {
+                String endPoint = "/api/v1/brands/99999";
+                ParameterizedTypeReference<ApiResponse<GetBrandResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+                ResponseEntity<ApiResponse<GetBrandResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, HttpEntity.EMPTY, responseType);
+
+                //then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/api/order/OrderV1ApiIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/api/order/OrderV1ApiIntegrationTest.java
new file mode 100644
index 000000000..f5e959223
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/application/api/order/OrderV1ApiIntegrationTest.java
@@ -0,0 +1,390 @@
+package com.loopers.application.api.order;
+
+import com.loopers.application.api.ApiIntegrationTest;
+import com.loopers.application.api.common.dto.ApiResponse;
+import com.loopers.core.domain.brand.Brand;
+import com.loopers.core.domain.brand.repository.BrandRepository;
+import com.loopers.core.domain.brand.vo.BrandId;
+import com.loopers.core.domain.order.Order;
+import com.loopers.core.domain.order.OrderItem;
+import com.loopers.core.domain.order.repository.OrderItemRepository;
+import com.loopers.core.domain.order.repository.OrderRepository;
+import com.loopers.core.domain.order.vo.OrderId;
+import com.loopers.core.domain.order.vo.OrderItemId;
+import com.loopers.core.domain.product.Product;
+import com.loopers.core.domain.product.repository.ProductRepository;
+import com.loopers.core.domain.product.vo.ProductId;
+import com.loopers.core.domain.user.User;
+import com.loopers.core.domain.user.UserPoint;
+import com.loopers.core.domain.user.repository.UserPointRepository;
+import com.loopers.core.domain.user.repository.UserRepository;
+import com.loopers.core.domain.user.vo.*;
+import org.instancio.Instancio;
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static com.loopers.application.api.order.OrderV1Dto.*;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.instancio.Select.field;
+
+class OrderV1ApiIntegrationTest extends ApiIntegrationTest {
+
+    @Autowired
+    UserRepository userRepository;
+
+    @Autowired
+    BrandRepository brandRepository;
+
+    @Autowired
+    ProductRepository productRepository;
+
+    @Autowired
+    UserPointRepository userPointRepository;
+
+    @Autowired
+    OrderRepository orderRepository;
+
+    @Autowired
+    OrderItemRepository orderItemRepository;
+
+    String userIdentifier;
+    String productId;
+    Product product;
+
+    @Nested
+    @DisplayName("주문 요청")
+    class 주문_요청 {
+
+        @Nested
+        @DisplayName("정상 요청인 경우")
+        class 정상_요청인_경우 {
+
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
+                );
+                userIdentifier = user.getIdentifier().value();
+                userPointRepository.save(
+                        Instancio.of(UserPoint.class)
+                                .set(field("id"), UserPointId.empty())
+                                .set(field("userId"), user.getId())
+                                .set(field("balance"), new UserPointBalance(new BigDecimal(100_000)))
+                                .create()
+                );
+
+                Brand brand = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .create()
+                );
+
+                product = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), brand.getId())
+                                .create()
+                );
+                productId = product.getId().value();
+            }
+
+            @Test
+            @DisplayName("Status 200")
+            void status200() {
+                // When
+                String endPoint = "/api/v1/orders";
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", userIdentifier);
+                headers.setContentType(MediaType.APPLICATION_JSON);
+
+                OrderRequest request = new OrderRequest(
+                        List.of(
+                                new OrderRequest.OrderItemRequest(productId, 2L)
+                        ),
+                        null
+                );
+
+                HttpEntity<OrderRequest> httpEntity = new HttpEntity<>(request, headers);
+
+                ParameterizedTypeReference<ApiResponse<OrderResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<OrderResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.POST, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+            }
+        }
+
+        @Nested
+        @DisplayName("X-USER-ID 헤더가 없을 경우")
+        class X_USER_ID_헤더가_없을_경우 {
+
+            @Test
+            @DisplayName("Status 400")
+            void status400() {
+                // When
+                String endPoint = "/api/v1/orders";
+                HttpHeaders headers = new HttpHeaders();
+                headers.setContentType(MediaType.APPLICATION_JSON);
+
+                OrderRequest request = new OrderRequest(
+                        List.of(
+                                new OrderRequest.OrderItemRequest(productId, 2L)
+                        ),
+                        "1"
+                );
+
+                HttpEntity<OrderRequest> httpEntity = new HttpEntity<>(request, headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.POST, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
+            }
+        }
+
+        @Nested
+        @DisplayName("존재하지 않는 사용자로 요청할 경우")
+        class 존재하지_않는_사용자로_요청 {
+
+            @Test
+            @DisplayName("Status 404")
+            void status404() {
+                // When
+                String endPoint = "/api/v1/orders";
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", "nonExistentUser");
+                headers.setContentType(MediaType.APPLICATION_JSON);
+
+                OrderRequest request = new OrderRequest(
+                        List.of(
+                                new OrderRequest.OrderItemRequest(productId, 2L)
+                        ),
+                        null
+                );
+
+                HttpEntity<OrderRequest> httpEntity = new HttpEntity<>(request, headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.POST, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+
+        @Nested
+        @DisplayName("존재하지 않는 상품 ID로 요청할 경우")
+        class 존재하지_않는_상품_ID로_요청 {
+
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
+                );
+                userIdentifier = user.getIdentifier().value();
+            }
+
+            @Test
+            @DisplayName("404 Not Found 응답을 반환한다.")
+            void notFound응답을_반환한다() {
+                // When
+                String endPoint = "/api/v1/orders";
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", userIdentifier);
+                headers.setContentType(MediaType.APPLICATION_JSON);
+
+                OrderRequest request = new OrderRequest(
+                        List.of(
+                                new OrderRequest.OrderItemRequest("99999", 2L)
+                        ),
+                        null
+                );
+
+                HttpEntity<OrderRequest> httpEntity = new HttpEntity<>(request, headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.POST, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+    }
+
+    @Nested
+    @DisplayName("주문 목록 조회")
+    class 주문_목록_조회 {
+
+        @Nested
+        @DisplayName("정상 요청인 경우")
+        class 정상_요청인_경우 {
+
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
+                );
+
+                userIdentifier = user.getIdentifier().value();
+                orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), user.getId())
+                                .create()
+                );
+
+                orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), user.getId())
+                                .create()
+                );
+            }
+
+            @Test
+            @DisplayName("Status 200")
+            void Status200() {
+                String endPoint = "/api/v1/orders?createdAtSort=DESC&pageNo=0&pageSize=10";
+                ParameterizedTypeReference<ApiResponse<OrderListResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", "kilian");
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ResponseEntity<ApiResponse<OrderListResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, httpEntity, responseType);
+
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+            }
+        }
+
+        @Nested
+        @DisplayName("사용자가 존재하지 않는 경우")
+        class 사용자가_존재하지_않는_경우 {
+
+            @Test
+            @DisplayName("Status 404")
+            void Status404() {
+                String endPoint = "/api/v1/orders?createdAtSort=DESC&pageNo=0&pageSize=10";
+                ParameterizedTypeReference<ApiResponse<OrderListResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", "kilian");
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ResponseEntity<ApiResponse<OrderListResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, httpEntity, responseType);
+
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+    }
+
+    @Nested
+    @DisplayName("상품 상세 조회")
+    class 상품_상세_조회 {
+
+        @Nested
+        @DisplayName("정상 조회 되는 경우")
+        class 정상_조회_되는_경우 {
+
+            Order savedOrder;
+
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
+                );
+
+                userIdentifier = user.getIdentifier().value();
+                savedOrder = orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), user.getId())
+                                .create()
+                );
+                orderItemRepository.save(
+                        Instancio.of(OrderItem.class)
+                                .set(field(OrderItem::getId), OrderItemId.empty())
+                                .set(field(OrderItem::getProductId), new ProductId("1"))
+                                .set(field(OrderItem::getOrderId), savedOrder.getId())
+                                .create()
+                );
+            }
+
+            @Test
+            @DisplayName("Status 200")
+            void Status200() {
+                String endPoint = "/api/v1/orders/" + savedOrder.getId().value();
+                ParameterizedTypeReference<ApiResponse<OrderDetailResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+                ResponseEntity<ApiResponse<OrderDetailResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, HttpEntity.EMPTY, responseType);
+
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+            }
+        }
+
+        @Nested
+        @DisplayName("주문이 존재하지 않는 경우")
+        class 주문이_존재하지_않는_경우 {
+
+            @Test
+            @DisplayName("Status 404")
+            void Status404() {
+                String endPoint = "/api/v1/orders/99999";
+                ParameterizedTypeReference<ApiResponse<OrderDetailResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+                ResponseEntity<ApiResponse<OrderDetailResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, HttpEntity.EMPTY, responseType);
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/api/product/ProductV1ApiIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/api/product/ProductV1ApiIntegrationTest.java
new file mode 100644
index 000000000..27b05d5f7
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/application/api/product/ProductV1ApiIntegrationTest.java
@@ -0,0 +1,158 @@
+package com.loopers.application.api.product;
+
+import com.loopers.application.api.ApiIntegrationTest;
+import com.loopers.application.api.common.dto.ApiResponse;
+import com.loopers.core.domain.brand.Brand;
+import com.loopers.core.domain.brand.repository.BrandRepository;
+import com.loopers.core.domain.brand.vo.BrandId;
+import com.loopers.core.domain.common.vo.DeletedAt;
+import com.loopers.core.domain.product.Product;
+import com.loopers.core.domain.product.repository.ProductRepository;
+import com.loopers.core.domain.product.vo.ProductId;
+import org.instancio.Instancio;
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import static com.loopers.application.api.product.ProductV1Dto.GetProductListResponse;
+import static com.loopers.application.api.product.ProductV1Dto.GetProductResponse;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.instancio.Select.field;
+
+class ProductV1ApiIntegrationTest extends ApiIntegrationTest {
+
+    @Autowired
+    private BrandRepository brandRepository;
+
+    @Autowired
+    private ProductRepository productRepository;
+
+    @Nested
+    @DisplayName("상품 목록 조회")
+    class 상품_목록_조회 {
+
+        @Nested
+        @DisplayName("정상 요청인 경우")
+        class 정상_요청인_경우 {
+
+            String brandId;
+
+            @BeforeEach
+            void setUp() {
+                Brand brand = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .create()
+                );
+                brandId = brand.getId().value();
+
+                Product product1 = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), brand.getId())
+                                .set(field(Product::getDeletedAt), DeletedAt.empty())
+                                .create()
+                );
+
+                Product product2 = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), brand.getId())
+                                .set(field(Product::getDeletedAt), DeletedAt.empty())
+                                .create()
+                );
+            }
+
+            @Test
+            @DisplayName("Status 200")
+            void status200() {
+                // When
+                String endPoint = "/api/v1/products?brandId=" + brandId
+                        + "&createdAtSort=&priceSort=&likeCountSort=ASC&pageNo=0&pageSize=10";
+
+                ParameterizedTypeReference<ApiResponse<GetProductListResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<GetProductListResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, HttpEntity.EMPTY, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+            }
+        }
+    }
+
+    @Nested
+    @DisplayName("상품 정보 조회")
+    class 상품_정보_조회 {
+
+        @Nested
+        @DisplayName("정상 요청인 경우")
+        class 정상_요청인_경우 {
+
+            String productId;
+
+            @BeforeEach
+            void setUp() {
+                Brand brand = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .create()
+                );
+
+                productId = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), brand.getId())
+                                .set(field(Product::getDeletedAt), DeletedAt.empty())
+                                .create()
+                ).getId().value();
+            }
+
+            @Test
+            @DisplayName("Status 200")
+            void status200() {
+                // When
+                String endPoint = "/api/v1/products/" + productId;
+
+                ParameterizedTypeReference<ApiResponse<GetProductResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<GetProductResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, HttpEntity.EMPTY, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+            }
+        }
+
+        @Nested
+        @DisplayName("상품이 존재하지 않는 경우")
+        class 상품이_존재하지_않는_경우 {
+
+            @Test
+            @DisplayName("Status 404")
+            void status404() {
+                String endPoint = "/api/v1/products/99999";
+                ParameterizedTypeReference<ApiResponse<GetProductResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<GetProductResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, HttpEntity.EMPTY, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/api/productlike/ProductLikeV1ApiIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/api/productlike/ProductLikeV1ApiIntegrationTest.java
new file mode 100644
index 000000000..e1ea08274
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/application/api/productlike/ProductLikeV1ApiIntegrationTest.java
@@ -0,0 +1,450 @@
+package com.loopers.application.api.productlike;
+
+import com.loopers.application.api.ApiIntegrationTest;
+import com.loopers.application.api.common.dto.ApiResponse;
+import com.loopers.core.domain.brand.Brand;
+import com.loopers.core.domain.brand.repository.BrandRepository;
+import com.loopers.core.domain.brand.vo.BrandDescription;
+import com.loopers.core.domain.brand.vo.BrandId;
+import com.loopers.core.domain.brand.vo.BrandName;
+import com.loopers.core.domain.product.Product;
+import com.loopers.core.domain.product.repository.ProductRepository;
+import com.loopers.core.domain.product.vo.*;
+import com.loopers.core.domain.user.User;
+import com.loopers.core.domain.user.repository.UserRepository;
+import com.loopers.core.domain.user.type.UserGender;
+import com.loopers.core.domain.user.vo.UserBirthDay;
+import com.loopers.core.domain.user.vo.UserEmail;
+import com.loopers.core.domain.user.vo.UserId;
+import com.loopers.core.domain.user.vo.UserIdentifier;
+import org.instancio.Instancio;
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+
+import java.math.BigDecimal;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.instancio.Select.field;
+
+class ProductLikeV1ApiIntegrationTest extends ApiIntegrationTest {
+
+    @Autowired
+    protected UserRepository userRepository;
+
+    @Autowired
+    protected BrandRepository brandRepository;
+
+    @Autowired
+    protected ProductRepository productRepository;
+
+    protected String userId;
+    protected String productId;
+    protected Product product;
+
+    @Nested
+    @DisplayName("상품 좋아요 등록")
+    class 상품_좋아요_등록 {
+
+        @Nested
+        @DisplayName("정상 요청인 경우")
+        class 정상_요청인_경우 {
+
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
+                );
+                userId = user.getIdentifier().value();
+
+                Brand brand = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .create()
+                );
+
+                product = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), brand.getId())
+                                .set(field(Product::getLikeCount), ProductLikeCount.init())
+                                .create()
+                );
+                productId = product.getId().value();
+            }
+
+            @Test
+            @DisplayName("Status 200")
+            void status200() {
+                // When
+                String endPoint = "/api/v1/like/products/" + productId;
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", userId);
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.POST, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+                assertThat(response.getBody()).isNotNull();
+
+                // 좋아요 수 확인
+                Product likedProduct = productRepository.getById(new ProductId(productId));
+                assertThat(likedProduct.getLikeCount().value()).isEqualTo(1);
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+            }
+        }
+
+        @Nested
+        @DisplayName("X-USER-ID 헤더가 없을 경우")
+        class X_USER_ID_헤더가_없을_경우 {
+
+            @Test
+            @DisplayName("Status 400")
+            void status400() {
+                // When
+                String endPoint = "/api/v1/like/products/" + productId;
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.POST, HttpEntity.EMPTY, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
+            }
+        }
+
+        @Nested
+        @DisplayName("존재하지 않는 상품 ID로 요청할 경우")
+        class 존재하지_않는_상품_ID로_요청 {
+
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        User.create(
+                                UserIdentifier.create("kilian"),
+                                UserEmail.create("kilian@gmail.com"),
+                                UserBirthDay.create("1997-10-08"),
+                                UserGender.create("MALE")
+                        )
+                );
+                userId = user.getIdentifier().value();
+            }
+
+            @Test
+            @DisplayName("Status 404")
+            void status404() {
+                // When
+                String endPoint = "/api/v1/like/products/99999";
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", userId);
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.POST, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+
+        @Nested
+        @DisplayName("존재하지 않는 사용자로 요청할 경우")
+        class 존재하지_않는_사용자로_요청 {
+
+            @Test
+            @DisplayName("Status 404")
+            void status404() {
+                // When
+                String endPoint = "/api/v1/like/products/" + productId;
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", "99999");
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.POST, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+    }
+
+    @Nested
+    @DisplayName("상품 좋아요 취소")
+    class 상품_좋아요_취소 {
+
+        @Nested
+        @DisplayName("좋아요가 등록되어 있을 경우")
+        class 좋아요가_등록되어_있을_경우 {
+
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        User.create(
+                                UserIdentifier.create("kilian"),
+                                UserEmail.create("kilian@gmail.com"),
+                                UserBirthDay.create("1997-10-08"),
+                                UserGender.create("MALE")
+                        )
+                );
+                userId = user.getIdentifier().value();
+
+                Brand brand = brandRepository.save(
+                        Brand.create(new BrandName("kilian"), new BrandDescription("향수 브랜드"))
+                );
+
+                productId = productRepository.save(
+                        Product.create(
+                                brand.getId(),
+                                new ProductName("엔젤스 쉐어"),
+                                new ProductPrice(new BigDecimal("150.00")),
+                                new ProductStock(10L)
+                        )
+                ).getId().value();
+
+                // 좋아요 등록
+                String endPoint = "/api/v1/like/products/" + productId;
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", userId);
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                testRestTemplate.exchange(endPoint, HttpMethod.POST, httpEntity, responseType);
+            }
+
+            @Test
+            @DisplayName("Status 200")
+            void status200() {
+                // When
+                String endPoint = "/api/v1/like/products/" + productId;
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", userId);
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.DELETE, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+            }
+        }
+
+        @Nested
+        @DisplayName("X-USER-ID 헤더가 없을 경우")
+        class X_USER_ID_헤더가_없을_경우 {
+
+            @Test
+            @DisplayName("Status 400")
+            void status400() {
+                // When
+                String endPoint = "/api/v1/like/products/" + productId;
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.DELETE, HttpEntity.EMPTY, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
+            }
+        }
+
+        @Nested
+        @DisplayName("존재하지 않는 상품 ID로 요청할 경우")
+        class 존재하지_않는_상품_ID로_요청 {
+
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
+                );
+                userId = user.getIdentifier().value();
+            }
+
+            @Test
+            @DisplayName("Status 404")
+            void status404() {
+                // When
+                String endPoint = "/api/v1/like/products/99999";
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", userId);
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.DELETE, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+
+        @Nested
+        @DisplayName("존재하지 않는 사용자로 요청할 경우")
+        class 존재하지_않는_사용자로_요청 {
+
+            @Test
+            @DisplayName("404 Not Found 응답을 반환한다.")
+            void notFound응답을_반환한다() {
+                // When
+                String endPoint = "/api/v1/like/products/" + productId;
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", "nonExistUser");
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.DELETE, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+            }
+        }
+    }
+
+    @Nested
+    @DisplayName("좋아요한 상품 목록 조회")
+    class 좋아요한_상품_목록_조회 {
+
+        @BeforeEach
+        void setUp() {
+            // 사용자 생성
+            User user = userRepository.save(
+                    Instancio.of(User.class)
+                            .set(field(User::getId), UserId.empty())
+                            .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                            .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                            .create()
+            );
+            userId = user.getIdentifier().value();
+
+            // 브랜드 생성
+            Brand brand = brandRepository.save(
+                    Instancio.of(Brand.class)
+                            .set(field(Brand::getId), BrandId.empty())
+                            .create()
+            );
+
+            // 상품 생성 (2개)
+            Product product1 = productRepository.save(
+                    Instancio.of(Product.class)
+                            .set(field(Product::getId), ProductId.empty())
+                            .set(field(Product::getBrandId), brand.getId())
+                            .set(field(Product::getLikeCount), ProductLikeCount.init())
+                            .create()
+            );
+            Product product2 = productRepository.save(
+                    Instancio.of(Product.class)
+                            .set(field(Product::getId), ProductId.empty())
+                            .set(field(Product::getBrandId), brand.getId())
+                            .set(field(Product::getLikeCount), ProductLikeCount.init())
+                            .create()
+            );
+
+            // 좋아요 등록
+            String likeEndpoint1 = "/api/v1/like/products/" + product1.getId().value();
+            String likeEndpoint2 = "/api/v1/like/products/" + product2.getId().value();
+            HttpHeaders headers = new HttpHeaders();
+            headers.set("X-USER-ID", userId);
+            HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+            ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                    new ParameterizedTypeReference<>() {
+                    };
+
+            testRestTemplate.exchange(likeEndpoint1, HttpMethod.POST, httpEntity, responseType);
+            testRestTemplate.exchange(likeEndpoint2, HttpMethod.POST, httpEntity, responseType);
+        }
+
+        @Nested
+        @DisplayName("정상 요청인 경우")
+        class 정상_요청인_경우 {
+
+            @Test
+            @DisplayName("Status 200")
+            void status200() {
+                // When
+                String endPoint = "/api/v1/like/products?brandId=1&createdAtSort=&priceSort=&likeCountSort=&pageNo=0&pageSize=10";
+                HttpHeaders headers = new HttpHeaders();
+                headers.set("X-USER-ID", userId);
+                HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
+
+                ParameterizedTypeReference<ApiResponse<ProductLikeV1Dto.LikeProductsResponse>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<ProductLikeV1Dto.LikeProductsResponse>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, httpEntity, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+            }
+        }
+
+        @Nested
+        @DisplayName("X-USER-ID 헤더가 없을 경우")
+        class X_USER_ID_헤더가_없을_경우 {
+
+            @Test
+            @DisplayName("Status 400")
+            void status400() {
+                // When
+                String endPoint = "/api/v1/like/products?brandId=null&createdAtSort=&priceSort=&likeCountSort=&pageNo=0&pageSize=10";
+
+                ParameterizedTypeReference<ApiResponse<Void>> responseType =
+                        new ParameterizedTypeReference<>() {
+                        };
+
+                ResponseEntity<ApiResponse<Void>> response =
+                        testRestTemplate.exchange(endPoint, HttpMethod.GET, HttpEntity.EMPTY, responseType);
+
+                // Then
+                assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
+            }
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/api/user/UserV1ApiApiIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/api/user/UserV1ApiApiIntegrationTest.java
index 77155076e..4573ed5f8 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/api/user/UserV1ApiApiIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/api/user/UserV1ApiApiIntegrationTest.java
@@ -4,12 +4,12 @@
 import com.loopers.application.api.common.dto.ApiResponse;
 import com.loopers.core.domain.user.User;
 import com.loopers.core.domain.user.repository.UserRepository;
-import com.loopers.core.domain.user.type.UserGender;
-import com.loopers.core.domain.user.vo.UserBirthDay;
 import com.loopers.core.domain.user.vo.UserEmail;
+import com.loopers.core.domain.user.vo.UserId;
 import com.loopers.core.domain.user.vo.UserIdentifier;
 import com.loopers.core.service.user.JoinUserService;
 import com.loopers.core.service.user.command.JoinUserCommand;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -24,7 +24,7 @@
 import java.math.BigDecimal;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.instancio.Select.field;
 
 class UserV1ApiApiIntegrationTest extends ApiIntegrationTest {
 
@@ -43,8 +43,8 @@ class 회원가입 {
         class 정상_요청인_경우 {
 
             @Test
-            @DisplayName("생성된 유저의 정보를 반환한다.")
-            void 생성된_유저의_정보를_반환한다() {
+            @DisplayName("Status 200")
+            void status200() {
                 // given
                 UserV1Dto.JoinUserRequest request = new UserV1Dto.JoinUserRequest(
                         "user123",
@@ -64,12 +64,6 @@ class 정상_요청인_경우 {
 
                 // then
                 assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
-                assertThat(response.getBody()).isNotNull();
-                assertThat(response.getBody().data()).isNotNull();
-                assertThat(response.getBody().data().identifier()).isEqualTo("user123");
-                assertThat(response.getBody().data().email()).isEqualTo("user@example.com");
-                assertThat(response.getBody().data().birthday()).isEqualTo("2000-01-15");
-                assertThat(response.getBody().data().gender()).isEqualTo("MALE");
             }
         }
 
@@ -78,8 +72,8 @@ class 정상_요청인_경우 {
         class 성별이_없는_경우 {
 
             @Test
-            @DisplayName("400 Bad Request 응답을 반환한다.")
-            void badRequest응답을_반환한다() {
+            @DisplayName("Status 400")
+            void status400() {
                 // given
                 UserV1Dto.JoinUserRequest request = new UserV1Dto.JoinUserRequest(
                         "user123",
@@ -112,18 +106,17 @@ class 성공할_경우 {
             @BeforeEach
             void setUp() {
                 userRepository.save(
-                        User.create(
-                                UserIdentifier.create("kilian"),
-                                UserEmail.create("kilian@gmail.com"),
-                                UserBirthDay.create("1997-10-08"),
-                                UserGender.create("MALE")
-                        )
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
                 );
             }
 
             @Test
-            @DisplayName("해당하는 유저 정보를 응답으로 반환한다.")
-            void 해당하는_유저_정보를_응답으로_반환한다() {
+            @DisplayName("Status 200")
+            void status200() {
                 // given
                 String identifier = "kilian";
                 String endPoint = "/api/v1/users/" + identifier;
@@ -137,14 +130,6 @@ void setUp() {
 
                 // then
                 assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
-                assertSoftly(softly -> {
-                    softly.assertThat(response.getBody()).isNotNull();
-                    softly.assertThat(response.getBody().data()).isNotNull();
-                    softly.assertThat(response.getBody().data().identifier()).isEqualTo("kilian");
-                    softly.assertThat(response.getBody().data().email()).isEqualTo("kilian@gmail.com");
-                    softly.assertThat(response.getBody().data().birthday()).isEqualTo("1997-10-08");
-                    softly.assertThat(response.getBody().data().gender()).isEqualTo("MALE");
-                });
             }
         }
 
@@ -153,8 +138,8 @@ void setUp() {
         class 존재하지_않는_ID로_조회할_경우 {
 
             @Test
-            @DisplayName("404 Not Found 응답을 반환한다.")
-            void notFound응답을_반환한다() {
+            @DisplayName("Status 404")
+            void status404() {
                 // given
                 String identifier = "nonExist";
                 String endPoint = "/api/v1/users/" + identifier;
@@ -191,8 +176,8 @@ void setUp() {
             }
 
             @Test
-            @DisplayName("보유 포인트를 응답으로 반환한다.")
-            void 보유_포인트를_응답으로_반환한다() {
+            @DisplayName("Status 200")
+            void status200() {
                 // given
                 String userIdentifier = "kilian";
                 String endPoint = "/api/v1/users/points";
@@ -209,11 +194,6 @@ void setUp() {
 
                 // then
                 assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
-                assertSoftly(softly -> {
-                    softly.assertThat(response.getBody()).isNotNull();
-                    softly.assertThat(response.getBody().data()).isNotNull();
-                    softly.assertThat(response.getBody().data().balance().intValue()).isEqualTo(0);
-                });
             }
         }
 
@@ -222,8 +202,8 @@ void setUp() {
         class X_USER_ID_헤더가_없을_경우 {
 
             @Test
-            @DisplayName("400 Bad Request 응답을 반환한다.")
-            void badRequest응답을_반환한다() {
+            @DisplayName("Status 400")
+            void status400() {
                 // given
                 String endPoint = "/api/v1/users/points";
                 ParameterizedTypeReference<ApiResponse<Void>> responseType =
@@ -259,8 +239,8 @@ void setUp() {
             }
 
             @Test
-            @DisplayName("충전된 보유 총량을 응답으로 반환한다.")
-            void 포인트_충전_성공() {
+            @DisplayName("Status 200")
+            void status200() {
                 // given
                 String userIdentifier = "kilian";
                 String endPoint = "/api/v1/users/points/charge";
@@ -278,11 +258,6 @@ void setUp() {
 
                 // then
                 assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
-                assertSoftly(softly -> {
-                    softly.assertThat(response.getBody()).isNotNull();
-                    softly.assertThat(response.getBody().data()).isNotNull();
-                    softly.assertThat(response.getBody().data().balance().intValue()).isEqualTo(1000);
-                });
             }
         }
 
@@ -291,8 +266,8 @@ void setUp() {
         class 존재하지_않는_유저로_요청 {
 
             @Test
-            @DisplayName("404 Not Found 응답을 반환한다.")
-            void notFound응답을_반환한다() {
+            @DisplayName("Status 404")
+            void status404() {
                 // given
                 String userIdentifier = "nonExist";
                 String endPoint = "/api/v1/users/points/charge";
diff --git a/core/domain/src/main/java/com/loopers/core/domain/brand/Brand.java b/core/domain/src/main/java/com/loopers/core/domain/brand/Brand.java
index d4384825e..50811e030 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/brand/Brand.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/brand/Brand.java
@@ -11,7 +11,7 @@
 @Getter
 public class Brand {
 
-    private final BrandId brandId;
+    private final BrandId id;
 
     private final BrandName name;
 
@@ -24,14 +24,14 @@ public class Brand {
     private final DeletedAt deletedAt;
 
     private Brand(
-            BrandId brandId,
+            BrandId id,
             BrandName name,
             BrandDescription description,
             CreatedAt createdAt,
             UpdatedAt updatedAt,
             DeletedAt deletedAt
     ) {
-        this.brandId = brandId;
+        this.id = id;
         this.name = name;
         this.description = description;
         this.createdAt = createdAt;
diff --git a/core/domain/src/main/java/com/loopers/core/domain/error/DomainErrorCode.java b/core/domain/src/main/java/com/loopers/core/domain/error/DomainErrorCode.java
index 0d9be76cc..d15e283ef 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/error/DomainErrorCode.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/error/DomainErrorCode.java
@@ -17,7 +17,10 @@ public enum DomainErrorCode {
     CANNOT_CHARGE_POINTS_NEGATIVE(1009, "음수 포인트를 충전할 수 없습니다."),
     PRODUCT_OUT_OF_STOCK(1010, "상품의 재고가 부족합니다."),
     COULD_NOT_BE_NEGATIVE(1011, "%s는(은) 음수가 될 수 없습니다."),
-    NOT_ENOUGH_USER_POINT_BALANCE(1012, "사용자의 포인트 잔액이 충분하지 않습니다.");
+    NOT_ENOUGH_USER_POINT_BALANCE(1012, "사용자의 포인트 잔액이 충분하지 않습니다."),
+    NOT_SUPPORTED_COUPON_TYPE(1013, "지원하지 않는 쿠폰 유형입니다."),
+    NOT_AVAILABLE_COUPON_STATUS(1014, "사용할 수 없는 쿠폰의 상태입니다."),
+    OTHER_USING_THIS_COUPON(1015, "누군가 이미 사용중인 쿠폰입니다.");
 
     private final int code;
 
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/AbstractCoupon.java b/core/domain/src/main/java/com/loopers/core/domain/order/AbstractCoupon.java
new file mode 100644
index 000000000..da11f4952
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/AbstractCoupon.java
@@ -0,0 +1,78 @@
+package com.loopers.core.domain.order;
+
+import com.loopers.core.domain.common.vo.CreatedAt;
+import com.loopers.core.domain.common.vo.DeletedAt;
+import com.loopers.core.domain.common.vo.UpdatedAt;
+import com.loopers.core.domain.error.DomainErrorCode;
+import com.loopers.core.domain.order.type.CouponStatus;
+import com.loopers.core.domain.order.vo.CouponId;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import com.loopers.core.domain.user.vo.UserId;
+import lombok.Getter;
+
+import java.math.BigDecimal;
+
+@Getter
+public abstract class AbstractCoupon implements Coupon {
+
+    protected CouponId couponId;
+
+    protected UserId userId;
+
+    protected CouponStatus status;
+
+    protected CreatedAt createdAt;
+
+    protected UpdatedAt updatedAt;
+
+    protected DeletedAt deletedAt;
+
+    protected Long version;
+
+    protected AbstractCoupon(
+            CouponId couponId,
+            UserId userId,
+            CouponStatus status,
+            CreatedAt createdAt,
+            UpdatedAt updatedAt,
+            DeletedAt deletedAt,
+            Long version
+    ) {
+        this.couponId = couponId;
+        this.userId = userId;
+        this.status = status;
+        this.createdAt = createdAt;
+        this.updatedAt = updatedAt;
+        this.deletedAt = deletedAt;
+        this.version = version;
+    }
+
+    protected AbstractCoupon(
+            AbstractCoupon abstractCoupon
+    ) {
+        this.couponId = abstractCoupon.couponId;
+        this.userId = abstractCoupon.userId;
+        this.status = abstractCoupon.status;
+        this.createdAt = abstractCoupon.createdAt;
+        this.updatedAt = abstractCoupon.updatedAt;
+        this.deletedAt = abstractCoupon.deletedAt;
+        this.version = abstractCoupon.version;
+    }
+
+    @Override
+    public void use() {
+        this.status = CouponStatus.USED;
+        this.updatedAt = UpdatedAt.now();
+    }
+
+    public PayAmount discount(PayAmount payAmount) {
+        if (this.status != CouponStatus.AVAILABLE) {
+            throw new IllegalArgumentException(DomainErrorCode.NOT_AVAILABLE_COUPON_STATUS.getMessage());
+        }
+
+        BigDecimal discountAmount = calculateDiscountAmount(payAmount);
+        return payAmount.minus(discountAmount);
+    }
+
+    public abstract BigDecimal calculateDiscountAmount(PayAmount payAmount);
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/AmountDiscountCoupon.java b/core/domain/src/main/java/com/loopers/core/domain/order/AmountDiscountCoupon.java
new file mode 100644
index 000000000..6905e0be1
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/AmountDiscountCoupon.java
@@ -0,0 +1,36 @@
+package com.loopers.core.domain.order;
+
+import com.loopers.core.domain.order.vo.AmountDiscountCouponId;
+import com.loopers.core.domain.order.vo.CouponDiscountAmount;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import lombok.Getter;
+
+import java.math.BigDecimal;
+
+@Getter
+public class AmountDiscountCoupon extends AbstractCoupon {
+
+    private final AmountDiscountCouponId id;
+
+    private final CouponDiscountAmount amount;
+
+    private AmountDiscountCoupon(AbstractCoupon abstractCoupon, AmountDiscountCouponId id, CouponDiscountAmount amount) {
+        super(abstractCoupon);
+        this.id = id;
+        this.amount = amount;
+    }
+
+    public static AmountDiscountCoupon create(AbstractCoupon abstractCoupon, CouponDiscountAmount amount) {
+        return new AmountDiscountCoupon(abstractCoupon, AmountDiscountCouponId.empty(), amount);
+    }
+
+    public static AmountDiscountCoupon mappedBy(AmountDiscountCouponId id, AbstractCoupon abstractCoupon, CouponDiscountAmount amount) {
+        return new AmountDiscountCoupon(abstractCoupon, id, amount);
+    }
+
+
+    @Override
+    public BigDecimal calculateDiscountAmount(PayAmount payAmount) {
+        return this.amount.value();
+    }
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/Coupon.java b/core/domain/src/main/java/com/loopers/core/domain/order/Coupon.java
new file mode 100644
index 000000000..50935980a
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/Coupon.java
@@ -0,0 +1,10 @@
+package com.loopers.core.domain.order;
+
+import com.loopers.core.domain.payment.vo.PayAmount;
+
+public interface Coupon {
+
+    PayAmount discount(PayAmount payAmount);
+
+    void use();
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/DefaultCoupon.java b/core/domain/src/main/java/com/loopers/core/domain/order/DefaultCoupon.java
new file mode 100644
index 000000000..ee70ed942
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/DefaultCoupon.java
@@ -0,0 +1,62 @@
+package com.loopers.core.domain.order;
+
+import com.loopers.core.domain.common.vo.CreatedAt;
+import com.loopers.core.domain.common.vo.DeletedAt;
+import com.loopers.core.domain.common.vo.UpdatedAt;
+import com.loopers.core.domain.order.type.CouponStatus;
+import com.loopers.core.domain.order.vo.CouponId;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import com.loopers.core.domain.user.vo.UserId;
+import lombok.AccessLevel;
+import lombok.Builder;
+
+import java.math.BigDecimal;
+
+public class DefaultCoupon extends AbstractCoupon {
+
+    @Builder(access = AccessLevel.PRIVATE, toBuilder = true)
+    private DefaultCoupon(
+            CouponId couponId,
+            UserId userId,
+            CouponStatus status,
+            CreatedAt createdAt,
+            UpdatedAt updatedAt,
+            DeletedAt deletedAt,
+            Long version
+    ) {
+        super(couponId, userId, status, createdAt, updatedAt, deletedAt, version);
+    }
+
+    protected DefaultCoupon(AbstractCoupon coupon) {
+        super(coupon);
+    }
+
+    public static DefaultCoupon create(UserId userId) {
+        return new DefaultCoupon(
+                CouponId.empty(),
+                userId,
+                CouponStatus.AVAILABLE,
+                CreatedAt.now(),
+                UpdatedAt.now(),
+                DeletedAt.empty(),
+                0L
+        );
+    }
+
+    public static DefaultCoupon mappedBy(
+            CouponId couponId,
+            UserId userId,
+            CouponStatus status,
+            CreatedAt createdAt,
+            UpdatedAt updatedAt,
+            DeletedAt deletedAt,
+            Long version
+    ) {
+        return new DefaultCoupon(couponId, userId, status, createdAt, updatedAt, deletedAt, version);
+    }
+
+    @Override
+    public BigDecimal calculateDiscountAmount(PayAmount payAmount) {
+        return BigDecimal.ZERO;
+    }
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/Order.java b/core/domain/src/main/java/com/loopers/core/domain/order/Order.java
index a6f8f4c12..4d7a9ace0 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/order/Order.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/Order.java
@@ -10,7 +10,7 @@
 @Getter
 public class Order {
 
-    private final OrderId orderId;
+    private final OrderId id;
 
     private final UserId userId;
 
@@ -21,13 +21,13 @@ public class Order {
     private final DeletedAt deletedAt;
 
     private Order(
-            OrderId orderId,
+            OrderId id,
             UserId userId,
             CreatedAt createdAt,
             UpdatedAt updatedAt,
             DeletedAt deletedAt
     ) {
-        this.orderId = orderId;
+        this.id = id;
         this.userId = userId;
         this.createdAt = createdAt;
         this.updatedAt = updatedAt;
@@ -35,13 +35,13 @@ private Order(
     }
 
     public static Order mappedBy(
-            OrderId orderId,
+            OrderId id,
             UserId userId,
             CreatedAt createdAt,
             UpdatedAt updatedAt,
             DeletedAt deletedAt
     ) {
-        return new Order(orderId, userId, createdAt, updatedAt, deletedAt);
+        return new Order(id, userId, createdAt, updatedAt, deletedAt);
     }
 
 
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/RateDiscountCoupon.java b/core/domain/src/main/java/com/loopers/core/domain/order/RateDiscountCoupon.java
new file mode 100644
index 000000000..b203235a7
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/RateDiscountCoupon.java
@@ -0,0 +1,39 @@
+package com.loopers.core.domain.order;
+
+import com.loopers.core.domain.order.vo.CouponDiscountRate;
+import com.loopers.core.domain.order.vo.RateDiscountCouponId;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import lombok.Getter;
+
+import java.math.BigDecimal;
+
+@Getter
+public class RateDiscountCoupon extends AbstractCoupon {
+
+    private final RateDiscountCouponId id;
+
+    private final CouponDiscountRate rate;
+
+    private RateDiscountCoupon(
+            AbstractCoupon abstractCoupon,
+            RateDiscountCouponId id,
+            CouponDiscountRate rate
+    ) {
+        super(abstractCoupon);
+        this.id = id;
+        this.rate = rate;
+    }
+
+    public static RateDiscountCoupon create(AbstractCoupon abstractCoupon, CouponDiscountRate rate) {
+        return new RateDiscountCoupon(abstractCoupon, RateDiscountCouponId.empty(), rate);
+    }
+
+    public static RateDiscountCoupon mappedBy(RateDiscountCouponId id, AbstractCoupon abstractCoupon, CouponDiscountRate rate) {
+        return new RateDiscountCoupon(abstractCoupon, id, rate);
+    }
+
+    @Override
+    public BigDecimal calculateDiscountAmount(PayAmount payAmount) {
+        return this.rate.calculate(payAmount);
+    }
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/repository/CouponRepository.java b/core/domain/src/main/java/com/loopers/core/domain/order/repository/CouponRepository.java
new file mode 100644
index 000000000..481c84875
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/repository/CouponRepository.java
@@ -0,0 +1,11 @@
+package com.loopers.core.domain.order.repository;
+
+import com.loopers.core.domain.order.Coupon;
+import com.loopers.core.domain.order.vo.CouponId;
+
+public interface CouponRepository {
+
+    Coupon save(Coupon coupon);
+
+    Coupon getById(CouponId couponId);
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/type/CouponStatus.java b/core/domain/src/main/java/com/loopers/core/domain/order/type/CouponStatus.java
new file mode 100644
index 000000000..6e2196b36
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/type/CouponStatus.java
@@ -0,0 +1,5 @@
+package com.loopers.core.domain.order.type;
+
+public enum CouponStatus {
+    USED, EXPIRED, AVAILABLE
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/vo/AmountDiscountCouponId.java b/core/domain/src/main/java/com/loopers/core/domain/order/vo/AmountDiscountCouponId.java
new file mode 100644
index 000000000..0519f1988
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/vo/AmountDiscountCouponId.java
@@ -0,0 +1,8 @@
+package com.loopers.core.domain.order.vo;
+
+public record AmountDiscountCouponId(String value) {
+
+    public static AmountDiscountCouponId empty() {
+        return new AmountDiscountCouponId(null);
+    }
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/vo/CouponDiscountAmount.java b/core/domain/src/main/java/com/loopers/core/domain/order/vo/CouponDiscountAmount.java
new file mode 100644
index 000000000..281417d43
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/vo/CouponDiscountAmount.java
@@ -0,0 +1,26 @@
+package com.loopers.core.domain.order.vo;
+
+import com.loopers.core.domain.error.DomainErrorCode;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+
+public record CouponDiscountAmount(BigDecimal value) {
+
+    private static final String FIELD_NAME = "쿠폰 할인금액";
+
+    public CouponDiscountAmount {
+        validateNotNull(value);
+        validateNotNegative(value);
+    }
+
+    private static void validateNotNull(BigDecimal value) {
+        Objects.requireNonNull(value, DomainErrorCode.notNullMessage(FIELD_NAME));
+    }
+
+    private static void validateNotNegative(BigDecimal value) {
+        if (value.signum() < 0) {
+            throw new IllegalArgumentException(DomainErrorCode.negativeMessage(FIELD_NAME));
+        }
+    }
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/vo/CouponDiscountRate.java b/core/domain/src/main/java/com/loopers/core/domain/order/vo/CouponDiscountRate.java
new file mode 100644
index 000000000..37fb7b277
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/vo/CouponDiscountRate.java
@@ -0,0 +1,32 @@
+package com.loopers.core.domain.order.vo;
+
+import com.loopers.core.domain.error.DomainErrorCode;
+import com.loopers.core.domain.payment.vo.PayAmount;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+
+public record CouponDiscountRate(BigDecimal value) {
+
+    private static final BigDecimal MIN_RATE = BigDecimal.ZERO;
+    private static final BigDecimal MAX_RATE = new BigDecimal("100.00");
+
+    public CouponDiscountRate {
+        validateNotNull(value);
+        validateRange(value);
+    }
+
+    private static void validateNotNull(BigDecimal value) {
+        Objects.requireNonNull(value, DomainErrorCode.notNullMessage("쿠폰 할인율"));
+    }
+
+    private static void validateRange(BigDecimal value) {
+        if (value.compareTo(MIN_RATE) < 0 || value.compareTo(MAX_RATE) > 0) {
+            throw new IllegalArgumentException("할인율은 0~100 사이여야 합니다");
+        }
+    }
+
+    public BigDecimal calculate(PayAmount payAmount) {
+        return payAmount.applyRateDiscount(this.value);
+    }
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/vo/CouponId.java b/core/domain/src/main/java/com/loopers/core/domain/order/vo/CouponId.java
new file mode 100644
index 000000000..64f10c5e3
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/vo/CouponId.java
@@ -0,0 +1,14 @@
+package com.loopers.core.domain.order.vo;
+
+import java.util.Objects;
+
+public record CouponId(String value) {
+
+    public static CouponId empty() {
+        return new CouponId(null);
+    }
+
+    public boolean nonEmpty() {
+        return Objects.nonNull(value);
+    }
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/order/vo/RateDiscountCouponId.java b/core/domain/src/main/java/com/loopers/core/domain/order/vo/RateDiscountCouponId.java
new file mode 100644
index 000000000..2466d13ba
--- /dev/null
+++ b/core/domain/src/main/java/com/loopers/core/domain/order/vo/RateDiscountCouponId.java
@@ -0,0 +1,8 @@
+package com.loopers.core.domain.order.vo;
+
+public record RateDiscountCouponId(String value) {
+
+    public static RateDiscountCouponId empty() {
+        return new RateDiscountCouponId(null);
+    }
+}
diff --git a/core/domain/src/main/java/com/loopers/core/domain/payment/vo/PayAmount.java b/core/domain/src/main/java/com/loopers/core/domain/payment/vo/PayAmount.java
index 7969e6be7..9d1693f04 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/payment/vo/PayAmount.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/payment/vo/PayAmount.java
@@ -27,4 +27,18 @@ private static void validateNull(BigDecimal value) {
             throw new NullPointerException(notNullMessage(FIELD_NAME));
         }
     }
+
+    public PayAmount minus(BigDecimal amount) {
+        BigDecimal minusValue = this.value.subtract(amount);
+        if (minusValue.signum() < 0) {
+            return new PayAmount(BigDecimal.ZERO);
+        }
+
+        return new PayAmount(minusValue);
+    }
+
+    public BigDecimal applyRateDiscount(BigDecimal discountRate) {
+        return this.value.multiply(discountRate).divide(new BigDecimal("100"), 2, java.math.RoundingMode.HALF_UP);
+    }
+
 }
diff --git a/core/domain/src/main/java/com/loopers/core/domain/product/Product.java b/core/domain/src/main/java/com/loopers/core/domain/product/Product.java
index 70eb60a1c..144157dc1 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/product/Product.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/product/Product.java
@@ -15,7 +15,7 @@
 @Getter
 public class Product {
 
-    private final ProductId productId;
+    private final ProductId id;
 
     private final BrandId brandId;
 
@@ -35,7 +35,7 @@ public class Product {
 
     @Builder(access = AccessLevel.PRIVATE, toBuilder = true)
     private Product(
-            ProductId productId,
+            ProductId id,
             BrandId brandId,
             ProductName name,
             ProductPrice price,
@@ -45,7 +45,7 @@ private Product(
             UpdatedAt updatedAt,
             DeletedAt deletedAt
     ) {
-        this.productId = productId;
+        this.id = id;
         this.brandId = brandId;
         this.name = name;
         this.price = price;
@@ -59,14 +59,15 @@ private Product(
     public static Product create(
             BrandId brandId,
             ProductName name,
-            ProductPrice price
+            ProductPrice price,
+            ProductStock productStock
     ) {
         return new Product(
                 ProductId.empty(),
                 brandId,
                 name,
                 price,
-                ProductStock.init(),
+                productStock,
                 ProductLikeCount.init(),
                 CreatedAt.now(),
                 UpdatedAt.now(),
diff --git a/core/domain/src/main/java/com/loopers/core/domain/product/repository/ProductRepository.java b/core/domain/src/main/java/com/loopers/core/domain/product/repository/ProductRepository.java
index b6ec1acc7..20c270475 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/product/repository/ProductRepository.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/product/repository/ProductRepository.java
@@ -10,6 +10,8 @@ public interface ProductRepository {
 
     Product getById(ProductId productId);
 
+    Product getByIdWithLock(ProductId productId);
+
     ProductListView findListWithCondition(
             BrandId brandId,
             OrderSort createdAtSort,
diff --git a/core/domain/src/main/java/com/loopers/core/domain/product/vo/ProductStock.java b/core/domain/src/main/java/com/loopers/core/domain/product/vo/ProductStock.java
index 8411a643b..0d1515ca6 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/product/vo/ProductStock.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/product/vo/ProductStock.java
@@ -16,10 +16,6 @@ public record ProductStock(Long value) {
         validateNotNegative(value);
     }
 
-    public static ProductStock init() {
-        return new ProductStock(0L);
-    }
-
     private static void validateNotNull(Long value) {
         Objects.requireNonNull(value, DomainErrorCode.notNullMessage(FIELD_NAME));
     }
diff --git a/core/domain/src/main/java/com/loopers/core/domain/productlike/repository/ProductLikeRepository.java b/core/domain/src/main/java/com/loopers/core/domain/productlike/repository/ProductLikeRepository.java
index c037c7a34..5330c2c63 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/productlike/repository/ProductLikeRepository.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/productlike/repository/ProductLikeRepository.java
@@ -17,6 +17,8 @@ public interface ProductLikeRepository {
 
     Optional<ProductLike> findByUserIdAndProductId(UserId userId, ProductId productId);
 
+    Optional<ProductLike> findByUserIdAndProductIdWithLock(UserId userId, ProductId productId);
+
     LikeProductListView findLikeProductsListWithCondition(
             UserId userId,
             BrandId brandId,
diff --git a/core/domain/src/main/java/com/loopers/core/domain/user/User.java b/core/domain/src/main/java/com/loopers/core/domain/user/User.java
index a4d68019d..b6236c8f1 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/user/User.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/user/User.java
@@ -13,7 +13,7 @@
 @Getter
 public class User {
 
-    private final UserId userId;
+    private final UserId id;
 
     private final UserIdentifier identifier;
 
@@ -30,7 +30,7 @@ public class User {
     private final DeletedAt deletedAt;
 
     private User(
-            UserId userId,
+            UserId id,
             UserIdentifier identifier,
             UserEmail email,
             UserBirthDay birthDay,
@@ -39,7 +39,7 @@ private User(
             UpdatedAt updatedAt,
             DeletedAt deletedAt
     ) {
-        this.userId = userId;
+        this.id = id;
         this.identifier = identifier;
         this.email = email;
         this.birthDay = birthDay;
diff --git a/core/domain/src/main/java/com/loopers/core/domain/user/repository/UserPointRepository.java b/core/domain/src/main/java/com/loopers/core/domain/user/repository/UserPointRepository.java
index 5bedd7734..f474a9253 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/user/repository/UserPointRepository.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/user/repository/UserPointRepository.java
@@ -3,13 +3,11 @@
 import com.loopers.core.domain.user.UserPoint;
 import com.loopers.core.domain.user.vo.UserId;
 
-import java.util.Optional;
-
 public interface UserPointRepository {
 
     UserPoint save(UserPoint userPoint);
 
     UserPoint getByUserId(UserId userId);
 
-    Optional<UserPoint> findByUserId(UserId userId);
+    UserPoint getByUserIdWithLock(UserId userId);
 }
diff --git a/core/domain/src/main/java/com/loopers/core/domain/user/repository/UserRepository.java b/core/domain/src/main/java/com/loopers/core/domain/user/repository/UserRepository.java
index 68a2942bf..c7b2197be 100644
--- a/core/domain/src/main/java/com/loopers/core/domain/user/repository/UserRepository.java
+++ b/core/domain/src/main/java/com/loopers/core/domain/user/repository/UserRepository.java
@@ -15,4 +15,6 @@ public interface UserRepository {
     Optional<User> findByIdentifier(UserIdentifier identifier);
 
     User getByIdentifier(UserIdentifier identifier);
+
+    User getByIdentifierWithLock(UserIdentifier identifier);
 }
diff --git a/core/domain/src/test/java/com/loopers/core/domain/order/AbstractCouponTest.java b/core/domain/src/test/java/com/loopers/core/domain/order/AbstractCouponTest.java
new file mode 100644
index 000000000..8b7831e64
--- /dev/null
+++ b/core/domain/src/test/java/com/loopers/core/domain/order/AbstractCouponTest.java
@@ -0,0 +1,38 @@
+package com.loopers.core.domain.order;
+
+import com.loopers.core.domain.order.type.CouponStatus;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import org.instancio.Instancio;
+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.assertThatThrownBy;
+import static org.instancio.Select.field;
+
+class AbstractCouponTest {
+
+    @Nested
+    @DisplayName("discount()")
+    class DiscountMethod {
+
+        @Nested
+        @DisplayName("이미 사용된 쿠폰이면")
+        class couponUnavailable {
+
+            @Test
+            @DisplayName("예외가 발생한다.")
+            void throwException() {
+                DefaultCoupon defaultCoupon = Instancio.of(DefaultCoupon.class)
+                        .set(field(DefaultCoupon::getStatus), CouponStatus.USED)
+                        .create();
+                PayAmount payAmount = new PayAmount(new BigDecimal(1000));
+                assertThatThrownBy(() -> defaultCoupon.discount(payAmount))
+                        .isInstanceOf(IllegalArgumentException.class)
+                        .hasMessageContaining("사용할 수 없는");
+            }
+        }
+    }
+}
diff --git a/core/domain/src/test/java/com/loopers/core/domain/order/AmountDiscountCouponTest.java b/core/domain/src/test/java/com/loopers/core/domain/order/AmountDiscountCouponTest.java
new file mode 100644
index 000000000..0a9bf3169
--- /dev/null
+++ b/core/domain/src/test/java/com/loopers/core/domain/order/AmountDiscountCouponTest.java
@@ -0,0 +1,38 @@
+package com.loopers.core.domain.order;
+
+import com.loopers.core.domain.order.vo.CouponDiscountAmount;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import org.instancio.Instancio;
+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.instancio.Select.field;
+
+@DisplayName("정액 할인 쿠폰")
+class AmountDiscountCouponTest {
+
+    @Nested
+    @DisplayName("calculateDiscountAmount()")
+    class CalculateDiscountAmount {
+
+        @Nested
+        @DisplayName("할인 금액은")
+        class DiscountAmount {
+
+            @Test
+            @DisplayName("일정 양을 가진다.")
+            void discountAmount() {
+                PayAmount payAmount = new PayAmount(new BigDecimal(10000));
+                AmountDiscountCoupon coupon = Instancio.of(AmountDiscountCoupon.class)
+                        .set(field(AmountDiscountCoupon::getAmount), new CouponDiscountAmount(new BigDecimal(1000)))
+                        .create();
+
+                assertThat(coupon.calculateDiscountAmount(payAmount)).isEqualByComparingTo(new BigDecimal(1000));
+            }
+        }
+    }
+}
diff --git a/core/domain/src/test/java/com/loopers/core/domain/order/DefaultCouponTest.java b/core/domain/src/test/java/com/loopers/core/domain/order/DefaultCouponTest.java
new file mode 100644
index 000000000..7fa86cd20
--- /dev/null
+++ b/core/domain/src/test/java/com/loopers/core/domain/order/DefaultCouponTest.java
@@ -0,0 +1,33 @@
+package com.loopers.core.domain.order;
+
+import com.loopers.core.domain.user.vo.UserId;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import static com.loopers.core.domain.order.type.CouponStatus.AVAILABLE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DefaultCouponTest {
+
+    @Nested
+    @DisplayName("create()")
+    class CreateMethod {
+
+        @Nested
+        @DisplayName("정상 생성되었을때")
+        class DefaultValue {
+
+            @Test
+            @DisplayName("기본값을 가진다.")
+            void hasDefaultValue() {
+                DefaultCoupon coupon = DefaultCoupon.create(
+                        new UserId("1")
+                );
+
+                assertThat(coupon.status).isEqualTo(AVAILABLE);
+            }
+        }
+    }
+
+}
diff --git a/core/domain/src/test/java/com/loopers/core/domain/order/RateDiscountCouponTest.java b/core/domain/src/test/java/com/loopers/core/domain/order/RateDiscountCouponTest.java
new file mode 100644
index 000000000..4a3f4b4da
--- /dev/null
+++ b/core/domain/src/test/java/com/loopers/core/domain/order/RateDiscountCouponTest.java
@@ -0,0 +1,37 @@
+package com.loopers.core.domain.order;
+
+import com.loopers.core.domain.order.vo.CouponDiscountRate;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import org.instancio.Instancio;
+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.instancio.Select.field;
+
+class RateDiscountCouponTest {
+
+    @Nested
+    @DisplayName("calculateDiscountAmount()")
+    class CalculateDiscountAmount {
+
+        @Nested
+        @DisplayName("할인 금액은")
+        class DiscountAmount {
+
+            @Test
+            @DisplayName("일정 비율을 가진다.")
+            void discountAmount() {
+                PayAmount payAmount = new PayAmount(new BigDecimal(10000));
+                RateDiscountCoupon coupon = Instancio.of(RateDiscountCoupon.class)
+                        .set(field(RateDiscountCoupon::getRate), new CouponDiscountRate(new BigDecimal(20)))
+                        .create();
+
+                assertThat(coupon.calculateDiscountAmount(payAmount)).isEqualByComparingTo(new BigDecimal(2000));
+            }
+        }
+    }
+}
diff --git a/core/domain/src/test/java/com/loopers/core/domain/order/vo/CouponDiscountAmountTest.java b/core/domain/src/test/java/com/loopers/core/domain/order/vo/CouponDiscountAmountTest.java
new file mode 100644
index 000000000..b00869939
--- /dev/null
+++ b/core/domain/src/test/java/com/loopers/core/domain/order/vo/CouponDiscountAmountTest.java
@@ -0,0 +1,85 @@
+package com.loopers.core.domain.order.vo;
+
+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.MethodSource;
+
+import java.math.BigDecimal;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+@DisplayName("쿠폰 할인금액")
+class CouponDiscountAmountTest {
+
+    @Nested
+    @DisplayName("생성자")
+    class Constructor {
+
+        @Nested
+        @DisplayName("유효한 값인 경우")
+        class 유효한_값인_경우 {
+
+            static Stream<BigDecimal> validDiscountAmounts() {
+                return Stream.of(
+                        BigDecimal.ZERO,
+                        new BigDecimal(1),
+                        new BigDecimal(1000),
+                        new BigDecimal(10000),
+                        new BigDecimal("50000.50")
+                );
+            }
+
+            @ParameterizedTest
+            @MethodSource("validDiscountAmounts")
+            @DisplayName("CouponDisCountAmount 객체를 생성한다")
+            void 객체_생성(BigDecimal validAmount) {
+                // when
+                CouponDiscountAmount discountAmount = new CouponDiscountAmount(validAmount);
+
+                // then
+                assertThat(discountAmount.value()).isEqualByComparingTo(validAmount);
+            }
+        }
+
+        @Nested
+        @DisplayName("값이 음수인 경우")
+        class 값이_음수인_경우 {
+
+            static Stream<BigDecimal> invalidDiscountAmounts() {
+                return Stream.of(
+                        new BigDecimal(-1),
+                        new BigDecimal(-100),
+                        new BigDecimal("-10000.50")
+                );
+            }
+
+            @ParameterizedTest
+            @MethodSource("invalidDiscountAmounts")
+            @DisplayName("예외를 발생시킨다")
+            void 예외_발생(BigDecimal invalidAmount) {
+                // when & then
+                assertThatThrownBy(() -> new CouponDiscountAmount(invalidAmount))
+                        .isInstanceOf(IllegalArgumentException.class)
+                        .hasMessage("쿠폰 할인금액는(은) 음수가 될 수 없습니다.");
+            }
+        }
+
+        @Nested
+        @DisplayName("값이 null인 경우")
+        class 값이_null인_경우 {
+
+            @Test
+            @DisplayName("NullPointerException이 발생한다")
+            void NullPointerException이_발생한다() {
+                // when & then
+                assertThatThrownBy(() -> new CouponDiscountAmount(null))
+                        .isInstanceOf(NullPointerException.class)
+                        .hasMessage("쿠폰 할인금액는(은) Null이 될 수 없습니다.");
+            }
+        }
+    }
+}
diff --git a/core/domain/src/test/java/com/loopers/core/domain/order/vo/CouponDiscountRateTest.java b/core/domain/src/test/java/com/loopers/core/domain/order/vo/CouponDiscountRateTest.java
new file mode 100644
index 000000000..b457d4276
--- /dev/null
+++ b/core/domain/src/test/java/com/loopers/core/domain/order/vo/CouponDiscountRateTest.java
@@ -0,0 +1,94 @@
+package com.loopers.core.domain.order.vo;
+
+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;
+
+@DisplayName("쿠폰 할인율")
+class CouponDiscountRateTest {
+
+    @Nested
+    @DisplayName("쿠폰 할인율 생성 시")
+    class 쿠폰_할인율_생성 {
+
+        @Nested
+        @DisplayName("값이 null인 경우")
+        class 값이_null인_경우 {
+
+            @Test
+            @DisplayName("NullPointerException이 발생한다.")
+            void NullPointerException이_발생한다() {
+                assertThatThrownBy(() -> new CouponDiscountRate(null))
+                        .isInstanceOf(NullPointerException.class);
+            }
+        }
+
+        @Nested
+        @DisplayName("값이 0 미만인 경우")
+        class 값이_0_미만인_경우 {
+
+            @Test
+            @DisplayName("IllegalArgumentException이 발생한다.")
+            void IllegalArgumentException이_발생한다() {
+                assertThatThrownBy(() -> new CouponDiscountRate(new BigDecimal("-0.01")))
+                        .isInstanceOf(IllegalArgumentException.class)
+                        .hasMessage("할인율은 0~100 사이여야 합니다");
+            }
+        }
+
+        @Nested
+        @DisplayName("값이 100을 초과하는 경우")
+        class 값이_100을_초과하는_경우 {
+
+            @Test
+            @DisplayName("IllegalArgumentException이 발생한다.")
+            void IllegalArgumentException이_발생한다() {
+                assertThatThrownBy(() -> new CouponDiscountRate(new BigDecimal("100.01")))
+                        .isInstanceOf(IllegalArgumentException.class)
+                        .hasMessage("할인율은 0~100 사이여야 합니다");
+            }
+        }
+
+        @Nested
+        @DisplayName("유효한 할인율이 주어진 경우")
+        class 유효한_할인율이_주어진_경우 {
+
+            @Test
+            @DisplayName("쿠폰 할인율이 생성된다.")
+            void 쿠폰_할인율이_생성된다() {
+                CouponDiscountRate rate = new CouponDiscountRate(new BigDecimal("15.50"));
+
+                assertThat(rate.value()).isEqualByComparingTo(new BigDecimal("15.50"));
+            }
+
+            @Test
+            @DisplayName("최소값(0)으로 생성된다.")
+            void 최소값으로_생성된다() {
+                CouponDiscountRate rate = new CouponDiscountRate(BigDecimal.ZERO);
+
+                assertThat(rate.value()).isEqualByComparingTo(BigDecimal.ZERO);
+            }
+
+            @Test
+            @DisplayName("최대값(100)으로 생성된다.")
+            void 최대값으로_생성된다() {
+                CouponDiscountRate rate = new CouponDiscountRate(new BigDecimal("100.00"));
+
+                assertThat(rate.value()).isEqualByComparingTo(new BigDecimal("100.00"));
+            }
+
+            @Test
+            @DisplayName("정수값으로 생성된다.")
+            void 정수값으로_생성된다() {
+                CouponDiscountRate rate = new CouponDiscountRate(new BigDecimal("10"));
+
+                assertThat(rate.value()).isEqualByComparingTo(new BigDecimal("10"));
+            }
+        }
+    }
+}
diff --git a/core/domain/src/test/java/com/loopers/core/domain/payment/vo/PayAmountTest.java b/core/domain/src/test/java/com/loopers/core/domain/payment/vo/PayAmountTest.java
index 94d74204b..e2ed46941 100644
--- a/core/domain/src/test/java/com/loopers/core/domain/payment/vo/PayAmountTest.java
+++ b/core/domain/src/test/java/com/loopers/core/domain/payment/vo/PayAmountTest.java
@@ -25,11 +25,11 @@ class 유효한_값인_경우 {
 
             static Stream<BigDecimal> validPayAmounts() {
                 return Stream.of(
-                    BigDecimal.ZERO,
-                    new BigDecimal(1),
-                    new BigDecimal(10000),
-                    new BigDecimal(100000),
-                    new BigDecimal("999999.99")
+                        BigDecimal.ZERO,
+                        new BigDecimal(1),
+                        new BigDecimal(10000),
+                        new BigDecimal(100000),
+                        new BigDecimal("999999.99")
                 );
             }
 
@@ -51,9 +51,9 @@ class 값이_음수인_경우 {
 
             static Stream<BigDecimal> invalidPayAmounts() {
                 return Stream.of(
-                    new BigDecimal(-1),
-                    new BigDecimal(-100),
-                    new BigDecimal("-10000.50")
+                        new BigDecimal(-1),
+                        new BigDecimal(-100),
+                        new BigDecimal("-10000.50")
                 );
             }
 
@@ -63,8 +63,8 @@ static Stream<BigDecimal> invalidPayAmounts() {
             void 예외_발생(BigDecimal invalidAmount) {
                 // when & then
                 assertThatThrownBy(() -> new PayAmount(invalidAmount))
-                    .isInstanceOf(IllegalArgumentException.class)
-                    .hasMessage("결제 총 금액는(은) 음수가 될 수 없습니다.");
+                        .isInstanceOf(IllegalArgumentException.class)
+                        .hasMessage("결제 총 금액는(은) 음수가 될 수 없습니다.");
             }
         }
 
@@ -77,8 +77,41 @@ class 값이_null인_경우 {
             void NullPointerException이_발생한다() {
                 // when & then
                 assertThatThrownBy(() -> new PayAmount(null))
-                    .isInstanceOf(NullPointerException.class)
-                    .hasMessage("결제 총 금액는(은) Null이 될 수 없습니다.");
+                        .isInstanceOf(NullPointerException.class)
+                        .hasMessage("결제 총 금액는(은) Null이 될 수 없습니다.");
+            }
+        }
+    }
+
+    @Nested
+    @DisplayName("minus()")
+    class Minus {
+
+        @Nested
+        @DisplayName("가진금액보다 감소 시킬 금액이 크다면")
+        class SmallAmount {
+
+            @Test
+            @DisplayName("0원을 가진다.")
+            void hasZero() {
+                PayAmount payAmount = new PayAmount(new BigDecimal(1000));
+                PayAmount minus = payAmount.minus(new BigDecimal(10000));
+
+                assertThat(minus.value()).isEqualByComparingTo(new BigDecimal(0));
+            }
+        }
+
+        @Nested
+        @DisplayName("가진금액이 감소시킬 금액보다 크다면")
+        class LargeAmount {
+
+            @Test
+            @DisplayName("금액이 감소한다.")
+            void minus() {
+                PayAmount payAmount = new PayAmount(new BigDecimal(10000));
+                PayAmount minus = payAmount.minus(new BigDecimal(1000));
+
+                assertThat(minus.value()).isEqualByComparingTo(new BigDecimal(9000));
             }
         }
     }
diff --git a/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/AmountDiscountCouponJpaRepository.java b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/AmountDiscountCouponJpaRepository.java
new file mode 100644
index 000000000..3a1b0c6aa
--- /dev/null
+++ b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/AmountDiscountCouponJpaRepository.java
@@ -0,0 +1,11 @@
+package com.loopers.core.infra.database.mysql.coupon;
+
+import com.loopers.core.infra.database.mysql.coupon.entity.AmountDiscountCouponEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface AmountDiscountCouponJpaRepository extends JpaRepository<AmountDiscountCouponEntity, Long> {
+
+    Optional<AmountDiscountCouponEntity> findByCouponId(Long couponId);
+}
diff --git a/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/CouponJpaRepository.java b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/CouponJpaRepository.java
new file mode 100644
index 000000000..9e378bc2b
--- /dev/null
+++ b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/CouponJpaRepository.java
@@ -0,0 +1,8 @@
+package com.loopers.core.infra.database.mysql.coupon;
+
+import com.loopers.core.infra.database.mysql.coupon.entity.CouponEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface CouponJpaRepository extends JpaRepository<CouponEntity, Long> {
+
+}
diff --git a/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/RateDiscountCouponJpaRepository.java b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/RateDiscountCouponJpaRepository.java
new file mode 100644
index 000000000..0766204f2
--- /dev/null
+++ b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/RateDiscountCouponJpaRepository.java
@@ -0,0 +1,11 @@
+package com.loopers.core.infra.database.mysql.coupon;
+
+import com.loopers.core.infra.database.mysql.coupon.entity.RateDiscountCouponEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface RateDiscountCouponJpaRepository extends JpaRepository<RateDiscountCouponEntity, Long> {
+
+    Optional<RateDiscountCouponEntity> findByCouponId(Long couponId);
+}
diff --git a/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/entity/AmountDiscountCouponEntity.java b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/entity/AmountDiscountCouponEntity.java
new file mode 100644
index 000000000..fa4f26f33
--- /dev/null
+++ b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/entity/AmountDiscountCouponEntity.java
@@ -0,0 +1,52 @@
+package com.loopers.core.infra.database.mysql.coupon.entity;
+
+import com.loopers.core.domain.order.AmountDiscountCoupon;
+import com.loopers.core.domain.order.vo.AmountDiscountCouponId;
+import com.loopers.core.domain.order.vo.CouponDiscountAmount;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.Optional;
+
+@Entity
+@Table(
+        name = "amount_discount_coupon",
+        indexes = {
+                @Index(name = "idx_amount_discount_coupon_coupon_id", columnList = "coupon_id"),
+        }
+)
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public class AmountDiscountCouponEntity {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(nullable = false)
+    private Long couponId;
+
+    @Column(nullable = false, precision = 19, scale = 2)
+    private BigDecimal amount;
+
+    public static AmountDiscountCouponEntity of(CouponEntity couponEntity, AmountDiscountCoupon amountDiscountCoupon) {
+        return new AmountDiscountCouponEntity(
+                Optional.ofNullable(amountDiscountCoupon.getId().value())
+                        .map(Long::parseLong)
+                        .orElse(null),
+                couponEntity.getId(),
+                amountDiscountCoupon.getAmount().value()
+        );
+    }
+
+    public AmountDiscountCoupon to(CouponEntity couponEntity) {
+        return AmountDiscountCoupon.mappedBy(
+                new AmountDiscountCouponId(this.id.toString()),
+                couponEntity.to(),
+                new CouponDiscountAmount(this.amount)
+        );
+    }
+}
diff --git a/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/entity/CouponEntity.java b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/entity/CouponEntity.java
new file mode 100644
index 000000000..27882c1c5
--- /dev/null
+++ b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/entity/CouponEntity.java
@@ -0,0 +1,80 @@
+package com.loopers.core.infra.database.mysql.coupon.entity;
+
+import com.loopers.core.domain.common.vo.CreatedAt;
+import com.loopers.core.domain.common.vo.DeletedAt;
+import com.loopers.core.domain.common.vo.UpdatedAt;
+import com.loopers.core.domain.order.AbstractCoupon;
+import com.loopers.core.domain.order.DefaultCoupon;
+import com.loopers.core.domain.order.type.CouponStatus;
+import com.loopers.core.domain.order.vo.CouponId;
+import com.loopers.core.domain.user.vo.UserId;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+import java.util.Optional;
+
+@Getter
+@Entity
+@Table(
+        name = "coupon",
+        indexes = {
+                @Index(name = "idx_coupon_user_id", columnList = "user_id"),
+                @Index(name = "idx_coupon_created_at", columnList = "created_at")
+        }
+)
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public class CouponEntity {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(nullable = false)
+    private Long userId;
+
+    @Column(nullable = false)
+    private String status;
+
+    @Column(nullable = false)
+    private LocalDateTime createdAt;
+
+    @Column(nullable = false)
+    private LocalDateTime updatedAt;
+
+    private LocalDateTime deletedAt;
+
+    @Version
+    private Long version;
+
+    public static CouponEntity from(AbstractCoupon coupon) {
+        return new CouponEntity(
+                Optional.ofNullable(coupon.getCouponId().value())
+                        .map(Long::parseLong)
+                        .orElse(null),
+                Long.parseLong(Objects.requireNonNull(coupon.getUserId().value())),
+                coupon.getStatus().name(),
+                coupon.getCreatedAt().value(),
+                coupon.getUpdatedAt().value(),
+                coupon.getDeletedAt().value(),
+                coupon.getVersion()
+        );
+    }
+
+    public AbstractCoupon to() {
+        return DefaultCoupon.mappedBy(
+                new CouponId(this.id.toString()),
+                new UserId(this.userId.toString()),
+                CouponStatus.valueOf(this.status),
+                new CreatedAt(this.createdAt),
+                new UpdatedAt(this.updatedAt),
+                new DeletedAt(this.deletedAt),
+                this.version
+        );
+    }
+}
diff --git a/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/entity/RateDiscountCouponEntity.java b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/entity/RateDiscountCouponEntity.java
new file mode 100644
index 000000000..9cc7fcb0a
--- /dev/null
+++ b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/entity/RateDiscountCouponEntity.java
@@ -0,0 +1,52 @@
+package com.loopers.core.infra.database.mysql.coupon.entity;
+
+import com.loopers.core.domain.order.RateDiscountCoupon;
+import com.loopers.core.domain.order.vo.CouponDiscountRate;
+import com.loopers.core.domain.order.vo.RateDiscountCouponId;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.Optional;
+
+@Entity
+@Table(
+        name = "rate_discount_coupon",
+        indexes = {
+                @Index(name = "idx_rate_discount_coupon_coupon_id", columnList = "coupon_id"),
+        }
+)
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public class RateDiscountCouponEntity {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(nullable = false)
+    private Long couponId;
+
+    @Column(nullable = false, precision = 19, scale = 2)
+    private BigDecimal rate;
+
+    public static RateDiscountCouponEntity of(CouponEntity couponEntity, RateDiscountCoupon rateDiscountCoupon) {
+        return new RateDiscountCouponEntity(
+                Optional.ofNullable(rateDiscountCoupon.getId().value())
+                        .map(Long::parseLong)
+                        .orElse(null),
+                couponEntity.getId(),
+                rateDiscountCoupon.getRate().value()
+        );
+    }
+
+    public RateDiscountCoupon to(CouponEntity couponEntity) {
+        return RateDiscountCoupon.mappedBy(
+                new RateDiscountCouponId(this.id.toString()),
+                couponEntity.to(),
+                new CouponDiscountRate(this.rate)
+        );
+    }
+}
diff --git a/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/impl/CouponRepositoryImpl.java b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/impl/CouponRepositoryImpl.java
new file mode 100644
index 000000000..8d018290c
--- /dev/null
+++ b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/coupon/impl/CouponRepositoryImpl.java
@@ -0,0 +1,65 @@
+package com.loopers.core.infra.database.mysql.coupon.impl;
+
+import com.loopers.core.domain.error.DomainErrorCode;
+import com.loopers.core.domain.error.NotFoundException;
+import com.loopers.core.domain.order.AmountDiscountCoupon;
+import com.loopers.core.domain.order.Coupon;
+import com.loopers.core.domain.order.RateDiscountCoupon;
+import com.loopers.core.domain.order.repository.CouponRepository;
+import com.loopers.core.domain.order.vo.CouponId;
+import com.loopers.core.infra.database.mysql.coupon.AmountDiscountCouponJpaRepository;
+import com.loopers.core.infra.database.mysql.coupon.CouponJpaRepository;
+import com.loopers.core.infra.database.mysql.coupon.RateDiscountCouponJpaRepository;
+import com.loopers.core.infra.database.mysql.coupon.entity.AmountDiscountCouponEntity;
+import com.loopers.core.infra.database.mysql.coupon.entity.CouponEntity;
+import com.loopers.core.infra.database.mysql.coupon.entity.RateDiscountCouponEntity;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+@RequiredArgsConstructor
+public class CouponRepositoryImpl implements CouponRepository {
+
+    private final CouponJpaRepository couponRepository;
+    private final RateDiscountCouponJpaRepository rateDiscountCouponRepository;
+    private final AmountDiscountCouponJpaRepository amountDiscountCouponRepository;
+
+    @Override
+    public Coupon save(Coupon coupon) {
+        if (coupon instanceof RateDiscountCoupon rateDiscountCoupon) {
+            CouponEntity savedCouponEntity = couponRepository.save(CouponEntity.from(rateDiscountCoupon));
+
+            return rateDiscountCouponRepository.save(RateDiscountCouponEntity.of(savedCouponEntity, rateDiscountCoupon))
+                    .to(savedCouponEntity);
+        }
+
+        if (coupon instanceof AmountDiscountCoupon amountDiscountCoupon) {
+            CouponEntity savedCouponEntity = couponRepository.save(CouponEntity.from(amountDiscountCoupon));
+
+            return amountDiscountCouponRepository.save(AmountDiscountCouponEntity.of(savedCouponEntity, amountDiscountCoupon))
+                    .to(savedCouponEntity);
+        }
+
+        throw new IllegalArgumentException(DomainErrorCode.NOT_SUPPORTED_COUPON_TYPE.getMessage());
+    }
+
+    @Override
+    public Coupon getById(CouponId couponId) {
+        CouponEntity coupon = couponRepository.findById(Long.parseLong(couponId.value()))
+                .orElseThrow(() -> NotFoundException.withName("쿠폰"));
+
+        Optional<RateDiscountCouponEntity> rateDiscountCoupon = rateDiscountCouponRepository.findByCouponId(coupon.getId());
+        if (rateDiscountCoupon.isPresent()) {
+            return rateDiscountCoupon.get().to(coupon);
+        }
+
+        Optional<AmountDiscountCouponEntity> amountDiscountCoupon = amountDiscountCouponRepository.findByCouponId(coupon.getId());
+        if (amountDiscountCoupon.isPresent()) {
+            return amountDiscountCoupon.get().to(coupon);
+        }
+
+        throw NotFoundException.withName("쿠폰");
+    }
+}
diff --git a/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/order/entity/OrderEntity.java b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/order/entity/OrderEntity.java
index 3c39f70c5..f5e5e2127 100644
--- a/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/order/entity/OrderEntity.java
+++ b/core/infra/database/mysql/order-mysql/src/main/java/com/loopers/core/infra/database/mysql/order/entity/OrderEntity.java
@@ -44,7 +44,7 @@ public class OrderEntity {
 
     public static OrderEntity from(Order order) {
         return new OrderEntity(
-                Optional.ofNullable(order.getOrderId().value())
+                Optional.ofNullable(order.getId().value())
                         .map(Long::parseLong)
                         .orElse(null),
                 Long.parseLong(Objects.requireNonNull(order.getUserId().value())),
diff --git a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/brand/entity/BrandEntity.java b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/brand/entity/BrandEntity.java
index 7c2657ce5..7371d49dc 100644
--- a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/brand/entity/BrandEntity.java
+++ b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/brand/entity/BrandEntity.java
@@ -42,7 +42,7 @@ public class BrandEntity {
 
     public static BrandEntity from(Brand brand) {
         return new BrandEntity(
-                Optional.ofNullable(brand.getBrandId().value())
+                Optional.ofNullable(brand.getId().value())
                         .map(Long::parseLong)
                         .orElse(null),
                 brand.getName().value(),
diff --git a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/ProductJpaRepository.java b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/ProductJpaRepository.java
index 9fc240b39..c009493bb 100644
--- a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/ProductJpaRepository.java
+++ b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/ProductJpaRepository.java
@@ -1,9 +1,16 @@
 package com.loopers.core.infra.database.mysql.product;
 
 import com.loopers.core.infra.database.mysql.product.entity.ProductEntity;
+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;
 
-public interface ProductJpaRepository extends JpaRepository<ProductEntity, Long>, ProductQuerydslRepository {
+import java.util.Optional;
 
+public interface ProductJpaRepository extends JpaRepository<ProductEntity, Long>, ProductQuerydslRepository {
 
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    @Query("select p from ProductEntity p where p.id = :id")
+    Optional<ProductEntity> findByIdWithLock(Long id);
 }
diff --git a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/ProductQuerydslRepositoryImpl.java b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/ProductQuerydslRepositoryImpl.java
index 0c6d73a5b..3d4680eaf 100644
--- a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/ProductQuerydslRepositoryImpl.java
+++ b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/ProductQuerydslRepositoryImpl.java
@@ -14,7 +14,9 @@
 import org.springframework.stereotype.Component;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import static com.loopers.core.infra.database.mysql.product.entity.QProductEntity.productEntity;
 
@@ -51,9 +53,12 @@ public Page<ProductListProjection> findListWithCondition(
                 .offset(pageable.getOffset())
                 .limit(pageable.getPageSize())
                 .orderBy(
-                        orderByCreatedAt(createdAtSort),
-                        orderByPrice(priceSort),
-                        orderByLikeCount(likeCountSort)
+                        Stream.of(
+                                        orderByCreatedAt(createdAtSort),
+                                        orderByPrice(priceSort),
+                                        orderByLikeCount(likeCountSort)
+                                ).filter(Objects::nonNull)
+                                .toArray(OrderSpecifier[]::new)
                 ).fetch();
 
         JPAQuery<Long> countQuery = queryFactory
diff --git a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/entity/ProductEntity.java b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/entity/ProductEntity.java
index 9f24f6a8f..cf818d15c 100644
--- a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/entity/ProductEntity.java
+++ b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/entity/ProductEntity.java
@@ -60,7 +60,7 @@ public class ProductEntity {
 
     public static ProductEntity from(Product product) {
         return new ProductEntity(
-                Optional.ofNullable(product.getProductId().value())
+                Optional.ofNullable(product.getId().value())
                         .map(Long::parseLong)
                         .orElse(null),
                 Long.parseLong(Objects.requireNonNull(product.getBrandId().value())),
diff --git a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/impl/ProductRepositoryImpl.java b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/impl/ProductRepositoryImpl.java
index fae2b37af..cddde3507 100644
--- a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/impl/ProductRepositoryImpl.java
+++ b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/product/impl/ProductRepositoryImpl.java
@@ -34,6 +34,16 @@ public Product getById(ProductId productId) {
                 .orElseThrow(() -> NotFoundException.withName("상품"));
     }
 
+    @Override
+    public Product getByIdWithLock(ProductId productId) {
+        return repository.findByIdWithLock(
+                        Objects.requireNonNull(Optional.ofNullable(productId.value())
+                                .map(Long::parseLong)
+                                .orElse(null))
+                ).map(ProductEntity::to)
+                .orElseThrow(() -> NotFoundException.withName("상품"));
+    }
+
     @Override
     public ProductListView findListWithCondition(
             BrandId brandId,
diff --git a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/ProductLikeJpaRepository.java b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/ProductLikeJpaRepository.java
index ddbf0bae2..5e00397a1 100644
--- a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/ProductLikeJpaRepository.java
+++ b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/ProductLikeJpaRepository.java
@@ -1,7 +1,10 @@
 package com.loopers.core.infra.database.mysql.productlike;
 
 import com.loopers.core.infra.database.mysql.productlike.entity.ProductLikeEntity;
+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 java.util.Optional;
 
@@ -9,5 +12,9 @@ public interface ProductLikeJpaRepository extends JpaRepository<ProductLikeEntit
 
     Optional<ProductLikeEntity> findByUserIdAndProductId(Long userId, Long productId);
 
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    @Query("select pl from ProductLikeEntity pl where pl.userId = :userId and pl.productId = :productId")
+    Optional<ProductLikeEntity> findByUserIdAndProductIdWithLock(Long userId, Long productId);
+
     void deleteByUserIdAndProductId(Long userId, Long productId);
 }
diff --git a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/ProductLikeQuerydslRepositoryImpl.java b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/ProductLikeQuerydslRepositoryImpl.java
index 6c4cc6a70..a674b75ae 100644
--- a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/ProductLikeQuerydslRepositoryImpl.java
+++ b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/ProductLikeQuerydslRepositoryImpl.java
@@ -15,7 +15,9 @@
 import org.springframework.stereotype.Component;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import static com.loopers.core.infra.database.mysql.product.entity.QProductEntity.productEntity;
 import static com.loopers.core.infra.database.mysql.productlike.entity.QProductLikeEntity.productLikeEntity;
@@ -56,9 +58,12 @@ public Page<LikeProductListProjection> findLikeProductListWithCondition(
                 .offset(pageable.getOffset())
                 .limit(pageable.getPageSize())
                 .orderBy(
-                        orderByCreatedAt(createdAtSort),
-                        orderByPrice(priceSort),
-                        orderByLikeCount(likeCountSort)
+                        Stream.of(
+                                        orderByCreatedAt(createdAtSort),
+                                        orderByPrice(priceSort),
+                                        orderByLikeCount(likeCountSort)
+                                ).filter(Objects::nonNull)
+                                .toArray(OrderSpecifier[]::new)
                 ).fetch();
 
         JPAQuery<Long> countQuery = queryFactory
diff --git a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/impl/ProductLikeRepositoryImpl.java b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/impl/ProductLikeRepositoryImpl.java
index e20972a6a..a087b9456 100644
--- a/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/impl/ProductLikeRepositoryImpl.java
+++ b/core/infra/database/mysql/product-mysql/src/main/java/com/loopers/core/infra/database/mysql/productlike/impl/ProductLikeRepositoryImpl.java
@@ -46,6 +46,14 @@ public Optional<ProductLike> findByUserIdAndProductId(UserId userId, ProductId p
         ).map(ProductLikeEntity::to);
     }
 
+    @Override
+    public Optional<ProductLike> findByUserIdAndProductIdWithLock(UserId userId, ProductId productId) {
+        return repository.findByUserIdAndProductIdWithLock(
+                Long.parseLong(userId.value()),
+                Long.parseLong(productId.value())
+        ).map(ProductLikeEntity::to);
+    }
+
     @Override
     public LikeProductListView findLikeProductsListWithCondition(
             UserId userId,
diff --git a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/UserJpaRepository.java b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/UserJpaRepository.java
index b84067f76..2e00ff2ff 100644
--- a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/UserJpaRepository.java
+++ b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/UserJpaRepository.java
@@ -1,11 +1,18 @@
 package com.loopers.core.infra.database.mysql.user;
 
 import com.loopers.core.infra.database.mysql.user.entity.UserEntity;
+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 java.util.Optional;
 
 public interface UserJpaRepository extends JpaRepository<UserEntity, Long> {
 
     Optional<UserEntity> findByIdentifier(String identifier);
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    @Query("select u from UserEntity u where u.identifier = :identifier")
+    Optional<UserEntity> findByIdentifierWithLock(String identifier);
 }
diff --git a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/UserPointJpaRepository.java b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/UserPointJpaRepository.java
index 95952b3c6..e69348120 100644
--- a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/UserPointJpaRepository.java
+++ b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/UserPointJpaRepository.java
@@ -1,11 +1,18 @@
 package com.loopers.core.infra.database.mysql.user;
 
 import com.loopers.core.infra.database.mysql.user.entity.UserPointEntity;
+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 java.util.Optional;
 
 public interface UserPointJpaRepository extends JpaRepository<UserPointEntity, Long> {
 
     Optional<UserPointEntity> findByUserId(Long userId);
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    @Query("select up from UserPointEntity up where up.userId = :userId")
+    Optional<UserPointEntity> findByUserIdWithLock(Long userId);
 }
diff --git a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/entity/UserEntity.java b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/entity/UserEntity.java
index 12cc8a8bc..ab9c4702f 100644
--- a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/entity/UserEntity.java
+++ b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/entity/UserEntity.java
@@ -50,7 +50,7 @@ public class UserEntity {
 
     public static UserEntity from(User user) {
         return new UserEntity(
-                Optional.ofNullable(user.getUserId().value())
+                Optional.ofNullable(user.getId().value())
                         .map(Long::parseLong)
                         .orElse(null),
                 user.getIdentifier().value(),
diff --git a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/entity/UserPointEntity.java b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/entity/UserPointEntity.java
index 16845b390..a5202623e 100644
--- a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/entity/UserPointEntity.java
+++ b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/entity/UserPointEntity.java
@@ -9,7 +9,6 @@
 import jakarta.persistence.*;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
-import lombok.Builder;
 import lombok.NoArgsConstructor;
 
 import java.math.BigDecimal;
@@ -26,7 +25,6 @@
 )
 @NoArgsConstructor(access = AccessLevel.PROTECTED)
 @AllArgsConstructor(access = AccessLevel.PRIVATE)
-@Builder(access = AccessLevel.PRIVATE)
 public class UserPointEntity {
 
     @Id
diff --git a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/impl/UserPointRepositoryImpl.java b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/impl/UserPointRepositoryImpl.java
index bcb90f1b1..dbdab92c8 100644
--- a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/impl/UserPointRepositoryImpl.java
+++ b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/impl/UserPointRepositoryImpl.java
@@ -36,13 +36,14 @@ public UserPoint getByUserId(UserId userId) {
     }
 
     @Override
-    public Optional<UserPoint> findByUserId(UserId userId) {
-        return repository.findByUserId(
+    public UserPoint getByUserIdWithLock(UserId userId) {
+        return repository.findByUserIdWithLock(
                         Objects.requireNonNull(Optional.ofNullable(userId.value())
                                 .map(Long::parseLong)
                                 .orElse(null)
                         )
                 )
-                .map(UserPointEntity::to);
+                .orElseThrow(() -> NotFoundException.withName("사용자 포인트"))
+                .to();
     }
 }
diff --git a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/impl/UserRepositoryImpl.java b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/impl/UserRepositoryImpl.java
index 8b0e6daba..15c872015 100644
--- a/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/impl/UserRepositoryImpl.java
+++ b/core/infra/database/mysql/user-mysql/src/main/java/com/loopers/core/infra/database/mysql/user/impl/UserRepositoryImpl.java
@@ -47,4 +47,11 @@ public User getByIdentifier(UserIdentifier identifier) {
                 .orElseThrow(() -> NotFoundException.withName("사용자"))
                 .to();
     }
+
+    @Override
+    public User getByIdentifierWithLock(UserIdentifier identifier) {
+        return repository.findByIdentifierWithLock(identifier.value())
+                .orElseThrow(() -> NotFoundException.withName("사용자"))
+                .to();
+    }
 }
diff --git a/core/service/order-service/src/main/java/com/loopers/core/service/order/OrderQueryService.java b/core/service/order-service/src/main/java/com/loopers/core/service/order/OrderQueryService.java
index 7c281fff5..5788edfb3 100644
--- a/core/service/order-service/src/main/java/com/loopers/core/service/order/OrderQueryService.java
+++ b/core/service/order-service/src/main/java/com/loopers/core/service/order/OrderQueryService.java
@@ -30,7 +30,7 @@ public OrderListView getOrderListWithCondition(GetOrderListQuery query) {
         User user = userRepository.getByIdentifier(new UserIdentifier(query.getUserIdentifier()));
 
         return orderRepository.findListWithCondition(
-                user.getUserId(),
+                user.getId(),
                 OrderSort.from(query.getCreatedAtSort()),
                 query.getPageNo(),
                 query.getPageSize()
@@ -39,7 +39,7 @@ public OrderListView getOrderListWithCondition(GetOrderListQuery query) {
 
     public OrderDetail getOrderDetail(GetOrderDetailQuery query) {
         Order order = orderRepository.getById(new OrderId(query.getOrderId()));
-        List<OrderItem> orderItems = orderItemRepository.findAllByOrderId(order.getOrderId());
+        List<OrderItem> orderItems = orderItemRepository.findAllByOrderId(order.getId());
 
         return new OrderDetail(order, orderItems);
     }
diff --git a/core/service/order-service/src/main/java/com/loopers/core/service/order/OrderService.java b/core/service/order-service/src/main/java/com/loopers/core/service/order/OrderService.java
index 6b4e8d77e..5767d05d1 100644
--- a/core/service/order-service/src/main/java/com/loopers/core/service/order/OrderService.java
+++ b/core/service/order-service/src/main/java/com/loopers/core/service/order/OrderService.java
@@ -3,6 +3,7 @@
 import com.loopers.core.domain.order.Order;
 import com.loopers.core.domain.order.OrderItem;
 import com.loopers.core.domain.order.repository.OrderRepository;
+import com.loopers.core.domain.order.vo.CouponId;
 import com.loopers.core.domain.order.vo.Quantity;
 import com.loopers.core.domain.payment.vo.PayAmount;
 import com.loopers.core.domain.product.vo.ProductId;
@@ -29,19 +30,20 @@ public class OrderService {
 
     @Transactional
     public Order order(OrderProductsCommand command) {
-        User user = userRepository.getByIdentifier(new UserIdentifier(command.getUserIdentifier()));
-        Order savedOrder = orderRepository.save(Order.create(user.getUserId()));
-        List<OrderItem> orderItems = command.getOrderItems().stream()
+        User user = userRepository.getByIdentifier(new UserIdentifier(command.userIdentifier()));
+        Order savedOrder = orderRepository.save(Order.create(user.getId()));
+        CouponId couponId = new CouponId(command.couponId());
+        List<OrderItem> orderItems = command.orderItems().stream()
                 .map(productCommand -> OrderItem.create(
-                                savedOrder.getOrderId(),
-                                new ProductId(productCommand.getProductId()),
-                                new Quantity(productCommand.getQuantity())
+                                savedOrder.getId(),
+                                new ProductId(productCommand.productId()),
+                                new Quantity(productCommand.quantity())
                         )
                 )
                 .toList();
 
         PayAmount payAmount = orderLineAggregator.aggregate(orderItems);
-        orderCashier.checkout(user, savedOrder, payAmount);
+        orderCashier.checkout(user, savedOrder, payAmount, couponId);
 
         return savedOrder;
     }
diff --git a/core/service/order-service/src/main/java/com/loopers/core/service/order/command/OrderProductsCommand.java b/core/service/order-service/src/main/java/com/loopers/core/service/order/command/OrderProductsCommand.java
index efd641265..e02fade2d 100644
--- a/core/service/order-service/src/main/java/com/loopers/core/service/order/command/OrderProductsCommand.java
+++ b/core/service/order-service/src/main/java/com/loopers/core/service/order/command/OrderProductsCommand.java
@@ -1,31 +1,10 @@
 package com.loopers.core.service.order.command;
 
-import lombok.Getter;
-
 import java.util.List;
 
-@Getter
-public class OrderProductsCommand {
-
-    private final String userIdentifier;
-
-    private final List<OrderItem> orderItems;
-
-    public OrderProductsCommand(String userIdentifier, List<OrderItem> orderItems) {
-        this.userIdentifier = userIdentifier;
-        this.orderItems = orderItems;
-    }
-
-    @Getter
-    public static class OrderItem {
-
-        private final String productId;
+public record OrderProductsCommand(String userIdentifier, List<OrderItem> orderItems, String couponId) {
 
-        private final Long quantity;
+    public record OrderItem(String productId, Long quantity) {
 
-        public OrderItem(String productId, Long quantity) {
-            this.productId = productId;
-            this.quantity = quantity;
-        }
     }
 }
diff --git a/core/service/order-service/src/main/java/com/loopers/core/service/order/component/CouponDiscountStrategy.java b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/CouponDiscountStrategy.java
new file mode 100644
index 000000000..25ecd0365
--- /dev/null
+++ b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/CouponDiscountStrategy.java
@@ -0,0 +1,27 @@
+package com.loopers.core.service.order.component;
+
+import com.loopers.core.domain.order.Coupon;
+import com.loopers.core.domain.order.repository.CouponRepository;
+import com.loopers.core.domain.order.vo.CouponId;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@RequiredArgsConstructor
+public class CouponDiscountStrategy implements PayAmountDiscountStrategy {
+
+    private final CouponRepository couponRepository;
+
+    @Override
+    @Transactional
+    public PayAmount discount(PayAmount payAmount, CouponId couponId) {
+        Coupon coupon = couponRepository.getById(couponId);
+        PayAmount discountedAmount = coupon.discount(payAmount);
+        coupon.use();
+        couponRepository.save(coupon);
+
+        return discountedAmount;
+    }
+}
diff --git a/core/service/order-service/src/main/java/com/loopers/core/service/order/component/NoneDiscountStrategy.java b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/NoneDiscountStrategy.java
new file mode 100644
index 000000000..d6bfbf4b9
--- /dev/null
+++ b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/NoneDiscountStrategy.java
@@ -0,0 +1,14 @@
+package com.loopers.core.service.order.component;
+
+import com.loopers.core.domain.order.vo.CouponId;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import org.springframework.stereotype.Component;
+
+@Component
+public class NoneDiscountStrategy implements PayAmountDiscountStrategy {
+
+    @Override
+    public PayAmount discount(PayAmount payAmount, CouponId couponId) {
+        return payAmount;
+    }
+}
diff --git a/core/service/order-service/src/main/java/com/loopers/core/service/order/component/OrderCashier.java b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/OrderCashier.java
index fa4d450d1..4b1f6d768 100644
--- a/core/service/order-service/src/main/java/com/loopers/core/service/order/component/OrderCashier.java
+++ b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/OrderCashier.java
@@ -1,6 +1,7 @@
 package com.loopers.core.service.order.component;
 
 import com.loopers.core.domain.order.Order;
+import com.loopers.core.domain.order.vo.CouponId;
 import com.loopers.core.domain.payment.Payment;
 import com.loopers.core.domain.payment.repository.PaymentRepository;
 import com.loopers.core.domain.payment.vo.PayAmount;
@@ -17,15 +18,16 @@ public class OrderCashier {
 
     private final UserPointRepository userPointRepository;
     private final PaymentRepository paymentRepository;
+    private final PayAmountDiscountStrategySelector discountStrategySelector;
 
     @Transactional
-    public Payment checkout(User user, Order order, PayAmount payAmount) {
-        UserPoint userPoint = userPointRepository.getByUserId(user.getUserId());
-        userPointRepository.save(userPoint.pay(payAmount));
+    public Payment checkout(User user, Order order, PayAmount payAmount, CouponId couponId) {
+        UserPoint userPoint = userPointRepository.getByUserIdWithLock(user.getId());
+        PayAmountDiscountStrategy discountStrategy = discountStrategySelector.select(couponId);
+        PayAmount discountedPayAmount = discountStrategy.discount(payAmount, couponId);
+        userPointRepository.save(userPoint.pay(discountedPayAmount));
+        Payment payment = Payment.create(order.getId(), user.getId(), discountedPayAmount);
 
-        Payment payment = Payment.create(order.getOrderId(), user.getUserId(), payAmount);
-        paymentRepository.save(payment);
-
-        return payment;
+        return paymentRepository.save(payment);
     }
 }
diff --git a/core/service/order-service/src/main/java/com/loopers/core/service/order/component/OrderLineAllocator.java b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/OrderLineAllocator.java
index cac50470b..560591d2e 100644
--- a/core/service/order-service/src/main/java/com/loopers/core/service/order/component/OrderLineAllocator.java
+++ b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/OrderLineAllocator.java
@@ -19,7 +19,7 @@ public class OrderLineAllocator {
 
     @Transactional
     public BigDecimal allocate(OrderItem orderItem) {
-        Product product = productRepository.getById(orderItem.getProductId());
+        Product product = productRepository.getByIdWithLock(orderItem.getProductId());
         productRepository.save(product.decreaseStock(orderItem.getQuantity()));
         orderItemRepository.save(orderItem);
 
diff --git a/core/service/order-service/src/main/java/com/loopers/core/service/order/component/PayAmountDiscountStrategy.java b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/PayAmountDiscountStrategy.java
new file mode 100644
index 000000000..e0b850531
--- /dev/null
+++ b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/PayAmountDiscountStrategy.java
@@ -0,0 +1,9 @@
+package com.loopers.core.service.order.component;
+
+import com.loopers.core.domain.order.vo.CouponId;
+import com.loopers.core.domain.payment.vo.PayAmount;
+
+public interface PayAmountDiscountStrategy {
+
+    PayAmount discount(PayAmount payAmount, CouponId couponId);
+}
diff --git a/core/service/order-service/src/main/java/com/loopers/core/service/order/component/PayAmountDiscountStrategySelector.java b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/PayAmountDiscountStrategySelector.java
new file mode 100644
index 000000000..ac7949db6
--- /dev/null
+++ b/core/service/order-service/src/main/java/com/loopers/core/service/order/component/PayAmountDiscountStrategySelector.java
@@ -0,0 +1,21 @@
+package com.loopers.core.service.order.component;
+
+import com.loopers.core.domain.order.vo.CouponId;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class PayAmountDiscountStrategySelector {
+
+    private final NoneDiscountStrategy noneDiscountStrategy;
+    private final CouponDiscountStrategy couponDiscountStrategy;
+
+    public PayAmountDiscountStrategy select(CouponId couponId) {
+        if (couponId.nonEmpty()) {
+            return couponDiscountStrategy;
+        }
+
+        return noneDiscountStrategy;
+    }
+}
diff --git a/core/service/order-service/src/test/java/com/loopers/core/service/ConcurrencyTestUtil.java b/core/service/order-service/src/test/java/com/loopers/core/service/ConcurrencyTestUtil.java
new file mode 100644
index 000000000..716207078
--- /dev/null
+++ b/core/service/order-service/src/test/java/com/loopers/core/service/ConcurrencyTestUtil.java
@@ -0,0 +1,70 @@
+package com.loopers.core.service;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Slf4j
+public final class ConcurrencyTestUtil {
+
+    private ConcurrencyTestUtil() {
+        throw new UnsupportedOperationException("이 클래스의 인스턴스는 생성될 수 없습니다.");
+    }
+
+    public static <T> List<T> executeInParallel(
+            int threadCount,
+            Function<Integer, T> task) throws InterruptedException {
+
+        CountDownLatch latch = new CountDownLatch(threadCount);
+        CopyOnWriteArrayList<T> results = new CopyOnWriteArrayList<>();
+
+        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
+            for (int i = 0; i < threadCount; i++) {
+                final int index = i;
+                executor.submit(() -> {
+                    try {
+                        T result = task.apply(index);
+                        results.add(result);
+                    } catch (Exception e) {
+                        System.out.println(e.getMessage() + e.getClass().getName());
+                    } finally {
+                        latch.countDown();
+                    }
+                });
+            }
+        }
+
+        boolean completed = latch.await(30, TimeUnit.SECONDS);
+        assertThat(completed).isTrue();
+
+        return results;
+    }
+
+    public static void executeInParallelWithoutResult(
+            int threadCount,
+            Consumer<Integer> task) throws InterruptedException {
+
+        CountDownLatch latch = new CountDownLatch(threadCount);
+
+        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
+            for (int i = 0; i < threadCount; i++) {
+                final int index = i;
+                executor.submit(() -> {
+                    try {
+                        task.accept(index);
+                    } finally {
+                        latch.countDown();
+                    }
+                });
+            }
+        }
+
+        boolean completed = latch.await(30, TimeUnit.SECONDS);
+        assertThat(completed).isTrue();
+    }
+}
diff --git a/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderCashierIntegrationTest.java b/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderCashierIntegrationTest.java
index d86844275..e538a0810 100644
--- a/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderCashierIntegrationTest.java
+++ b/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderCashierIntegrationTest.java
@@ -1,24 +1,21 @@
 package com.loopers.core.service.order;
 
-import com.loopers.core.domain.common.vo.CreatedAt;
-import com.loopers.core.domain.common.vo.UpdatedAt;
 import com.loopers.core.domain.order.Order;
-import com.loopers.core.domain.order.OrderItem;
 import com.loopers.core.domain.order.repository.OrderRepository;
-import com.loopers.core.domain.order.vo.Quantity;
+import com.loopers.core.domain.order.vo.CouponId;
+import com.loopers.core.domain.order.vo.OrderId;
 import com.loopers.core.domain.payment.Payment;
 import com.loopers.core.domain.payment.repository.PaymentRepository;
 import com.loopers.core.domain.payment.vo.PayAmount;
-import com.loopers.core.domain.product.vo.ProductId;
 import com.loopers.core.domain.user.User;
 import com.loopers.core.domain.user.UserPoint;
 import com.loopers.core.domain.user.repository.UserPointRepository;
 import com.loopers.core.domain.user.repository.UserRepository;
-import com.loopers.core.domain.user.type.UserGender;
 import com.loopers.core.domain.user.vo.*;
+import com.loopers.core.service.ConcurrencyTestUtil;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.order.component.OrderCashier;
-import org.assertj.core.api.SoftAssertions;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -26,10 +23,13 @@
 import org.springframework.beans.factory.annotation.Autowired;
 
 import java.math.BigDecimal;
-import java.time.LocalDate;
 import java.util.List;
 
-@DisplayName("OrderCashier 통합 테스트")
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.instancio.Select.field;
+
+@DisplayName("주문 결제 처리")
 class OrderCashierIntegrationTest extends IntegrationTest {
 
     @Autowired
@@ -47,91 +47,165 @@ class OrderCashierIntegrationTest extends IntegrationTest {
     @Autowired
     private PaymentRepository paymentRepository;
 
-    private User user;
-    private Order order;
-    private PayAmount payAmount;
-
-    @BeforeEach
-    void setUp() {
-        // 사용자 생성 및 저장
-        user = User.create(
-                new UserIdentifier("testuser"),
-                new UserEmail("test@example.com"),
-                new UserBirthDay(LocalDate.of(2000, 1, 1)),
-                UserGender.MALE
-        );
-        user = userRepository.save(user);
-
-        // 사용자 포인트 생성 및 저장 (50,000 포인트)
-        UserPoint userPoint = UserPoint.mappedBy(
-                UserPointId.empty(),
-                user.getUserId(),
-                new UserPointBalance(new BigDecimal("50000")),
-                CreatedAt.now(),
-                UpdatedAt.now()
-        );
-        userPointRepository.save(userPoint);
-
-        // 주문 생성 및 저장
-        order = Order.create(user.getUserId());
-        order = orderRepository.save(order);
-
-        // 결제 금액
-        payAmount = new PayAmount(new BigDecimal("20000"));
-    }
-
     @Nested
-    @DisplayName("checkout 메서드")
-    class CheckoutMethod {
-
-        @Test
-        @DisplayName("주문을 생성하고 포인트를 차감하고 결제 정보를 저장한다")
-        void checkoutSuccess() {
-            // when
-            Payment result = orderCashier.checkout(user, order, payAmount);
-
-            // then - 결제 정보 확인
-            SoftAssertions.assertSoftly(softly -> {
-                softly.assertThat(result).isNotNull();
-                softly.assertThat(result.getOrderId()).isEqualTo(order.getOrderId());
-                softly.assertThat(result.getUserId()).isEqualTo(user.getUserId());
-                softly.assertThat(result.getAmount().value()).isEqualByComparingTo(new BigDecimal("20000"));
-            });
-
-            // 포인트 차감 확인
-            UserPoint deductedUserPoint = userPointRepository.getByUserId(user.getUserId());
-            SoftAssertions.assertSoftly(softly -> {
-                softly.assertThat(deductedUserPoint.getBalance().value())
-                        .isEqualByComparingTo(new BigDecimal("30000")); // 50000 - 20000
-            });
-        }
-
-        @Test
-        @DisplayName("포인트가 부족하면 예외를 발생시킨다")
-        void throwExceptionWhenInsufficientPoint() {
-            // given
-            PayAmount largePayAmount = new PayAmount(new BigDecimal("100000"));
-
-            // when & then
-            org.assertj.core.api.Assertions.assertThatThrownBy(() -> orderCashier.checkout(user, order, largePayAmount))
-                    .isInstanceOf(IllegalArgumentException.class)
-                    .hasMessage("사용자의 포인트 잔액이 충분하지 않습니다.");
+    @DisplayName("결제 시")
+    class 결제_시 {
+
+        @Nested
+        @DisplayName("포인트가 충분한 경우")
+        class 포인트가_충분한_경우 {
+
+            private User user;
+            private Order order;
+            private PayAmount payAmount;
+
+            @BeforeEach
+            void setUp() {
+                // 사용자 생성 및 저장
+                user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("testuser"))
+                                .set(field(User::getEmail), new UserEmail("test@example.com"))
+                                .create()
+                );
+
+                // 사용자 포인트 생성 및 저장 (50,000 포인트)
+                userPointRepository.save(
+                        Instancio.of(UserPoint.class)
+                                .set(field("id"), UserPointId.empty())
+                                .set(field("userId"), user.getId())
+                                .set(field("balance"), new UserPointBalance(new BigDecimal("50000")))
+                                .create()
+                );
+
+                // 주문 생성 및 저장
+                order = orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), user.getId())
+                                .create()
+                );
+
+                // 결제 금액 (20,000 - 포인트는 50,000으로 충분함)
+                payAmount = new PayAmount(new BigDecimal("20000"));
+            }
+
+            @Test
+            @DisplayName("주문을 생성하고 포인트를 차감하고 결제 정보를 저장한다")
+            void 주문을_생성하고_포인트를_차감하고_결제_정보를_저장한다() {
+                // when
+                Payment result = orderCashier.checkout(user, order, payAmount, CouponId.empty());
+
+                // then - 결제 정보 확인
+                assertSoftly(softly -> {
+                    softly.assertThat(result)
+                            .as("결제 정보")
+                            .isNotNull();
+                    softly.assertThat(result.getOrderId())
+                            .as("주문 ID 일치")
+                            .isEqualTo(order.getId());
+                    softly.assertThat(result.getUserId())
+                            .as("사용자 ID 일치")
+                            .isEqualTo(user.getId());
+                    softly.assertThat(result.getAmount().value())
+                            .as("결제 금액")
+                            .isEqualByComparingTo(new BigDecimal("20000"));
+                });
+
+                // 포인트 차감 확인
+                UserPoint deductedUserPoint = userPointRepository.getByUserId(user.getId());
+                assertSoftly(softly -> {
+                    softly.assertThat(deductedUserPoint.getBalance().value())
+                            .as("차감 후 포인트 잔액")
+                            .isEqualByComparingTo(new BigDecimal("30000")); // 50000 - 20000
+                });
+            }
+
+            @Test
+            @DisplayName("정확히 남은 포인트만큼 결제하면 포인트가 0이 된다")
+            void 정확히_남은_포인트만큼_결제하면_포인트가_0이_된다() {
+                // given
+                PayAmount exactPayAmount = new PayAmount(new BigDecimal("50000"));
+
+                // when
+                orderCashier.checkout(user, order, exactPayAmount, CouponId.empty());
+
+                // then
+                UserPoint userPoint = userPointRepository.getByUserId(user.getId());
+                assertSoftly(softly -> {
+                    softly.assertThat(userPoint.getBalance().value())
+                            .as("차감 후 포인트 잔액")
+                            .isZero();
+                });
+            }
+
+            @Test
+            @DisplayName("동시에 여러 주문요청을 결제한다면 결제한 금액만큼 감소된다.")
+            void 동시에_여러_주문요청을_결제한다면_결제한_금액만큼_감소된다() throws InterruptedException {
+                int requestCount = 100;
+                PayAmount payAmountPerUser = new PayAmount(new BigDecimal("500"));
+                List<Payment> results = ConcurrencyTestUtil.executeInParallel(
+                        requestCount,
+                        index -> orderCashier.checkout(user, order, payAmountPerUser, CouponId.empty())
+                );
+
+                UserPoint actualUserPoint = userPointRepository.getByUserId(user.getId());
+                assertSoftly(softly -> {
+                    softly.assertThat(results).as("동시 요청 결과 수").hasSize(requestCount);
+                    softly.assertThat(actualUserPoint.getBalance().value()).isEqualByComparingTo(new BigDecimal(0));
+                });
+            }
         }
 
-        @Test
-        @DisplayName("정확히 남은 포인트만큼 결제하면 포인트가 0이 된다")
-        void checkoutWithExactPoint() {
-            // given
-            PayAmount exactPayAmount = new PayAmount(new BigDecimal("50000"));
-
-            // when
-            orderCashier.checkout(user, order, exactPayAmount);
-
-            // then
-            UserPoint userPoint = userPointRepository.getByUserId(user.getUserId());
-            SoftAssertions.assertSoftly(softly -> {
-                softly.assertThat(userPoint.getBalance().value()).isZero();
-            });
+        @Nested
+        @DisplayName("포인트가 부족한 경우")
+        class 포인트가_부족한_경우 {
+
+            private User user;
+            private Order order;
+
+            @BeforeEach
+            void setUp() {
+                // 사용자 생성 및 저장
+                user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("testuser"))
+                                .set(field(User::getEmail), new UserEmail("test@example.com"))
+                                .create()
+                );
+
+                // 사용자 포인트 생성 및 저장 (50,000 포인트)
+                userPointRepository.save(
+                        Instancio.of(UserPoint.class)
+                                .set(field("id"), UserPointId.empty())
+                                .set(field("userId"), user.getId())
+                                .set(field("balance"), new UserPointBalance(new BigDecimal("50000")))
+                                .create()
+                );
+
+                // 주문 생성 및 저장
+                order = orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), user.getId())
+                                .create()
+                );
+            }
+
+            @Test
+            @DisplayName("예외를 발생시킨다")
+            void 예외를_발생시킨다() {
+                // given
+                PayAmount largePayAmount = new PayAmount(new BigDecimal("100000"));
+
+                // when & then
+                assertThatThrownBy(
+                        () -> orderCashier.checkout(user, order, largePayAmount, CouponId.empty())
+                ).isInstanceOf(IllegalArgumentException.class)
+                        .hasMessage("사용자의 포인트 잔액이 충분하지 않습니다.");
+            }
         }
     }
 }
diff --git a/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderLineAllocatorIntegrationTest.java b/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderLineAllocatorIntegrationTest.java
index b8a6113f0..96b735449 100644
--- a/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderLineAllocatorIntegrationTest.java
+++ b/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderLineAllocatorIntegrationTest.java
@@ -1,18 +1,20 @@
 package com.loopers.core.service.order;
 
 import com.loopers.core.domain.brand.vo.BrandId;
-import com.loopers.core.domain.common.vo.CreatedAt;
-import com.loopers.core.domain.common.vo.DeletedAt;
-import com.loopers.core.domain.common.vo.UpdatedAt;
 import com.loopers.core.domain.order.OrderItem;
 import com.loopers.core.domain.order.vo.OrderId;
+import com.loopers.core.domain.order.vo.OrderItemId;
 import com.loopers.core.domain.order.vo.Quantity;
 import com.loopers.core.domain.product.Product;
 import com.loopers.core.domain.product.repository.ProductRepository;
-import com.loopers.core.domain.product.vo.*;
+import com.loopers.core.domain.product.vo.ProductId;
+import com.loopers.core.domain.product.vo.ProductName;
+import com.loopers.core.domain.product.vo.ProductPrice;
+import com.loopers.core.domain.product.vo.ProductStock;
+import com.loopers.core.service.ConcurrencyTestUtil;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.order.component.OrderLineAllocator;
-import org.assertj.core.api.SoftAssertions;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -20,8 +22,13 @@
 import org.springframework.beans.factory.annotation.Autowired;
 
 import java.math.BigDecimal;
+import java.util.List;
 
-@DisplayName("OrderLineAllocator 통합 테스트")
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.instancio.Select.field;
+
+@DisplayName("주문 라인 할당")
 class OrderLineAllocatorIntegrationTest extends IntegrationTest {
 
     @Autowired
@@ -30,111 +37,172 @@ class OrderLineAllocatorIntegrationTest extends IntegrationTest {
     @Autowired
     private ProductRepository productRepository;
 
-    private Product product;
-    private OrderItem orderItem;
-
-    @BeforeEach
-    void setUp() {
-        // 상품 생성 및 저장 (가격: 10,000, 재고: 100개)
-        product = Product.mappedBy(
-                ProductId.empty(),
-                new BrandId("1"),
-                new ProductName("테스트상품"),
-                new ProductPrice(new BigDecimal("10000")),
-                new ProductStock(100L),
-                ProductLikeCount.init(),
-                CreatedAt.now(),
-                UpdatedAt.now(),
-                DeletedAt.empty()
-        );
-        product = productRepository.save(product);
-
-        // 주문 상품 생성 (수량: 5개)
-        orderItem = OrderItem.create(
-            new OrderId("1"),
-            product.getProductId(),
-            new Quantity(5L)
-        );
-    }
-
     @Nested
-    @DisplayName("allocate 메서드")
-    class AllocateMethod {
-
-        @Test
-        @DisplayName("상품의 재고를 차감하고 총 가격을 반환한다")
-        void allocateSuccess() {
-            // when
-            BigDecimal totalPrice = orderLineAllocator.allocate(orderItem);
-
-            // then - 반환된 가격 검증 (10,000 * 5 = 50,000)
-            SoftAssertions.assertSoftly(softly -> {
-                softly.assertThat(totalPrice).isEqualByComparingTo(new BigDecimal("50000"));
-            });
-
-            // 상품 재고 차감 확인
-            Product allocatedProduct = productRepository.getById(product.getProductId());
-            SoftAssertions.assertSoftly(softly -> {
-                softly.assertThat(allocatedProduct.getStock().value()).isEqualTo(95L); // 100 - 5
-            });
-        }
-
-        @Test
-        @DisplayName("여러 번 할당하면 재고가 누적으로 차감된다")
-        void allocateMultipleTimes() {
-            // when - 첫 번째 할당 (5개)
-            orderLineAllocator.allocate(orderItem);
-
-            // 두 번째 할당 (5개)
-            orderLineAllocator.allocate(orderItem);
-
-            // then - 재고 누적 차감 확인
-            Product allocatedProduct = productRepository.getById(product.getProductId());
-            SoftAssertions.assertSoftly(softly -> {
-                softly.assertThat(allocatedProduct.getStock().value()).isEqualTo(90L); // 100 - 5 - 5
-            });
-        }
-
-        @Test
-        @DisplayName("재고 부족 시 예외를 발생시킨다")
-        void throwExceptionWhenInsufficientStock() {
-            // given - 재고보다 많은 수량 주문
-            OrderItem largeOrderItem = OrderItem.create(
-                    new OrderId("1"),
-                    product.getProductId(),
-                    new Quantity(150L)
-            );
-
-            // when & then
-            org.assertj.core.api.Assertions.assertThatThrownBy(
-                            () -> orderLineAllocator.allocate(largeOrderItem)
-                    ).isInstanceOf(IllegalArgumentException.class)
-                    .hasMessage("상품의 재고가 부족합니다.");
+    @DisplayName("상품 할당 시")
+    class 상품_할당 {
+
+        @Nested
+        @DisplayName("충분한 재고가 있는 경우")
+        class 충분한_재고가_있는_경우 {
+
+            private Product product;
+            private OrderItem orderItem;
+
+            @BeforeEach
+            void setUp() {
+                // 상품 생성 및 저장 (가격: 10,000, 재고: 100개)
+                product = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), new BrandId("1"))
+                                .set(field(Product::getName), new ProductName("테스트상품"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal("10000")))
+                                .set(field(Product::getStock), new ProductStock(100L))
+                                .create()
+                );
+
+                // 주문 상품 생성 (수량: 5개)
+                orderItem = Instancio.of(OrderItem.class)
+                        .set(field(OrderItem::getId), OrderItemId.empty())
+                        .set(field(OrderItem::getOrderId), new OrderId("1"))
+                        .set(field(OrderItem::getProductId), product.getId())
+                        .set(field(OrderItem::getQuantity), new Quantity(5L))
+                        .create();
+            }
+
+            @Test
+            @DisplayName("상품의 재고를 차감하고 총 가격을 반환한다")
+            void 상품의_재고를_차감하고_총_가격을_반환한다() {
+                // when
+                BigDecimal totalPrice = orderLineAllocator.allocate(orderItem);
+
+                // then - 반환된 가격 검증 (10,000 * 5 = 50,000)
+                assertSoftly(softly -> {
+                    softly.assertThat(totalPrice)
+                            .as("할당된 총 가격")
+                            .isEqualByComparingTo(new BigDecimal("50000"));
+                });
+
+                // 상품 재고 차감 확인
+                Product allocatedProduct = productRepository.getById(product.getId());
+                assertSoftly(softly -> {
+                    softly.assertThat(allocatedProduct.getStock().value())
+                            .as("차감 후 남은 재고")
+                            .isEqualTo(95L); // 100 - 5
+                });
+            }
+
+            @Test
+            @DisplayName("여러 번 할당하면 재고가 누적으로 차감된다")
+            void 여러_번_할당하면_재고가_누적으로_차감된다() {
+                // when - 첫 번째 할당 (5개)
+                orderLineAllocator.allocate(orderItem);
+
+                // 두 번째 할당 (5개)
+                orderLineAllocator.allocate(orderItem);
+
+                // then - 재고 누적 차감 확인
+                Product allocatedProduct = productRepository.getById(product.getId());
+                assertSoftly(softly -> {
+                    softly.assertThat(allocatedProduct.getStock().value())
+                            .as("누적 차감 후 남은 재고")
+                            .isEqualTo(90L); // 100 - 5 - 5
+                });
+            }
+
+            @Test
+            @DisplayName("정확히 남은 재고만큼 할당하면 재고가 0이 된다")
+            void 정확히_남은_재고만큼_할당하면_재고가_0이_된다() {
+                // given - 정확히 현재 재고만큼 주문
+                OrderItem exactOrderItem = Instancio.of(OrderItem.class)
+                        .set(field(OrderItem::getId), OrderItemId.empty())
+                        .set(field(OrderItem::getOrderId), new OrderId("1"))
+                        .set(field(OrderItem::getProductId), product.getId())
+                        .set(field(OrderItem::getQuantity), new Quantity(100L))
+                        .create();
+
+                // when
+                BigDecimal totalPrice = orderLineAllocator.allocate(exactOrderItem);
+
+                // then - 총 가격 검증 (10,000 * 100 = 1,000,000)
+                assertSoftly(softly -> {
+                    softly.assertThat(totalPrice)
+                            .as("할당된 총 가격")
+                            .isEqualByComparingTo(new BigDecimal("1000000"));
+                });
+
+                // 재고가 0이 되었는지 확인
+                Product allocatedProduct = productRepository.getById(product.getId());
+                assertSoftly(softly -> {
+                    softly.assertThat(allocatedProduct.getStock().value())
+                            .as("차감 후 남은 재고")
+                            .isZero();
+                });
+            }
+
+            @Test
+            @DisplayName("동시에 여러 사용자가 주문하면 주문한양만큼 감소한다.")
+            void 동시에_여러_사용자가_주문하면_주문한양만큼_감소한다() throws InterruptedException {
+                int requestCount = 100;
+                OrderItem orderItem = Instancio.of(OrderItem.class)
+                        .set(field(OrderItem::getId), OrderItemId.empty())
+                        .set(field(OrderItem::getOrderId), new OrderId("1"))
+                        .set(field(OrderItem::getProductId), product.getId())
+                        .set(field(OrderItem::getQuantity), new Quantity(1L))
+                        .create();
+
+                List<BigDecimal> results = ConcurrencyTestUtil.executeInParallel(
+                        100,
+                        index -> orderLineAllocator.allocate(orderItem)
+                );
+
+                Product actualProduct = productRepository.getById(product.getId());
+
+
+                assertSoftly(softly -> {
+                    softly.assertThat(results).as("동시 요청 결과 수").hasSize(requestCount);
+                    softly.assertThat(actualProduct.getStock().value()).as("최종 재고 수").isEqualTo(0L);
+                });
+            }
         }
 
-        @Test
-        @DisplayName("정확히 남은 재고만큼 할당하면 재고가 0이 된다")
-        void allocateWithExactStock() {
-            // given - 정확히 현재 재고만큼 주문
-            OrderItem exactOrderItem = OrderItem.create(
-                    new OrderId("1"),
-                    product.getProductId(),
-                    new Quantity(100L)
-            );
-
-            // when
-            BigDecimal totalPrice = orderLineAllocator.allocate(exactOrderItem);
-
-            // then - 총 가격 검증 (10,000 * 100 = 1,000,000)
-            SoftAssertions.assertSoftly(softly -> {
-                softly.assertThat(totalPrice).isEqualByComparingTo(new BigDecimal("1000000"));
-            });
-
-            // 재고가 0이 되었는지 확인
-            Product allocatedProduct = productRepository.getById(product.getProductId());
-            SoftAssertions.assertSoftly(softly -> {
-                softly.assertThat(allocatedProduct.getStock().value()).isZero();
-            });
+        @Nested
+        @DisplayName("재고가 부족한 경우")
+        class 재고가_부족한_경우 {
+
+            private Product product;
+
+            @BeforeEach
+            void setUp() {
+                // 상품 생성 및 저장 (가격: 10,000, 재고: 100개)
+                product = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), new BrandId("1"))
+                                .set(field(Product::getName), new ProductName("테스트상품"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal("10000")))
+                                .set(field(Product::getStock), new ProductStock(100L))
+                                .create()
+                );
+            }
+
+            @Test
+            @DisplayName("예외를 발생시킨다")
+            void 예외를_발생시킨다() {
+                // given - 재고보다 많은 수량 주문
+                OrderItem largeOrderItem = Instancio.of(OrderItem.class)
+                        .set(field(OrderItem::getId), OrderItemId.empty())
+                        .set(field(OrderItem::getOrderId), new OrderId("1"))
+                        .set(field(OrderItem::getProductId), product.getId())
+                        .set(field(OrderItem::getQuantity), new Quantity(150L))
+                        .create();
+
+                // when & then
+                assertThatThrownBy(
+                        () -> orderLineAllocator.allocate(largeOrderItem)
+                ).isInstanceOf(IllegalArgumentException.class)
+                        .hasMessage("상품의 재고가 부족합니다.");
+            }
         }
     }
 }
diff --git a/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderQueryServiceIntegrationTest.java b/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderQueryServiceIntegrationTest.java
index b6380efa2..1805e8bc1 100644
--- a/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderQueryServiceIntegrationTest.java
+++ b/core/service/order-service/src/test/java/com/loopers/core/service/order/OrderQueryServiceIntegrationTest.java
@@ -5,6 +5,9 @@
 import com.loopers.core.domain.brand.vo.BrandDescription;
 import com.loopers.core.domain.brand.vo.BrandId;
 import com.loopers.core.domain.brand.vo.BrandName;
+import com.loopers.core.domain.common.vo.CreatedAt;
+import com.loopers.core.domain.common.vo.DeletedAt;
+import com.loopers.core.domain.common.vo.UpdatedAt;
 import com.loopers.core.domain.error.NotFoundException;
 import com.loopers.core.domain.order.Order;
 import com.loopers.core.domain.order.OrderDetail;
@@ -13,21 +16,23 @@
 import com.loopers.core.domain.order.repository.OrderItemRepository;
 import com.loopers.core.domain.order.repository.OrderRepository;
 import com.loopers.core.domain.order.vo.OrderId;
+import com.loopers.core.domain.order.vo.OrderItemId;
 import com.loopers.core.domain.order.vo.Quantity;
 import com.loopers.core.domain.product.Product;
 import com.loopers.core.domain.product.repository.ProductRepository;
+import com.loopers.core.domain.product.vo.ProductId;
 import com.loopers.core.domain.product.vo.ProductName;
 import com.loopers.core.domain.product.vo.ProductPrice;
+import com.loopers.core.domain.product.vo.ProductStock;
 import com.loopers.core.domain.user.User;
 import com.loopers.core.domain.user.repository.UserRepository;
-import com.loopers.core.domain.user.type.UserGender;
-import com.loopers.core.domain.user.vo.UserBirthDay;
 import com.loopers.core.domain.user.vo.UserEmail;
 import com.loopers.core.domain.user.vo.UserId;
 import com.loopers.core.domain.user.vo.UserIdentifier;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.order.query.GetOrderDetailQuery;
 import com.loopers.core.service.order.query.GetOrderListQuery;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -39,6 +44,7 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.instancio.Select.field;
 
 class OrderQueryServiceIntegrationTest extends IntegrationTest {
 
@@ -70,22 +76,24 @@ class 주문_리스트_조회_시 {
 
         @BeforeEach
         void setUp() {
-            User user = userRepository.save(User.create(
-                    UserIdentifier.create("loopers"),
-                    new UserEmail("loopers@test.com"),
-                    UserBirthDay.create("1990-01-01"),
-                    UserGender.create("MALE")
-            ));
+            User user = userRepository.save(
+                    Instancio.of(User.class)
+                            .set(field(User::getId), UserId.empty())
+                            .set(field(User::getIdentifier), UserIdentifier.create("loopers"))
+                            .set(field(User::getEmail), new UserEmail("loopers@test.com"))
+                            .create()
+            );
             savedUserIdentifier = user.getIdentifier().value();
-            savedUserId = user.getUserId();
-
-            User otherUser = userRepository.save(User.create(
-                    UserIdentifier.create("other"),
-                    new UserEmail("other@test.com"),
-                    UserBirthDay.create("1991-02-02"),
-                    UserGender.create("FEMALE")
-            ));
-            otherUserId = otherUser.getUserId();
+            savedUserId = user.getId();
+
+            User otherUser = userRepository.save(
+                    Instancio.of(User.class)
+                            .set(field(User::getId), UserId.empty())
+                            .set(field(User::getIdentifier), UserIdentifier.create("other"))
+                            .set(field(User::getEmail), new UserEmail("other@test.com"))
+                            .create()
+            );
+            otherUserId = otherUser.getId();
         }
 
         @Nested
@@ -94,9 +102,33 @@ class 조건에_맞는_주문이_존재하는_경우 {
 
             @BeforeEach
             void setUp() {
-                orderRepository.save(Order.create(savedUserId));
-                orderRepository.save(Order.create(savedUserId));
-                orderRepository.save(Order.create(savedUserId));
+                orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), savedUserId)
+                                .set(field(Order::getCreatedAt), CreatedAt.now())
+                                .set(field(Order::getUpdatedAt), UpdatedAt.now())
+                                .set(field(Order::getDeletedAt), DeletedAt.empty())
+                                .create()
+                );
+                orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), savedUserId)
+                                .set(field(Order::getCreatedAt), CreatedAt.now())
+                                .set(field(Order::getUpdatedAt), UpdatedAt.now())
+                                .set(field(Order::getDeletedAt), DeletedAt.empty())
+                                .create()
+                );
+                orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), savedUserId)
+                                .set(field(Order::getCreatedAt), CreatedAt.now())
+                                .set(field(Order::getUpdatedAt), UpdatedAt.now())
+                                .set(field(Order::getDeletedAt), DeletedAt.empty())
+                                .create()
+                );
             }
 
             @Test
@@ -159,8 +191,18 @@ void setUp() {
             @Test
             @DisplayName("특정 사용자의 주문만 조회된다.")
             void 특정_사용자의_주문만_조회된다() {
-                orderRepository.save(Order.create(otherUserId));
-                orderRepository.save(Order.create(otherUserId));
+                orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), otherUserId)
+                                .create()
+                );
+                orderRepository.save(
+                        Instancio.of(Order.class)
+                                .set(field(Order::getId), OrderId.empty())
+                                .set(field(Order::getUserId), otherUserId)
+                                .create()
+                );
 
                 GetOrderListQuery query = new GetOrderListQuery(
                         savedUserIdentifier,
@@ -237,31 +279,46 @@ class 주문_상세_조회_시 {
 
         @BeforeEach
         void setUp() {
-            Brand brand = brandRepository.save(Brand.create(
-                    new BrandName("loopers"),
-                    new BrandDescription("education brand")
-            ));
-            savedBrandId = brand.getBrandId();
-
-            User user = userRepository.save(User.create(
-                    UserIdentifier.create("loopers"),
-                    new UserEmail("loopers@test.com"),
-                    UserBirthDay.create("1990-01-01"),
-                    UserGender.create("MALE")
-            ));
-            savedUserId = user.getUserId();
-
-            User otherUser = userRepository.save(User.create(
-                    UserIdentifier.create("other"),
-                    new UserEmail("other@test.com"),
-                    UserBirthDay.create("1991-02-02"),
-                    UserGender.create("FEMALE")
-            ));
-
-            Order order = orderRepository.save(Order.create(savedUserId));
-            savedOrderId = order.getOrderId().value();
-
-            Order otherOrder = orderRepository.save(Order.create(otherUser.getUserId()));
+            Brand brand = brandRepository.save(
+                    Instancio.of(Brand.class)
+                            .set(field(Brand::getId), BrandId.empty())
+                            .set(field(Brand::getName), new BrandName("loopers"))
+                            .set(field(Brand::getDescription), new BrandDescription("education brand"))
+                            .create()
+            );
+            savedBrandId = brand.getId();
+
+            User user = userRepository.save(
+                    Instancio.of(User.class)
+                            .set(field(User::getId), UserId.empty())
+                            .set(field(User::getIdentifier), UserIdentifier.create("loopers"))
+                            .set(field(User::getEmail), new UserEmail("loopers@test.com"))
+                            .create()
+            );
+            savedUserId = user.getId();
+
+            User otherUser = userRepository.save(
+                    Instancio.of(User.class)
+                            .set(field(User::getId), UserId.empty())
+                            .set(field(User::getIdentifier), UserIdentifier.create("other"))
+                            .set(field(User::getEmail), new UserEmail("other@test.com"))
+                            .create()
+            );
+
+            Order order = orderRepository.save(
+                    Instancio.of(Order.class)
+                            .set(field(Order::getId), OrderId.empty())
+                            .set(field(Order::getUserId), savedUserId)
+                            .create()
+            );
+            savedOrderId = order.getId().value();
+
+            Order otherOrder = orderRepository.save(
+                    Instancio.of(Order.class)
+                            .set(field(Order::getId), OrderId.empty())
+                            .set(field(Order::getUserId), otherUser.getId())
+                            .create()
+            );
         }
 
         @Nested
@@ -271,33 +328,43 @@ class 주문이_존재하는_경우 {
             @BeforeEach
             void setUp() {
                 Product product1 = productRepository.save(
-                        Product.create(
-                                savedBrandId,
-                                new ProductName("MacBook Pro"),
-                                new ProductPrice(new BigDecimal(1_300_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), savedBrandId)
+                                .set(field(Product::getName), new ProductName("MacBook Pro"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(1_300_000)))
+                                .set(field(Product::getStock), new ProductStock(100L))
+                                .create()
                 );
 
                 Product product2 = productRepository.save(
-                        Product.create(
-                                savedBrandId,
-                                new ProductName("iPad Air"),
-                                new ProductPrice(new BigDecimal(800_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), savedBrandId)
+                                .set(field(Product::getName), new ProductName("iPad Air"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(800_000)))
+                                .set(field(Product::getStock), new ProductStock(100L))
+                                .create()
                 );
 
                 OrderId orderId = new OrderId(savedOrderId);
-                orderItemRepository.save(OrderItem.create(
-                        orderId,
-                        product1.getProductId(),
-                        new Quantity(1L)
-                ));
-
-                orderItemRepository.save(OrderItem.create(
-                        orderId,
-                        product2.getProductId(),
-                        new Quantity(2L)
-                ));
+                orderItemRepository.save(
+                        Instancio.of(OrderItem.class)
+                                .set(field(OrderItem::getId), OrderItemId.empty())
+                                .set(field(OrderItem::getOrderId), orderId)
+                                .set(field(OrderItem::getProductId), product1.getId())
+                                .set(field(OrderItem::getQuantity), new Quantity(1L))
+                                .create()
+                );
+
+                orderItemRepository.save(
+                        Instancio.of(OrderItem.class)
+                                .set(field(OrderItem::getId), OrderItemId.empty())
+                                .set(field(OrderItem::getOrderId), orderId)
+                                .set(field(OrderItem::getProductId), product2.getId())
+                                .set(field(OrderItem::getQuantity), new Quantity(2L))
+                                .create()
+                );
             }
 
             @Test
diff --git a/core/service/order-service/src/test/java/com/loopers/core/service/order/component/CouponDiscountStrategyTest.java b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/CouponDiscountStrategyTest.java
new file mode 100644
index 000000000..eec577333
--- /dev/null
+++ b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/CouponDiscountStrategyTest.java
@@ -0,0 +1,115 @@
+package com.loopers.core.service.order.component;
+
+import com.loopers.core.domain.order.AmountDiscountCoupon;
+import com.loopers.core.domain.order.repository.CouponRepository;
+import com.loopers.core.domain.order.type.CouponStatus;
+import com.loopers.core.domain.order.vo.AmountDiscountCouponId;
+import com.loopers.core.domain.order.vo.CouponDiscountAmount;
+import com.loopers.core.domain.order.vo.CouponId;
+import com.loopers.core.domain.payment.vo.PayAmount;
+import com.loopers.core.domain.user.vo.UserId;
+import com.loopers.core.service.ConcurrencyTestUtil;
+import com.loopers.core.service.IntegrationTest;
+import org.instancio.Instancio;
+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.springframework.beans.factory.annotation.Autowired;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.instancio.Select.field;
+
+@DisplayName("쿠폰 할인전략")
+class CouponDiscountStrategyTest extends IntegrationTest {
+
+    @Autowired
+    private CouponRepository couponRepository;
+
+    @Autowired
+    private CouponDiscountStrategy discountStrategy;
+
+    @Nested
+    @DisplayName("discount()")
+    class DiscountMethod {
+
+        @Nested
+        @DisplayName("쿠폰이 존재하면")
+        class couponExist {
+
+            CouponId couponId;
+
+            @BeforeEach
+            void setUp() {
+                AmountDiscountCoupon coupon = (AmountDiscountCoupon) couponRepository.save(
+                        Instancio.of(AmountDiscountCoupon.class)
+                                .set(field(AmountDiscountCoupon::getCouponId), CouponId.empty())
+                                .set(field(AmountDiscountCoupon::getId), AmountDiscountCouponId.empty())
+                                .set(field(AmountDiscountCoupon::getUserId), new UserId("1"))
+                                .set(field(AmountDiscountCoupon::getAmount), new CouponDiscountAmount(new BigDecimal(1000)))
+                                .set(field(AmountDiscountCoupon::getStatus), CouponStatus.AVAILABLE)
+                                .create()
+                );
+                couponId = coupon.getCouponId();
+            }
+
+            @Test
+            @DisplayName("쿠폰을 사용하고 할인된다.")
+            void usedCouponAndDiscount() {
+                PayAmount payAmount = new PayAmount(new BigDecimal(10000));
+                PayAmount discounted = discountStrategy.discount(payAmount, couponId);
+
+                AmountDiscountCoupon usedCoupon = (AmountDiscountCoupon) couponRepository.getById(couponId);
+
+                assertSoftly(softly -> {
+                    softly.assertThat(discounted.value()).isEqualByComparingTo(new BigDecimal(9000));
+                    softly.assertThat(usedCoupon.getStatus()).isEqualTo(CouponStatus.USED);
+                });
+            }
+        }
+
+        @Nested
+        @DisplayName("동시에 하나의 쿠폰을 여러번 사용하려고 하면")
+        class concurrentlyUseCoupon {
+
+            CouponId couponId;
+
+            @BeforeEach
+            void setUp() {
+                AmountDiscountCoupon coupon = (AmountDiscountCoupon) couponRepository.save(
+                        Instancio.of(AmountDiscountCoupon.class)
+                                .set(field(AmountDiscountCoupon::getCouponId), CouponId.empty())
+                                .set(field(AmountDiscountCoupon::getId), AmountDiscountCouponId.empty())
+                                .set(field(AmountDiscountCoupon::getUserId), new UserId("1"))
+                                .set(field(AmountDiscountCoupon::getAmount), new CouponDiscountAmount(new BigDecimal(1000)))
+                                .set(field(AmountDiscountCoupon::getStatus), CouponStatus.AVAILABLE)
+                                .create()
+                );
+                couponId = coupon.getCouponId();
+            }
+
+            @Test
+            @DisplayName("한번만 사용된다.")
+            void usedOnce() throws InterruptedException {
+                int requestCount = 100;
+                List<PayAmount> payAmounts = ConcurrencyTestUtil.executeInParallel(
+                        requestCount,
+                        index -> {
+                            PayAmount payAmount = new PayAmount(new BigDecimal(10000));
+                            return discountStrategy.discount(payAmount, couponId);
+                        }
+                );
+
+                List<PayAmount> discountedPayAmounts = payAmounts.stream()
+                        .filter(payAmount -> payAmount.value().compareTo(new BigDecimal(9000)) == 0)
+                        .toList();
+                assertThat(discountedPayAmounts).hasSize(1);
+            }
+        }
+    }
+
+}
diff --git a/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderCashierTest.java b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderCashierTest.java
index 3481d30df..befd0ef06 100644
--- a/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderCashierTest.java
+++ b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderCashierTest.java
@@ -1,36 +1,28 @@
 package com.loopers.core.service.order.component;
 
 import com.loopers.core.domain.order.Order;
-import com.loopers.core.domain.order.OrderItem;
+import com.loopers.core.domain.order.vo.CouponId;
 import com.loopers.core.domain.order.vo.OrderId;
-import com.loopers.core.domain.order.vo.Quantity;
-import com.loopers.core.domain.payment.Payment;
-import com.loopers.core.domain.payment.repository.PaymentRepository;
 import com.loopers.core.domain.payment.vo.PayAmount;
-import com.loopers.core.domain.product.vo.ProductId;
 import com.loopers.core.domain.user.User;
 import com.loopers.core.domain.user.UserPoint;
 import com.loopers.core.domain.user.repository.UserPointRepository;
-import com.loopers.core.domain.user.type.UserGender;
 import com.loopers.core.domain.user.vo.*;
+import org.instancio.Instancio;
 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.api.extension.ExtendWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 import java.math.BigDecimal;
-import java.time.LocalDate;
-import java.util.List;
 
-import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.instancio.Select.field;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 @DisplayName("OrderCashier 단위 테스트")
@@ -41,97 +33,65 @@ class OrderCashierTest {
     private UserPointRepository userPointRepository;
 
     @Mock
-    private PaymentRepository paymentRepository;
+    private PayAmountDiscountStrategySelector strategySelector;
+
+    @Mock
+    private PayAmountDiscountStrategy discountStrategy;
 
     @InjectMocks
     private OrderCashier orderCashier;
 
-    private User user;
-    private Order order;
-    private PayAmount payAmount;
-    private UserPoint userPoint;
-
-    @BeforeEach
-    void setUp() {
-        // given
-        user = User.create(
-                new UserIdentifier("testuser"),
-                new UserEmail("test@example.com"),
-                new UserBirthDay(LocalDate.of(2000, 1, 1)),
-                UserGender.MALE
-        );
-
-        order = Order.create(user.getUserId());
-
-        payAmount = new PayAmount(new BigDecimal("20000"));
-
-        userPoint = UserPoint.mappedBy(
-                UserPointId.empty(),
-                user.getUserId(),
-                new UserPointBalance(new BigDecimal("50000")),
-                null,
-                null
-        );
-    }
-
     @Nested
     @DisplayName("checkout 메서드")
     class CheckoutMethod {
 
-        @Test
-        @DisplayName("포인트를 차감하고 Payment를 저장한다")
-        void checkoutSuccess() {
-            // given
-            when(userPointRepository.getByUserId(user.getUserId())).thenReturn(userPoint);
-
-            // when
-            Payment result = orderCashier.checkout(user, order, payAmount);
-
-            // then
-            assertThat(result).isNotNull();
-            assertThat(result.getOrderId()).isEqualTo(order.getOrderId());
-            assertThat(result.getUserId()).isEqualTo(user.getUserId());
-            assertThat(result.getAmount()).isEqualTo(payAmount);
-
-            // 포인트 차감 검증
-            ArgumentCaptor<UserPoint> userPointCaptor = ArgumentCaptor.forClass(UserPoint.class);
-            verify(userPointRepository).save(userPointCaptor.capture());
-            assertThat(userPointCaptor.getValue().getBalance().value())
-                    .isEqualByComparingTo(new BigDecimal("30000")); // 50000 - 20000
-
-            // Payment 저장 검증
-            verify(paymentRepository).save(any(Payment.class));
+        private User user;
+        private Order order;
+
+        @BeforeEach
+        void setUp() {
+            user = Instancio.of(User.class)
+                    .set(field(User::getId), UserId.empty())
+                    .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                    .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                    .create();
+
+            order = Instancio.of(Order.class)
+                    .set(field(Order::getId), OrderId.empty())
+                    .set(field(Order::getUserId), user.getId())
+                    .create();
         }
 
-        @Test
-        @DisplayName("포인트가 부족하면 예외를 발생시킨다")
-        void throwExceptionWhenInsufficientPoint() {
-            // given
-            PayAmount largePayAmount = new PayAmount(new BigDecimal("100000"));
-
-            when(userPointRepository.getByUserId(user.getUserId())).thenReturn(userPoint);
-
-            // when & then
-            assertThatThrownBy(() -> orderCashier.checkout(user, order, largePayAmount))
-                    .isInstanceOf(IllegalArgumentException.class)
-                    .hasMessage("사용자의 포인트 잔액이 충분하지 않습니다.");
-        }
-
-        @Test
-        @DisplayName("정확히 남은 포인트만큼 결제하면 포인트가 0이 된다")
-        void checkoutWithExactPoint() {
-            // given
-            PayAmount exactPayAmount = new PayAmount(new BigDecimal("50000"));
-
-            when(userPointRepository.getByUserId(user.getUserId())).thenReturn(userPoint);
-
-            // when
-            orderCashier.checkout(user, order, exactPayAmount);
-
-            // then
-            ArgumentCaptor<UserPoint> userPointCaptor = ArgumentCaptor.forClass(UserPoint.class);
-            verify(userPointRepository).save(userPointCaptor.capture());
-            assertThat(userPointCaptor.getValue().getBalance().value()).isZero();
+        @Nested
+        @DisplayName("포인트가 부족할 때")
+        class WhenInsufficientPoint {
+
+            private UserPoint userPoint;
+            private PayAmount largePayAmount;
+
+            @BeforeEach
+            void setUp() {
+                userPoint = Instancio.of(UserPoint.class)
+                        .set(field("id"), UserPointId.empty())
+                        .set(field("userId"), user.getId())
+                        .set(field("balance"), new UserPointBalance(new BigDecimal(50_000)))
+                        .create();
+
+                largePayAmount = new PayAmount(new BigDecimal("100000"));
+
+                when(userPointRepository.getByUserIdWithLock(user.getId())).thenReturn(userPoint);
+                when(strategySelector.select(any())).thenReturn(discountStrategy);
+                when(discountStrategy.discount(any(), any())).thenReturn(largePayAmount);
+            }
+
+            @Test
+            @DisplayName("예외를 발생시킨다")
+            void throwExceptionWhenInsufficientPoint() {
+                // when & then
+                assertThatThrownBy(() -> orderCashier.checkout(user, order, largePayAmount, CouponId.empty()))
+                        .isInstanceOf(IllegalArgumentException.class)
+                        .hasMessage("사용자의 포인트 잔액이 충분하지 않습니다.");
+            }
         }
     }
 }
diff --git a/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderLineAggregatorTest.java b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderLineAggregatorTest.java
index 674fa3cfc..cc5ce1440 100644
--- a/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderLineAggregatorTest.java
+++ b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderLineAggregatorTest.java
@@ -2,9 +2,11 @@
 
 import com.loopers.core.domain.order.OrderItem;
 import com.loopers.core.domain.order.vo.OrderId;
+import com.loopers.core.domain.order.vo.OrderItemId;
 import com.loopers.core.domain.order.vo.Quantity;
 import com.loopers.core.domain.payment.vo.PayAmount;
 import com.loopers.core.domain.product.vo.ProductId;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -17,6 +19,7 @@
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.instancio.Select.field;
 import static org.mockito.Mockito.when;
 
 @DisplayName("OrderLineAggregator 단위 테스트")
@@ -37,8 +40,18 @@ class AggregateMethod {
         @DisplayName("여러 상품의 가격을 합산한다")
         void aggregateMultipleProducts() {
             // given
-            OrderItem product1 = OrderItem.create(OrderId.empty(), new ProductId("1"), new Quantity(2L));
-            OrderItem product2 = OrderItem.create(OrderId.empty(), new ProductId("2"), new Quantity(3L));
+            OrderItem product1 = Instancio.of(OrderItem.class)
+                    .set(field(OrderItem::getId), OrderItemId.empty())
+                    .set(field(OrderItem::getProductId), new ProductId("1"))
+                    .set(field(OrderItem::getQuantity), new Quantity(2L))
+                    .set(field(OrderItem::getOrderId), OrderId.empty())
+                    .create();
+            OrderItem product2 = Instancio.of(OrderItem.class)
+                    .set(field(OrderItem::getId), OrderItemId.empty())
+                    .set(field(OrderItem::getProductId), new ProductId("2"))
+                    .set(field(OrderItem::getQuantity), new Quantity(3L))
+                    .set(field(OrderItem::getOrderId), OrderId.empty())
+                    .create();
             List<OrderItem> orderItems = List.of(product1, product2);
 
             when(orderLineAllocator.allocate(product1)).thenReturn(new BigDecimal("20000"));
@@ -55,7 +68,12 @@ void aggregateMultipleProducts() {
         @DisplayName("단일 상품의 가격을 반환한다")
         void aggregateSingleProduct() {
             // given
-            OrderItem orderItem = OrderItem.create(OrderId.empty(), new ProductId("1"), new Quantity(5L));
+            OrderItem orderItem = Instancio.of(OrderItem.class)
+                    .set(field(OrderItem::getId), OrderItemId.empty())
+                    .set(field(OrderItem::getProductId), new ProductId("1"))
+                    .set(field(OrderItem::getQuantity), new Quantity(5L))
+                    .set(field(OrderItem::getOrderId), OrderId.empty())
+                    .create();
             List<OrderItem> orderItems = List.of(orderItem);
 
             when(orderLineAllocator.allocate(orderItem)).thenReturn(new BigDecimal("50000"));
@@ -71,8 +89,18 @@ void aggregateSingleProduct() {
         @DisplayName("소수점이 포함된 가격을 정확하게 합산한다")
         void aggregateWithDecimalPrices() {
             // given
-            OrderItem product1 = OrderItem.create(OrderId.empty(), new ProductId("1"), new Quantity(1L));
-            OrderItem product2 = OrderItem.create(OrderId.empty(), new ProductId("2"), new Quantity(1L));
+            OrderItem product1 = Instancio.of(OrderItem.class)
+                    .set(field(OrderItem::getId), OrderItemId.empty())
+                    .set(field(OrderItem::getProductId), new ProductId("1"))
+                    .set(field(OrderItem::getQuantity), new Quantity(1L))
+                    .set(field(OrderItem::getOrderId), OrderId.empty())
+                    .create();
+            OrderItem product2 = Instancio.of(OrderItem.class)
+                    .set(field(OrderItem::getId), OrderItemId.empty())
+                    .set(field(OrderItem::getProductId), new ProductId("1"))
+                    .set(field(OrderItem::getQuantity), new Quantity(2L))
+                    .set(field(OrderItem::getOrderId), OrderId.empty())
+                    .create();
             List<OrderItem> orderItems = List.of(product1, product2);
 
             when(orderLineAllocator.allocate(product1)).thenReturn(new BigDecimal("10000.50"));
diff --git a/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderLineAllocatorTest.java b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderLineAllocatorTest.java
index 9e5f03c3c..c2ccfc946 100644
--- a/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderLineAllocatorTest.java
+++ b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/OrderLineAllocatorTest.java
@@ -3,12 +3,15 @@
 import com.loopers.core.domain.order.OrderItem;
 import com.loopers.core.domain.order.repository.OrderItemRepository;
 import com.loopers.core.domain.order.vo.OrderId;
+import com.loopers.core.domain.order.vo.OrderItemId;
 import com.loopers.core.domain.order.vo.Quantity;
 import com.loopers.core.domain.product.Product;
 import com.loopers.core.domain.product.repository.ProductRepository;
 import com.loopers.core.domain.product.vo.ProductId;
 import com.loopers.core.domain.product.vo.ProductPrice;
 import com.loopers.core.domain.product.vo.ProductStock;
+import org.instancio.Instancio;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -21,6 +24,7 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.instancio.Select.field;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -42,65 +46,87 @@ class OrderLineAllocatorTest {
     @DisplayName("allocate 메서드")
     class AllocateMethod {
 
-        @Test
-        @DisplayName("충분한 재고가 있으면 재고를 차감하고 총 가격을 반환한다")
-        void allocateWithSufficientStock() {
-            // given
-            ProductId productId = new ProductId("1");
-            Quantity quantity = new Quantity(5L);
-            OrderItem orderItem = OrderItem.create(OrderId.empty(), productId, quantity);
-
-            Product product = Product.mappedBy(
-                    productId,
-                    null,
-                    null,
-                    new ProductPrice(new BigDecimal("10000")),
-                    new ProductStock(100L),
-                    null,
-                    null,
-                    null,
-                    null
-            );
-
-            when(productRepository.getById(productId)).thenReturn(product);
-            when(productRepository.save(any(Product.class))).thenReturn(product.decreaseStock(quantity));
-            when(orderItemRepository.save(any(OrderItem.class))).thenReturn(orderItem);
-
-            // when
-            BigDecimal result = orderLineAllocator.allocate(orderItem);
-
-            // then
-            assertThat(result).isEqualByComparingTo(new BigDecimal("50000")); // 10000 * 5
-            verify(productRepository).save(any(Product.class));
-            verify(orderItemRepository).save(any(OrderItem.class));
+        @Nested
+        @DisplayName("충분한 재고가 있을 때")
+        class WhenSufficientStock {
+
+            private ProductId productId;
+            private Quantity quantity;
+            private OrderItem orderItem;
+            private Product product;
+
+            @BeforeEach
+            void setUp() {
+                productId = new ProductId("1");
+                quantity = new Quantity(5L);
+                orderItem = Instancio.of(OrderItem.class)
+                        .set(field(OrderItem::getId), OrderItemId.empty())
+                        .set(field(OrderItem::getProductId), productId)
+                        .set(field(OrderItem::getQuantity), quantity)
+                        .set(field(OrderItem::getOrderId), OrderId.empty())
+                        .create();
+
+                product = Instancio.of(Product.class)
+                        .set(field(Product::getId), productId)
+                        .set(field(Product::getPrice), new ProductPrice(new BigDecimal("10000")))
+                        .set(field(Product::getStock), new ProductStock(100L))
+                        .create();
+
+                when(productRepository.getByIdWithLock(productId)).thenReturn(product);
+                when(productRepository.save(any(Product.class))).thenReturn(product.decreaseStock(quantity));
+                when(orderItemRepository.save(any(OrderItem.class))).thenReturn(orderItem);
+            }
+
+            @Test
+            @DisplayName("재고를 차감하고 총 가격을 반환한다")
+            void allocateAndReturnTotalPrice() {
+                // when
+                BigDecimal result = orderLineAllocator.allocate(orderItem);
+
+                // then
+                assertThat(result).isEqualByComparingTo(new BigDecimal("50000")); // 10000 * 5
+                verify(productRepository).save(any(Product.class));
+                verify(orderItemRepository).save(any(OrderItem.class));
+            }
         }
 
-        @Test
-        @DisplayName("재고가 부족하면 예외를 발생시킨다")
-        void throwExceptionWhenInsufficientStock() {
-            // given
-            ProductId productId = new ProductId("1");
-            Quantity quantity = new Quantity(101L); // 100개보다 많음
-            OrderItem orderItem = OrderItem.create(OrderId.empty(), productId, quantity);
-
-            Product product = Product.mappedBy(
-                    productId,
-                    null,
-                    null,
-                    new ProductPrice(new BigDecimal("10000")),
-                    new ProductStock(100L),
-                    null,
-                    null,
-                    null,
-                    null
-            );
-
-            when(productRepository.getById(productId)).thenReturn(product);
-
-            // when & then
-            assertThatThrownBy(() -> orderLineAllocator.allocate(orderItem))
-                    .isInstanceOf(IllegalArgumentException.class)
-                    .hasMessage("상품의 재고가 부족합니다.");
+        @Nested
+        @DisplayName("재고가 부족할 때")
+        class WhenInsufficientStock {
+
+            private ProductId productId;
+            private Quantity quantity;
+            private OrderItem orderItem;
+            private Product product;
+
+            @BeforeEach
+            void setUp() {
+                productId = new ProductId("1");
+                quantity = new Quantity(101L); // 100개보다 많음
+                orderItem = Instancio.of(OrderItem.class)
+                        .set(field(OrderItem::getId), OrderItemId.empty())
+                        .set(field(OrderItem::getProductId), productId)
+                        .set(field(OrderItem::getQuantity), quantity)
+                        .set(field(OrderItem::getOrderId), OrderId.empty())
+                        .create();
+
+                product = Instancio.of(Product.class)
+                        .set(field(Product::getId), productId)
+                        .set(field(Product::getPrice), new ProductPrice(new BigDecimal("10000")))
+                        .set(field(Product::getStock), new ProductStock(100L))
+                        .create();
+
+                when(productRepository.getByIdWithLock(productId)).thenReturn(product);
+            }
+
+            @Test
+            @DisplayName("예외를 발생시킨다")
+            void throwException() {
+                // when & then
+                assertThatThrownBy(() -> orderLineAllocator.allocate(orderItem))
+                        .isInstanceOf(IllegalArgumentException.class)
+                        .hasMessage("상품의 재고가 부족합니다.");
+            }
         }
     }
 }
diff --git a/core/service/order-service/src/test/java/com/loopers/core/service/order/component/PayAmountDiscountStrategySelectorTest.java b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/PayAmountDiscountStrategySelectorTest.java
new file mode 100644
index 000000000..f81114ea4
--- /dev/null
+++ b/core/service/order-service/src/test/java/com/loopers/core/service/order/component/PayAmountDiscountStrategySelectorTest.java
@@ -0,0 +1,55 @@
+package com.loopers.core.service.order.component;
+
+import com.loopers.core.domain.order.vo.CouponId;
+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.mockito.Mockito.mock;
+
+@DisplayName("할인 전략 셀렉터")
+class PayAmountDiscountStrategySelectorTest {
+
+    @Nested
+    @DisplayName("select()")
+    class Select {
+
+        @Nested
+        @DisplayName("Coupon이 Null인 경우")
+        class CouponIsNull {
+
+            @Test
+            @DisplayName("NoneDiscountStrategy를 반환한다.")
+            void noneDiscountStrategy() {
+                NoneDiscountStrategy noneDiscountStrategy = mock(NoneDiscountStrategy.class);
+                CouponDiscountStrategy couponDiscountStrategy = mock(CouponDiscountStrategy.class);
+                PayAmountDiscountStrategySelector selector = new PayAmountDiscountStrategySelector(
+                        noneDiscountStrategy,
+                        couponDiscountStrategy
+                );
+
+                PayAmountDiscountStrategy discountStrategy = selector.select(CouponId.empty());
+                assertThat(discountStrategy).isExactlyInstanceOf(NoneDiscountStrategy.class);
+            }
+        }
+
+        @Nested
+        @DisplayName("쿠폰이 Null이 아닌 경우")
+        class couponIsNotNull {
+
+            @Test
+            @DisplayName("CouponDiscountStrategy를 반환한다.")
+            void couponDiscountStrategy() {
+                NoneDiscountStrategy noneDiscountStrategy = mock(NoneDiscountStrategy.class);
+                CouponDiscountStrategy couponDiscountStrategy = mock(CouponDiscountStrategy.class);
+                PayAmountDiscountStrategySelector selector = new PayAmountDiscountStrategySelector(
+                        noneDiscountStrategy,
+                        couponDiscountStrategy
+                );
+                PayAmountDiscountStrategy discountStrategy = selector.select(new CouponId("1"));
+                assertThat(discountStrategy).isExactlyInstanceOf(CouponDiscountStrategy.class);
+            }
+        }
+    }
+}
diff --git a/core/service/product-service/src/main/java/com/loopers/core/service/productlike/ProductLikeQueryService.java b/core/service/product-service/src/main/java/com/loopers/core/service/productlike/ProductLikeQueryService.java
index 6ae49b36a..2c7a32bfb 100644
--- a/core/service/product-service/src/main/java/com/loopers/core/service/productlike/ProductLikeQueryService.java
+++ b/core/service/product-service/src/main/java/com/loopers/core/service/productlike/ProductLikeQueryService.java
@@ -4,7 +4,9 @@
 import com.loopers.core.domain.common.type.OrderSort;
 import com.loopers.core.domain.productlike.LikeProductListView;
 import com.loopers.core.domain.productlike.repository.ProductLikeRepository;
-import com.loopers.core.domain.user.vo.UserId;
+import com.loopers.core.domain.user.User;
+import com.loopers.core.domain.user.repository.UserRepository;
+import com.loopers.core.domain.user.vo.UserIdentifier;
 import com.loopers.core.service.productlike.query.GetLikeProductsListQuery;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
@@ -14,10 +16,13 @@
 public class ProductLikeQueryService {
 
     private final ProductLikeRepository repository;
+    private final UserRepository userRepository;
 
     public LikeProductListView getLikeProductsListView(GetLikeProductsListQuery query) {
+        User user = userRepository.getByIdentifier(new UserIdentifier(query.getUserIdentifier()));
+
         return repository.findLikeProductsListWithCondition(
-                new UserId(query.getUserId()),
+                user.getId(),
                 new BrandId(query.getBrandId()),
                 OrderSort.from(query.getCreatedAtSort()),
                 OrderSort.from(query.getPriceSort()),
diff --git a/core/service/product-service/src/main/java/com/loopers/core/service/productlike/ProductLikeService.java b/core/service/product-service/src/main/java/com/loopers/core/service/productlike/ProductLikeService.java
index fa8176c06..81cecb85d 100644
--- a/core/service/product-service/src/main/java/com/loopers/core/service/productlike/ProductLikeService.java
+++ b/core/service/product-service/src/main/java/com/loopers/core/service/productlike/ProductLikeService.java
@@ -25,13 +25,13 @@ public class ProductLikeService {
     @Transactional
     public void like(ProductLikeCommand command) {
         User user = userRepository.getByIdentifier(new UserIdentifier(command.getUserIdentifier()));
-        Product product = productRepository.getById(new ProductId(command.getProductId()));
+        Product product = productRepository.getByIdWithLock(new ProductId(command.getProductId()));
 
-        boolean isAlreadyLiked = productLikeRepository.findByUserIdAndProductId(user.getUserId(), product.getProductId())
+        boolean isAlreadyLiked = productLikeRepository.findByUserIdAndProductIdWithLock(user.getId(), product.getId())
                 .isPresent();
 
         if (!isAlreadyLiked) {
-            productLikeRepository.save(ProductLike.create(user.getUserId(), product.getProductId()));
+            productLikeRepository.save(ProductLike.create(user.getId(), product.getId()));
             productRepository.save(product.increaseLikeCount());
         }
     }
@@ -39,13 +39,13 @@ public void like(ProductLikeCommand command) {
     @Transactional
     public void unlike(ProductUnlikeCommand command) {
         User user = userRepository.getByIdentifier(new UserIdentifier(command.getUserIdentifier()));
-        Product product = productRepository.getById(new ProductId(command.getProductId()));
+        Product product = productRepository.getByIdWithLock(new ProductId(command.getProductId()));
 
-        boolean isAlreadyLiked = productLikeRepository.findByUserIdAndProductId(user.getUserId(), product.getProductId())
+        boolean isAlreadyLiked = productLikeRepository.findByUserIdAndProductIdWithLock(user.getId(), product.getId())
                 .isPresent();
 
         if (isAlreadyLiked) {
-            productLikeRepository.deleteByUserIdAndProductId(user.getUserId(), product.getProductId());
+            productLikeRepository.deleteByUserIdAndProductId(user.getId(), product.getId());
             productRepository.save(product.decreaseLikeCount());
         }
     }
diff --git a/core/service/product-service/src/main/java/com/loopers/core/service/productlike/query/GetLikeProductsListQuery.java b/core/service/product-service/src/main/java/com/loopers/core/service/productlike/query/GetLikeProductsListQuery.java
index d001f62b9..c4ac9218a 100644
--- a/core/service/product-service/src/main/java/com/loopers/core/service/productlike/query/GetLikeProductsListQuery.java
+++ b/core/service/product-service/src/main/java/com/loopers/core/service/productlike/query/GetLikeProductsListQuery.java
@@ -5,7 +5,7 @@
 @Getter
 public class GetLikeProductsListQuery {
 
-    private final String userId;
+    private final String userIdentifier;
 
     private final String brandId;
 
@@ -20,7 +20,7 @@ public class GetLikeProductsListQuery {
     private final int PageSize;
 
     public GetLikeProductsListQuery(
-            String userId,
+            String userIdentifier,
             String brandId,
             String createdAtSort,
             String priceSort,
@@ -28,7 +28,7 @@ public GetLikeProductsListQuery(
             int pageNo,
             int pageSize
     ) {
-        this.userId = userId;
+        this.userIdentifier = userIdentifier;
         this.brandId = brandId;
         this.createdAtSort = createdAtSort;
         this.priceSort = priceSort;
diff --git a/core/service/product-service/src/test/java/com/loopers/core/service/ConcurrencyTestUtil.java b/core/service/product-service/src/test/java/com/loopers/core/service/ConcurrencyTestUtil.java
new file mode 100644
index 000000000..260dcf8fa
--- /dev/null
+++ b/core/service/product-service/src/test/java/com/loopers/core/service/ConcurrencyTestUtil.java
@@ -0,0 +1,65 @@
+package com.loopers.core.service;
+
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public final class ConcurrencyTestUtil {
+
+    private ConcurrencyTestUtil() {
+        throw new UnsupportedOperationException("이 클래스의 인스턴스는 생성될 수 없습니다.");
+    }
+
+    public static <T> List<T> executeInParallel(
+            int threadCount,
+            Function<Integer, T> task) throws InterruptedException {
+
+        CountDownLatch latch = new CountDownLatch(threadCount);
+        CopyOnWriteArrayList<T> results = new CopyOnWriteArrayList<>();
+
+        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
+            for (int i = 0; i < threadCount; i++) {
+                final int index = i;
+                executor.submit(() -> {
+                    try {
+                        T result = task.apply(index);
+                        results.add(result);
+                    } finally {
+                        latch.countDown();
+                    }
+                });
+            }
+        }
+
+        boolean completed = latch.await(30, TimeUnit.SECONDS);
+        assertThat(completed).isTrue();
+
+        return results;
+    }
+
+    public static void executeInParallelWithoutResult(
+            int threadCount,
+            Consumer<Integer> task) throws InterruptedException {
+
+        CountDownLatch latch = new CountDownLatch(threadCount);
+
+        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
+            for (int i = 0; i < threadCount; i++) {
+                final int index = i;
+                executor.submit(() -> {
+                    try {
+                        task.accept(index);
+                    } finally {
+                        latch.countDown();
+                    }
+                });
+            }
+        }
+
+        boolean completed = latch.await(30, TimeUnit.SECONDS);
+        assertThat(completed).isTrue();
+    }
+}
diff --git a/core/service/product-service/src/test/java/com/loopers/core/service/brand/BrandQueryServiceTest.java b/core/service/product-service/src/test/java/com/loopers/core/service/brand/BrandQueryServiceTest.java
index d211291ce..f496fb60c 100644
--- a/core/service/product-service/src/test/java/com/loopers/core/service/brand/BrandQueryServiceTest.java
+++ b/core/service/product-service/src/test/java/com/loopers/core/service/brand/BrandQueryServiceTest.java
@@ -8,6 +8,7 @@
 import com.loopers.core.domain.error.NotFoundException;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.brand.query.GetBrandQuery;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -16,6 +17,7 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.instancio.Select.field;
 
 class BrandQueryServiceTest extends IntegrationTest {
 
@@ -37,10 +39,13 @@ class 브랜드가_존재하는_경우 {
 
             @BeforeEach
             void setUp() {
-                brandId = brandRepository.save(Brand.create(
-                        new BrandName("loopers"),
-                        new BrandDescription("education brand")
-                )).getBrandId();
+                brandId = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .set(field(Brand::getName), new BrandName("loopers"))
+                                .set(field(Brand::getDescription), new BrandDescription("education brand"))
+                                .create()
+                ).getId();
             }
 
             @Test
diff --git a/core/service/product-service/src/test/java/com/loopers/core/service/product/ProductQueryServiceTest.java b/core/service/product-service/src/test/java/com/loopers/core/service/product/ProductQueryServiceTest.java
index f7f60ff26..a38cf3339 100644
--- a/core/service/product-service/src/test/java/com/loopers/core/service/product/ProductQueryServiceTest.java
+++ b/core/service/product-service/src/test/java/com/loopers/core/service/product/ProductQueryServiceTest.java
@@ -5,17 +5,21 @@
 import com.loopers.core.domain.brand.vo.BrandDescription;
 import com.loopers.core.domain.brand.vo.BrandId;
 import com.loopers.core.domain.brand.vo.BrandName;
+import com.loopers.core.domain.common.vo.DeletedAt;
 import com.loopers.core.domain.error.NotFoundException;
 import com.loopers.core.domain.product.Product;
 import com.loopers.core.domain.product.ProductDetail;
 import com.loopers.core.domain.product.ProductListView;
 import com.loopers.core.domain.product.repository.ProductRepository;
+import com.loopers.core.domain.product.vo.ProductId;
 import com.loopers.core.domain.product.vo.ProductName;
 import com.loopers.core.domain.product.vo.ProductPrice;
+import com.loopers.core.domain.product.vo.ProductStock;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.product.query.GetProductDetailQuery;
 import com.loopers.core.service.product.query.GetProductListQuery;
 import com.loopers.core.service.product.query.GetProductQuery;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -27,6 +31,7 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.instancio.Select.field;
 
 class ProductQueryServiceTest extends IntegrationTest {
 
@@ -48,17 +53,23 @@ class 상품_리스트_조회_시 {
 
         @BeforeEach
         void setUp() {
-            Brand brand = brandRepository.save(Brand.create(
-                    new BrandName("loopers"),
-                    new BrandDescription("education brand")
-            ));
-            savedBrandId = brand.getBrandId();
-
-            Brand otherBrand = brandRepository.save(Brand.create(
-                    new BrandName("other"),
-                    new BrandDescription("other brand")
-            ));
-            otherBrandId = otherBrand.getBrandId();
+            Brand brand = brandRepository.save(
+                    Instancio.of(Brand.class)
+                            .set(field(Brand::getId), BrandId.empty())
+                            .set(field(Brand::getName), new BrandName("loopers"))
+                            .set(field(Brand::getDescription), new BrandDescription("education brand"))
+                            .create()
+            );
+            savedBrandId = brand.getId();
+
+            Brand otherBrand = brandRepository.save(
+                    Instancio.of(Brand.class)
+                            .set(field(Brand::getId), BrandId.empty())
+                            .set(field(Brand::getName), new BrandName("other"))
+                            .set(field(Brand::getDescription), new BrandDescription("other brand"))
+                            .create()
+            );
+            otherBrandId = otherBrand.getId();
         }
 
         @Nested
@@ -68,25 +79,34 @@ class 조건에_맞는_상품이_존재하는_경우 {
             @BeforeEach
             void setUp() {
                 productRepository.save(
-                        Product.create(
-                                savedBrandId,
-                                new ProductName("MacBook Pro"),
-                                new ProductPrice(new BigDecimal(1_300_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), savedBrandId)
+                                .set(field(Product::getName), new ProductName("MacBook Pro"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(1_300_000)))
+                                .set(field(Product::getStock), new ProductStock(100_000L))
+                                .set(field(Product::getDeletedAt), DeletedAt.empty())
+                                .create()
                 );
                 productRepository.save(
-                        Product.create(
-                                savedBrandId,
-                                new ProductName("iPad Air"),
-                                new ProductPrice(new BigDecimal(800_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), savedBrandId)
+                                .set(field(Product::getName), new ProductName("iPad Air"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(800_000)))
+                                .set(field(Product::getStock), new ProductStock(100_000L))
+                                .set(field(Product::getDeletedAt), DeletedAt.empty())
+                                .create()
                 );
                 productRepository.save(
-                        Product.create(
-                                savedBrandId,
-                                new ProductName("iPhone 15"),
-                                new ProductPrice(new BigDecimal(1_500_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), savedBrandId)
+                                .set(field(Product::getName), new ProductName("iPhone 15"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(1_500_000)))
+                                .set(field(Product::getStock), new ProductStock(100_000L))
+                                .set(field(Product::getDeletedAt), DeletedAt.empty())
+                                .create()
                 );
             }
 
@@ -195,11 +215,13 @@ void setUp() {
             @DisplayName("특정 브랜드의 상품만 조회된다.")
             void 특정_브랜드의_상품만_조회된다() {
                 productRepository.save(
-                        Product.create(
-                                otherBrandId,
-                                new ProductName("Samsung Galaxy"),
-                                new ProductPrice(new BigDecimal(1_000_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), otherBrandId)
+                                .set(field(Product::getName), new ProductName("Samsung Galaxy"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(1_000_000)))
+                                .set(field(Product::getStock), new ProductStock(100_000L))
+                                .create()
                 );
 
                 GetProductListQuery query = new GetProductListQuery(
@@ -260,20 +282,25 @@ class ID로_상품_조회_시 {
 
         @BeforeEach
         void setUp() {
-            Brand brand = brandRepository.save(Brand.create(
-                    new BrandName("loopers"),
-                    new BrandDescription("education brand")
-            ));
-            savedBrandId = brand.getBrandId();
+            Brand brand = brandRepository.save(
+                    Instancio.of(Brand.class)
+                            .set(field(Brand::getId), BrandId.empty())
+                            .set(field(Brand::getName), new BrandName("loopers"))
+                            .set(field(Brand::getDescription), new BrandDescription("education brand"))
+                            .create()
+            );
+            savedBrandId = brand.getId();
 
             Product product = productRepository.save(
-                    Product.create(
-                            savedBrandId,
-                            new ProductName("MacBook Pro"),
-                            new ProductPrice(new BigDecimal(1_300_000))
-                    )
+                    Instancio.of(Product.class)
+                            .set(field(Product::getId), ProductId.empty())
+                            .set(field(Product::getBrandId), savedBrandId)
+                            .set(field(Product::getName), new ProductName("MacBook Pro"))
+                            .set(field(Product::getPrice), new ProductPrice(new BigDecimal(1_300_000)))
+                            .set(field(Product::getStock), new ProductStock(100_000L))
+                            .create()
             );
-            savedProductId = product.getProductId().value();
+            savedProductId = product.getId().value();
         }
 
         @Nested
@@ -320,20 +347,25 @@ class 상품_상세_조회_시 {
 
         @BeforeEach
         void setUp() {
-            savedBrand = brandRepository.save(Brand.create(
-                    new BrandName("loopers"),
-                    new BrandDescription("education brand")
-            ));
-            savedBrandId = savedBrand.getBrandId();
+            savedBrand = brandRepository.save(
+                    Instancio.of(Brand.class)
+                            .set(field(Brand::getId), BrandId.empty())
+                            .set(field(Brand::getName), new BrandName("loopers"))
+                            .set(field(Brand::getDescription), new BrandDescription("education brand"))
+                            .create()
+            );
+            savedBrandId = savedBrand.getId();
 
             Product product = productRepository.save(
-                    Product.create(
-                            savedBrandId,
-                            new ProductName("MacBook Pro"),
-                            new ProductPrice(new BigDecimal(1_300_000))
-                    )
+                    Instancio.of(Product.class)
+                            .set(field(Product::getId), ProductId.empty())
+                            .set(field(Product::getBrandId), savedBrandId)
+                            .set(field(Product::getName), new ProductName("MacBook Pro"))
+                            .set(field(Product::getPrice), new ProductPrice(new BigDecimal(1_300_000)))
+                            .set(field(Product::getStock), new ProductStock(100_000L))
+                            .create()
             );
-            savedProductId = product.getProductId().value();
+            savedProductId = product.getId().value();
         }
 
         @Nested
@@ -362,11 +394,11 @@ class 상품이_존재하는_경우 {
                 ProductDetail result = productQueryService.getProductDetail(query);
 
                 assertSoftly(softly -> {
-                    softly.assertThat(result.getProduct().getProductId().value())
+                    softly.assertThat(result.getProduct().getId().value())
                             .isEqualTo(savedProductId);
                     softly.assertThat(result.getProduct().getBrandId().value())
                             .isEqualTo(savedBrandId.value());
-                    softly.assertThat(result.getBrand().getBrandId().value())
+                    softly.assertThat(result.getBrand().getId().value())
                             .isEqualTo(savedBrandId.value());
                 });
             }
@@ -407,14 +439,16 @@ class 브랜드가_존재하지_않는_경우 {
             void NotFoundException이_던져진다() {
                 // 먼저 상품을 생성하고, 브랜드를 삭제하는 시나리오
                 Product orphanProduct = productRepository.save(
-                        Product.create(
-                                new BrandId("99999"),
-                                new ProductName("Orphan Product"),
-                                new ProductPrice(new BigDecimal(100_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), new BrandId("99999"))
+                                .set(field(Product::getName), new ProductName("Orphan Product"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(100_000)))
+                                .set(field(Product::getStock), new ProductStock(100_000L))
+                                .create()
                 );
 
-                GetProductDetailQuery query = new GetProductDetailQuery(orphanProduct.getProductId().value());
+                GetProductDetailQuery query = new GetProductDetailQuery(orphanProduct.getId().value());
 
                 assertThatThrownBy(() -> productQueryService.getProductDetail(query))
                         .isInstanceOf(NotFoundException.class)
diff --git a/core/service/product-service/src/test/java/com/loopers/core/service/productlike/ProductLikeQueryServiceTest.java b/core/service/product-service/src/test/java/com/loopers/core/service/productlike/ProductLikeQueryServiceTest.java
index 0b453888c..0f8db8bdf 100644
--- a/core/service/product-service/src/test/java/com/loopers/core/service/productlike/ProductLikeQueryServiceTest.java
+++ b/core/service/product-service/src/test/java/com/loopers/core/service/productlike/ProductLikeQueryServiceTest.java
@@ -5,22 +5,22 @@
 import com.loopers.core.domain.brand.vo.BrandDescription;
 import com.loopers.core.domain.brand.vo.BrandId;
 import com.loopers.core.domain.brand.vo.BrandName;
+import com.loopers.core.domain.common.vo.DeletedAt;
 import com.loopers.core.domain.product.Product;
 import com.loopers.core.domain.product.repository.ProductRepository;
-import com.loopers.core.domain.product.vo.ProductName;
-import com.loopers.core.domain.product.vo.ProductPrice;
+import com.loopers.core.domain.product.vo.*;
 import com.loopers.core.domain.productlike.LikeProductListView;
 import com.loopers.core.domain.productlike.ProductLike;
 import com.loopers.core.domain.productlike.repository.ProductLikeRepository;
+import com.loopers.core.domain.productlike.vo.ProductLikeId;
 import com.loopers.core.domain.user.User;
 import com.loopers.core.domain.user.repository.UserRepository;
-import com.loopers.core.domain.user.type.UserGender;
-import com.loopers.core.domain.user.vo.UserBirthDay;
 import com.loopers.core.domain.user.vo.UserEmail;
 import com.loopers.core.domain.user.vo.UserId;
 import com.loopers.core.domain.user.vo.UserIdentifier;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.productlike.query.GetLikeProductsListQuery;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -28,10 +28,10 @@
 import org.springframework.beans.factory.annotation.Autowired;
 
 import java.math.BigDecimal;
-import java.time.LocalDate;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.instancio.Select.field;
 
 public class ProductLikeQueryServiceTest extends IntegrationTest {
 
@@ -54,32 +54,8 @@ public class ProductLikeQueryServiceTest extends IntegrationTest {
     @DisplayName("좋아요 상품 리스트 조회 시")
     class 좋아요_상품_리스트_조회_시 {
 
-        private String savedUserId;
+        private String savedUserIdentifier;
         private BrandId savedBrandId;
-        private BrandId otherBrandId;
-
-        @BeforeEach
-        void setUp() {
-            User user = userRepository.save(User.create(
-                    new UserIdentifier("testUser"),
-                    new UserEmail("test@loopers.com"),
-                    new UserBirthDay(LocalDate.of(2000, 1, 1)),
-                    UserGender.MALE
-            ));
-            savedUserId = user.getUserId().value();
-
-            Brand brand = brandRepository.save(Brand.create(
-                    new BrandName("Apple"),
-                    new BrandDescription("Apple products")
-            ));
-            savedBrandId = brand.getBrandId();
-
-            Brand otherBrand = brandRepository.save(Brand.create(
-                    new BrandName("Samsung"),
-                    new BrandDescription("Samsung products")
-            ));
-            otherBrandId = otherBrand.getBrandId();
-        }
 
         @Nested
         @DisplayName("사용자가 좋아요한 상품이 존재하는 경우")
@@ -87,48 +63,85 @@ class 사용자가_좋아요한_상품이_존재하는_경우 {
 
             @BeforeEach
             void setUp() {
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), UserIdentifier.create("testUser"))
+                                .set(field(User::getEmail), new UserEmail("test@loopers.com"))
+                                .create()
+                );
+                savedUserIdentifier = user.getIdentifier().value();
+                savedBrandId = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .set(field(Brand::getName), new BrandName("Test Brand"))
+                                .set(field(Brand::getDescription), new BrandDescription("Test Description"))
+                                .create()
+                ).getId();
+
                 Product product1 = productRepository.save(
-                        Product.create(
-                                savedBrandId,
-                                new ProductName("MacBook Pro"),
-                                new ProductPrice(new BigDecimal(1_300_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), savedBrandId)
+                                .set(field(Product::getName), new ProductName("MacBook Pro"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(1_300_000)))
+                                .set(field(Product::getStock), new ProductStock(10L))
+                                .set(field(Product::getLikeCount), ProductLikeCount.init())
+                                .set(field(Product::getDeletedAt), DeletedAt.empty())
+                                .create()
                 );
                 Product product2 = productRepository.save(
-                        Product.create(
-                                savedBrandId,
-                                new ProductName("iPad Air"),
-                                new ProductPrice(new BigDecimal(800_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), savedBrandId)
+                                .set(field(Product::getName), new ProductName("iPad Air"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(800_000)))
+                                .set(field(Product::getStock), new ProductStock(10L))
+                                .set(field(Product::getLikeCount), ProductLikeCount.init())
+                                .set(field(Product::getDeletedAt), DeletedAt.empty())
+                                .create()
                 );
                 Product product3 = productRepository.save(
-                        Product.create(
-                                otherBrandId,
-                                new ProductName("Galaxy S24"),
-                                new ProductPrice(new BigDecimal(1_100_000))
-                        )
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), savedBrandId)
+                                .set(field(Product::getName), new ProductName("Galaxy S24"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal(1_100_000)))
+                                .set(field(Product::getStock), new ProductStock(10L))
+                                .set(field(Product::getLikeCount), ProductLikeCount.init())
+                                .set(field(Product::getDeletedAt), DeletedAt.empty())
+                                .create()
                 );
 
-                productLikeRepository.save(ProductLike.create(
-                        new UserId(savedUserId),
-                        product1.getProductId()
-                ));
-                productLikeRepository.save(ProductLike.create(
-                        new UserId(savedUserId),
-                        product2.getProductId()
-                ));
-                productLikeRepository.save(ProductLike.create(
-                        new UserId(savedUserId),
-                        product3.getProductId()
-                ));
+                productLikeRepository.save(
+                        Instancio.of(ProductLike.class)
+                                .set(field(ProductLike::getId), ProductLikeId.empty())
+                                .set(field(ProductLike::getUserId), user.getId())
+                                .set(field(ProductLike::getProductId), product1.getId())
+                                .create()
+                );
+                productLikeRepository.save(
+                        Instancio.of(ProductLike.class)
+                                .set(field(ProductLike::getId), ProductLikeId.empty())
+                                .set(field(ProductLike::getUserId), user.getId())
+                                .set(field(ProductLike::getProductId), product2.getId())
+                                .create()
+                );
+                productLikeRepository.save(
+                        Instancio.of(ProductLike.class)
+                                .set(field(ProductLike::getId), ProductLikeId.empty())
+                                .set(field(ProductLike::getUserId), user.getId())
+                                .set(field(ProductLike::getProductId), product3.getId())
+                                .create()
+                );
             }
 
             @Test
             @DisplayName("사용자의 좋아요 상품 리스트가 조회된다.")
             void 사용자의_좋아요_상품_리스트가_조회된다() {
                 GetLikeProductsListQuery query = new GetLikeProductsListQuery(
-                        savedUserId,
-                        savedBrandId.value(),
+                        savedUserIdentifier,
+                        null,
                         "ASC",
                         "ASC",
                         "ASC",
@@ -141,7 +154,7 @@ void setUp() {
                 assertSoftly(softly -> {
                     softly.assertThat(result).isNotNull();
                     softly.assertThat(result.getItems()).isNotEmpty();
-                    softly.assertThat(result.getTotalElements()).isEqualTo(2);
+                    softly.assertThat(result.getTotalElements()).isEqualTo(3);
                 });
             }
 
@@ -149,7 +162,7 @@ void setUp() {
             @DisplayName("브랜드별로 필터링된 좋아요 상품만 조회된다.")
             void 브랜드별로_필터링된_좋아요_상품만_조회된다() {
                 GetLikeProductsListQuery query = new GetLikeProductsListQuery(
-                        savedUserId,
+                        savedUserIdentifier,
                         savedBrandId.value(),
                         "ASC",
                         "ASC",
@@ -161,7 +174,7 @@ void setUp() {
                 LikeProductListView result = productLikeQueryService.getLikeProductsListView(query);
 
                 assertSoftly(softly -> {
-                    softly.assertThat(result.getTotalElements()).isEqualTo(2);
+                    softly.assertThat(result.getTotalElements()).isEqualTo(3);
                     softly.assertThat(result.getItems())
                             .allMatch(item -> item.getBrandId().value().equals(savedBrandId.value()),
                                     "모든 상품이 특정 브랜드에 속해야 함");
@@ -172,8 +185,8 @@ void setUp() {
             @DisplayName("생성일시 오름차순으로 정렬된다.")
             void 생성일시_오름차순으로_정렬된다() {
                 GetLikeProductsListQuery query = new GetLikeProductsListQuery(
-                        savedUserId,
-                        savedBrandId.value(),
+                        savedUserIdentifier,
+                        null,
                         "ASC",
                         "ASC",
                         "ASC",
@@ -194,8 +207,8 @@ void setUp() {
             @DisplayName("생성일시 내림차순으로 정렬된다.")
             void 생성일시_내림차순으로_정렬된다() {
                 GetLikeProductsListQuery query = new GetLikeProductsListQuery(
-                        savedUserId,
-                        savedBrandId.value(),
+                        savedUserIdentifier,
+                        null,
                         "DESC",
                         "ASC",
                         "ASC",
@@ -216,8 +229,8 @@ void setUp() {
             @DisplayName("가격을 함께 정렬할 때 유효하다.")
             void 가격을_함께_정렬할_때_유효하다() {
                 GetLikeProductsListQuery query = new GetLikeProductsListQuery(
-                        savedUserId,
-                        savedBrandId.value(),
+                        savedUserIdentifier,
+                        null,
                         "ASC",
                         "ASC",
                         "ASC",
@@ -236,12 +249,24 @@ void setUp() {
         @DisplayName("사용자가 좋아요한 상품이 없는 경우")
         class 사용자가_좋아요한_상품이_없는_경우 {
 
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), UserIdentifier.create("testUser"))
+                                .set(field(User::getEmail), new UserEmail("test@loopers.com"))
+                                .create()
+                );
+                savedUserIdentifier = user.getIdentifier().value();
+            }
+
             @Test
             @DisplayName("빈 리스트가 반환된다.")
             void 빈_리스트가_반환된다() {
                 GetLikeProductsListQuery query = new GetLikeProductsListQuery(
-                        savedUserId,
-                        savedBrandId.value(),
+                        savedUserIdentifier,
+                        null,
                         "ASC",
                         "ASC",
                         "ASC",
diff --git a/core/service/product-service/src/test/java/com/loopers/core/service/productlike/ProductLikeServiceTest.java b/core/service/product-service/src/test/java/com/loopers/core/service/productlike/ProductLikeServiceTest.java
index f3aefa7bd..5ae8cbece 100644
--- a/core/service/product-service/src/test/java/com/loopers/core/service/productlike/ProductLikeServiceTest.java
+++ b/core/service/product-service/src/test/java/com/loopers/core/service/productlike/ProductLikeServiceTest.java
@@ -8,19 +8,20 @@
 import com.loopers.core.domain.error.NotFoundException;
 import com.loopers.core.domain.product.Product;
 import com.loopers.core.domain.product.repository.ProductRepository;
-import com.loopers.core.domain.product.vo.ProductId;
-import com.loopers.core.domain.product.vo.ProductName;
-import com.loopers.core.domain.product.vo.ProductPrice;
+import com.loopers.core.domain.product.vo.*;
+import com.loopers.core.domain.productlike.ProductLike;
 import com.loopers.core.domain.productlike.repository.ProductLikeRepository;
+import com.loopers.core.domain.productlike.vo.ProductLikeId;
 import com.loopers.core.domain.user.User;
 import com.loopers.core.domain.user.repository.UserRepository;
-import com.loopers.core.domain.user.type.UserGender;
-import com.loopers.core.domain.user.vo.UserBirthDay;
 import com.loopers.core.domain.user.vo.UserEmail;
+import com.loopers.core.domain.user.vo.UserId;
 import com.loopers.core.domain.user.vo.UserIdentifier;
+import com.loopers.core.service.ConcurrencyTestUtil;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.productlike.command.ProductLikeCommand;
 import com.loopers.core.service.productlike.command.ProductUnlikeCommand;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -28,10 +29,11 @@
 import org.springframework.beans.factory.annotation.Autowired;
 
 import java.math.BigDecimal;
-import java.time.LocalDate;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.instancio.Select.field;
 
 class ProductLikeServiceTest extends IntegrationTest {
 
@@ -60,25 +62,34 @@ class 상품_좋아요_등록_시 {
 
         @BeforeEach
         void setUp() {
-            Brand brand = brandRepository.save(Brand.create(
-                    new BrandName("Apple"),
-                    new BrandDescription("Apple products")
-            ));
-            brandId = brand.getBrandId();
-
-            Product product = productRepository.save(Product.create(
-                    brandId,
-                    new ProductName("MacBook Pro"),
-                    new ProductPrice(new BigDecimal("1500000"))
-            ));
-            productId = product.getProductId().value();
-
-            User user = userRepository.save(User.create(
-                    new UserIdentifier("user123"),
-                    new UserEmail("user@example.com"),
-                    new UserBirthDay(LocalDate.of(1990, 1, 1)),
-                    UserGender.MALE
-            ));
+            Brand brand = brandRepository.save(
+                    Instancio.of(Brand.class)
+                            .set(field(Brand::getId), BrandId.empty())
+                            .set(field(Brand::getName), new BrandName("Apple"))
+                            .set(field(Brand::getDescription), new BrandDescription("Apple products"))
+                            .create()
+            );
+            brandId = brand.getId();
+
+            Product product = productRepository.save(
+                    Instancio.of(Product.class)
+                            .set(field(Product::getId), ProductId.empty())
+                            .set(field(Product::getBrandId), brandId)
+                            .set(field(Product::getName), new ProductName("MacBook Pro"))
+                            .set(field(Product::getPrice), new ProductPrice(new BigDecimal("1500000")))
+                            .set(field(Product::getStock), new ProductStock(100_000L))
+                            .set(field(Product::getLikeCount), ProductLikeCount.init())
+                            .create()
+            );
+            productId = product.getId().value();
+
+            User user = userRepository.save(
+                    Instancio.of(User.class)
+                            .set(field(User::getId), UserId.empty())
+                            .set(field(User::getIdentifier), UserIdentifier.create("user123"))
+                            .set(field(User::getEmail), new UserEmail("user@example.com"))
+                            .create()
+            );
             userIdentifier = user.getIdentifier().value();
         }
 
@@ -104,6 +115,54 @@ class 상품이_존재하고_좋아요를_누른_적이_없는_경우 {
             }
         }
 
+        @Nested
+        @DisplayName("하나의 사용자가 여러번 동시에 좋아요를 누른 경우")
+        class 하나의_사용자가_여러번_동시에_좋아요를_누른_경우 {
+
+            @Test
+            @DisplayName("1번만 증가한다.")
+            void 동시에_하나의_사용자가_좋아요를_누르면_1번만_증가한다() throws InterruptedException {
+                int requestCount = 100;
+                ConcurrencyTestUtil.executeInParallelWithoutResult(
+                        requestCount,
+                        index -> productLikeService.like(new ProductLikeCommand(userIdentifier, productId))
+                );
+
+                Product productAfter = productRepository.getById(new ProductId(productId));
+                assertThat(productAfter.getLikeCount().value()).isEqualTo(1L);
+            }
+        }
+
+        @Nested
+        @DisplayName("동시에 여러 사용자가 좋아요를 누른 경우")
+        class 동시에_여러_사용자가_좋아요를_누른_경우 {
+
+            @Test
+            @DisplayName("사용자 수 만큼 좋아요 수가 증가한다.")
+            void 사용자_수_만큼_좋아요_수가_증가한다() throws InterruptedException {
+                int requestCount = 100;
+
+                ConcurrencyTestUtil.executeInParallelWithoutResult(
+                        requestCount,
+                        index -> {
+                            // 동시성 테스트는 domain factory method 사용
+                            User user = userRepository.save(
+                                    Instancio.of(User.class)
+                                            .set(field(User::getId), UserId.empty())
+                                            .set(field(User::getIdentifier), new UserIdentifier("user_" + index))
+                                            .set(field(User::getEmail), new UserEmail("user_" + index + "@example.com"))
+                                            .create()
+                            );
+                            productLikeService.like(new ProductLikeCommand(user.getIdentifier().value(), productId));
+                        }
+                );
+
+                Product productAfter = productRepository.getById(new ProductId(productId));
+                assertThat(productAfter.getLikeCount().value()).isEqualTo(requestCount);
+            }
+        }
+
+
         @Nested
         @DisplayName("상품이 존재하고 이미 좋아요를 누른 경우")
         class 상품이_존재하고_이미_좋아요를_누른_경우 {
@@ -172,36 +231,42 @@ class 상품_좋아요_취소_시 {
         private String productId;
         private String userIdentifier;
 
-        @BeforeEach
-        void setUp() {
-            Brand brand = brandRepository.save(Brand.create(
-                    new BrandName("Apple"),
-                    new BrandDescription("Apple products")
-            ));
-            brandId = brand.getBrandId();
-
-            Product product = productRepository.save(Product.create(
-                    brandId,
-                    new ProductName("MacBook Pro"),
-                    new ProductPrice(new BigDecimal("1500000"))
-            ));
-            productId = product.getProductId().value();
-
-            User user = userRepository.save(User.create(
-                    new UserIdentifier("user123"),
-                    new UserEmail("user@example.com"),
-                    new UserBirthDay(LocalDate.of(1990, 1, 1)),
-                    UserGender.MALE
-            ));
-            userIdentifier = user.getIdentifier().value();
-        }
-
         @Nested
         @DisplayName("상품이 존재하고 이미 좋아요를 누른 경우")
         class 상품이_존재하고_이미_좋아요를_누른_경우 {
 
             @BeforeEach
             void setUp() {
+                Brand brand = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .set(field(Brand::getName), new BrandName("Apple"))
+                                .set(field(Brand::getDescription), new BrandDescription("Apple products"))
+                                .create()
+                );
+                brandId = brand.getId();
+
+                Product product = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getBrandId), brandId)
+                                .set(field(Product::getName), new ProductName("MacBook Pro"))
+                                .set(field(Product::getPrice), new ProductPrice(new BigDecimal("1500000")))
+                                .set(field(Product::getStock), new ProductStock(100_000L))
+                                .set(field(Product::getLikeCount), ProductLikeCount.init())
+                                .create()
+                );
+                productId = product.getId().value();
+
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), UserIdentifier.create("user123"))
+                                .set(field(User::getEmail), new UserEmail("user@example.com"))
+                                .create()
+                );
+                userIdentifier = user.getIdentifier().value();
+
                 ProductLikeCommand likeCommand = new ProductLikeCommand(userIdentifier, productId);
                 productLikeService.like(likeCommand);
             }
@@ -210,8 +275,6 @@ void setUp() {
             @DisplayName("좋아요가 삭제되고 상품의 좋아요 수가 1 감소한다.")
             void 좋아요가_삭제되고_상품의_좋아요_수가_1_감소한다() {
                 ProductUnlikeCommand command = new ProductUnlikeCommand(userIdentifier, productId);
-                Product productBefore = productRepository.getById(new ProductId(productId));
-                long likeCountBefore = productBefore.getLikeCount().value();
 
                 productLikeService.unlike(command);
 
@@ -219,15 +282,156 @@ void setUp() {
                 assertSoftly(softly -> {
                     softly.assertThat(productAfter.getLikeCount().value())
                             .as("상품의 좋아요 수가 1 감소해야 함")
-                            .isEqualTo(likeCountBefore - 1);
+                            .isEqualTo(0L);
                 });
             }
         }
 
+        @Nested
+        @DisplayName("동시에 하나의 사용자가 여러번 취소하면")
+        class 동시에_하나의_사용자가_여러번_취소하면 {
+
+            @BeforeEach
+            void setUp() {
+                Brand brand = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .set(field(Brand::getName), new BrandName("Apple"))
+                                .set(field(Brand::getDescription), new BrandDescription("Apple products"))
+                                .create()
+                );
+                brandId = brand.getId();
+
+                Product product = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getBrandId), brandId)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getLikeCount), new ProductLikeCount(100L))
+                                .create()
+                );
+                productId = product.getId().value();
+
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), UserIdentifier.create("user123"))
+                                .set(field(User::getEmail), new UserEmail("user@example.com"))
+                                .create()
+                );
+                userIdentifier = user.getIdentifier().value();
+
+                productLikeRepository.save(
+                        ProductLike.create(
+                                user.getId(),
+                                product.getId()
+                        )
+                );
+            }
+
+            @Test
+            @DisplayName("동시에 하나의 사용자가 여러번 취소하면 1만 감소한다.")
+            void 동시에_하나의_사용자가_여러번_취소하면_1만_감소한다() throws InterruptedException {
+                ProductUnlikeCommand command = new ProductUnlikeCommand(userIdentifier, productId);
+
+                int requestCount = 100;
+                ConcurrencyTestUtil.executeInParallelWithoutResult(
+                        requestCount,
+                        index -> productLikeService.unlike(command)
+                );
+
+                Product productAfter = productRepository.getById(new ProductId(productId));
+                assertThat(productAfter.getLikeCount().value()).isEqualTo(99L);
+            }
+        }
+
+        @Nested
+        @DisplayName("동시에 여러 사용자가 취소하면")
+        class 동시에_여러_사용자가_취소하면 {
+
+            @BeforeEach
+            void setUp() {
+                Brand brand = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .set(field(Brand::getName), new BrandName("Apple"))
+                                .set(field(Brand::getDescription), new BrandDescription("Apple products"))
+                                .create()
+                );
+                brandId = brand.getId();
+
+                Product product = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getBrandId), brandId)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getLikeCount), new ProductLikeCount(100L))
+                                .create()
+                );
+                productId = product.getId().value();
+            }
+
+            @Test
+            @DisplayName("사용자의 수만큼 좋아요 수가 감소한다.")
+            void 사용자의_수만큼_좋아요_수가_감소한다() throws InterruptedException {
+                int requestCount = 100;
+
+                ConcurrencyTestUtil.executeInParallelWithoutResult(
+                        requestCount,
+                        index -> {
+                            User user = userRepository.save(
+                                    Instancio.of(User.class)
+                                            .set(field(User::getId), UserId.empty())
+                                            .set(field(User::getIdentifier), new UserIdentifier("user_" + index))
+                                            .set(field(User::getEmail), new UserEmail("user_" + index + "@example.com"))
+                                            .create()
+                            );
+                            productLikeRepository.save(
+                                    Instancio.of(ProductLike.class)
+                                            .set(field(ProductLike::getId), ProductLikeId.empty())
+                                            .set(field(ProductLike::getUserId), user.getId())
+                                            .set(field(ProductLike::getProductId), new ProductId(productId))
+                                            .create()
+                            );
+                            productLikeService.unlike(new ProductUnlikeCommand(user.getIdentifier().value(), productId));
+                        }
+                );
+                Product product = productRepository.getById(new ProductId(productId));
+                assertThat(product.getLikeCount().value()).isEqualTo(0L);
+            }
+        }
+
         @Nested
         @DisplayName("상품이 존재하고 좋아요를 누른 적이 없는 경우")
         class 상품이_존재하고_좋아요를_누른_적이_없는_경우 {
 
+            @BeforeEach
+            void setUp() {
+                Brand brand = brandRepository.save(
+                        Instancio.of(Brand.class)
+                                .set(field(Brand::getId), BrandId.empty())
+                                .set(field(Brand::getName), new BrandName("Apple"))
+                                .set(field(Brand::getDescription), new BrandDescription("Apple products"))
+                                .create()
+                );
+                brandId = brand.getId();
+
+                Product product = productRepository.save(
+                        Instancio.of(Product.class)
+                                .set(field(Product::getBrandId), brandId)
+                                .set(field(Product::getId), ProductId.empty())
+                                .set(field(Product::getLikeCount), new ProductLikeCount(100L))
+                                .create()
+                );
+                productId = product.getId().value();
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), UserIdentifier.create("user123"))
+                                .set(field(User::getEmail), new UserEmail("user@example.com"))
+                                .create()
+                );
+                userIdentifier = user.getIdentifier().value();
+            }
+
             @Test
             @DisplayName("아무것도 하지 않는다.")
             void 아무것도_하지_않는다() {
@@ -250,6 +454,18 @@ class 상품이_존재하고_좋아요를_누른_적이_없는_경우 {
         @DisplayName("상품이 존재하지 않는 경우")
         class 상품이_존재하지_않는_경우 {
 
+            @BeforeEach
+            void setUp() {
+                User user = userRepository.save(
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), UserIdentifier.create("user123"))
+                                .set(field(User::getEmail), new UserEmail("user@example.com"))
+                                .create()
+                );
+                userIdentifier = user.getIdentifier().value();
+            }
+
             @Test
             @DisplayName("NotFoundException이 던져진다.")
             void NotFoundException이_던져진다() {
diff --git a/core/service/user-service/src/main/java/com/loopers/core/service/user/JoinUserService.java b/core/service/user-service/src/main/java/com/loopers/core/service/user/JoinUserService.java
index 5bbb9052c..af58facd1 100644
--- a/core/service/user-service/src/main/java/com/loopers/core/service/user/JoinUserService.java
+++ b/core/service/user-service/src/main/java/com/loopers/core/service/user/JoinUserService.java
@@ -36,7 +36,7 @@ public User joinUser(JoinUserCommand command) {
         UserGender userGender = UserGender.create(command.getGender());
 
         User savedUser = userRepository.save(User.create(userIdentifier, userEmail, userBirthDay, userGender));
-        userPointRepository.save(UserPoint.create(savedUser.getUserId()));
+        userPointRepository.save(UserPoint.create(savedUser.getId()));
 
         return savedUser;
     }
diff --git a/core/service/user-service/src/main/java/com/loopers/core/service/user/UserPointQueryService.java b/core/service/user-service/src/main/java/com/loopers/core/service/user/UserPointQueryService.java
index da9b419b8..b79976fa9 100644
--- a/core/service/user-service/src/main/java/com/loopers/core/service/user/UserPointQueryService.java
+++ b/core/service/user-service/src/main/java/com/loopers/core/service/user/UserPointQueryService.java
@@ -20,8 +20,8 @@ public class UserPointQueryService {
     public UserPoint getByUserIdentifier(GetUserPointQuery query) {
         try {
             User user = userRepository.getByIdentifier(new UserIdentifier(query.getUserIdentifier()));
-            
-            return userPointRepository.getByUserId(user.getUserId());
+
+            return userPointRepository.getByUserId(user.getId());
         } catch (NotFoundException exception) {
             return null;
         }
diff --git a/core/service/user-service/src/main/java/com/loopers/core/service/user/UserPointService.java b/core/service/user-service/src/main/java/com/loopers/core/service/user/UserPointService.java
index 744a45f90..af60af880 100644
--- a/core/service/user-service/src/main/java/com/loopers/core/service/user/UserPointService.java
+++ b/core/service/user-service/src/main/java/com/loopers/core/service/user/UserPointService.java
@@ -8,6 +8,7 @@
 import com.loopers.core.service.user.command.UserPointChargeCommand;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 @Service
 @RequiredArgsConstructor
@@ -16,9 +17,10 @@ public class UserPointService {
     private final UserRepository userRepository;
     private final UserPointRepository userPointRepository;
 
+    @Transactional
     public UserPoint charge(UserPointChargeCommand command) {
-        User user = userRepository.getByIdentifier(new UserIdentifier(command.getUserIdentifier()));
-        UserPoint userPoint = userPointRepository.getByUserId(user.getUserId());
+        User user = userRepository.getByIdentifierWithLock(new UserIdentifier(command.getUserIdentifier()));
+        UserPoint userPoint = userPointRepository.getByUserId(user.getId());
 
         return userPointRepository.save(userPoint.charge(command.getPoint()));
     }
diff --git a/core/service/user-service/src/test/java/com/loopers/core/service/ConcurrencyTestUtil.java b/core/service/user-service/src/test/java/com/loopers/core/service/ConcurrencyTestUtil.java
new file mode 100644
index 000000000..260dcf8fa
--- /dev/null
+++ b/core/service/user-service/src/test/java/com/loopers/core/service/ConcurrencyTestUtil.java
@@ -0,0 +1,65 @@
+package com.loopers.core.service;
+
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public final class ConcurrencyTestUtil {
+
+    private ConcurrencyTestUtil() {
+        throw new UnsupportedOperationException("이 클래스의 인스턴스는 생성될 수 없습니다.");
+    }
+
+    public static <T> List<T> executeInParallel(
+            int threadCount,
+            Function<Integer, T> task) throws InterruptedException {
+
+        CountDownLatch latch = new CountDownLatch(threadCount);
+        CopyOnWriteArrayList<T> results = new CopyOnWriteArrayList<>();
+
+        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
+            for (int i = 0; i < threadCount; i++) {
+                final int index = i;
+                executor.submit(() -> {
+                    try {
+                        T result = task.apply(index);
+                        results.add(result);
+                    } finally {
+                        latch.countDown();
+                    }
+                });
+            }
+        }
+
+        boolean completed = latch.await(30, TimeUnit.SECONDS);
+        assertThat(completed).isTrue();
+
+        return results;
+    }
+
+    public static void executeInParallelWithoutResult(
+            int threadCount,
+            Consumer<Integer> task) throws InterruptedException {
+
+        CountDownLatch latch = new CountDownLatch(threadCount);
+
+        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
+            for (int i = 0; i < threadCount; i++) {
+                final int index = i;
+                executor.submit(() -> {
+                    try {
+                        task.accept(index);
+                    } finally {
+                        latch.countDown();
+                    }
+                });
+            }
+        }
+
+        boolean completed = latch.await(30, TimeUnit.SECONDS);
+        assertThat(completed).isTrue();
+    }
+}
diff --git a/core/service/user-service/src/test/java/com/loopers/core/service/user/JoinUserServiceIntegrationTest.java b/core/service/user-service/src/test/java/com/loopers/core/service/user/JoinUserServiceIntegrationTest.java
index 248e95e94..979db3d74 100644
--- a/core/service/user-service/src/test/java/com/loopers/core/service/user/JoinUserServiceIntegrationTest.java
+++ b/core/service/user-service/src/test/java/com/loopers/core/service/user/JoinUserServiceIntegrationTest.java
@@ -2,20 +2,22 @@
 
 import com.loopers.core.domain.user.User;
 import com.loopers.core.domain.user.repository.UserRepository;
-import com.loopers.core.domain.user.type.UserGender;
-import com.loopers.core.domain.user.vo.UserBirthDay;
 import com.loopers.core.domain.user.vo.UserEmail;
 import com.loopers.core.domain.user.vo.UserIdentifier;
+import com.loopers.core.domain.user.vo.UserId;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.user.command.JoinUserCommand;
 import org.assertj.core.api.Assertions;
 import org.assertj.core.api.SoftAssertions;
+import org.instancio.Instancio;
 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.springframework.beans.factory.annotation.Autowired;
 
+import static org.instancio.Select.field;
+
 import java.util.Optional;
 
 class JoinUserServiceIntegrationTest extends IntegrationTest {
@@ -45,7 +47,7 @@ class 유효한_경우 {
                 );
 
                 User user = joinUserService.joinUser(joinUserCommand);
-                Optional<User> findUser = userRepository.findById(user.getUserId());
+                Optional<User> findUser = userRepository.findById(user.getId());
 
                 SoftAssertions.assertSoftly(softly -> {
                     softly.assertThat(findUser.isPresent()).isTrue();
@@ -61,12 +63,11 @@ class 이미_존재하는_회원의_ID인_경우 {
             @BeforeEach
             void setUp() {
                 userRepository.save(
-                        User.create(
-                                UserIdentifier.create("kilian"),
-                                UserEmail.create("kilian@gmail.com"),
-                                UserBirthDay.create("1997-10-08"),
-                                UserGender.create("MALE")
-                        )
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
                 );
             }
 
diff --git a/core/service/user-service/src/test/java/com/loopers/core/service/user/UserPointQueryServiceIntegrationTest.java b/core/service/user-service/src/test/java/com/loopers/core/service/user/UserPointQueryServiceIntegrationTest.java
index d19a64945..b3f196b36 100644
--- a/core/service/user-service/src/test/java/com/loopers/core/service/user/UserPointQueryServiceIntegrationTest.java
+++ b/core/service/user-service/src/test/java/com/loopers/core/service/user/UserPointQueryServiceIntegrationTest.java
@@ -4,20 +4,20 @@
 import com.loopers.core.domain.user.UserPoint;
 import com.loopers.core.domain.user.repository.UserPointRepository;
 import com.loopers.core.domain.user.repository.UserRepository;
-import com.loopers.core.domain.user.type.UserGender;
-import com.loopers.core.domain.user.vo.UserBirthDay;
-import com.loopers.core.domain.user.vo.UserEmail;
-import com.loopers.core.domain.user.vo.UserIdentifier;
+import com.loopers.core.domain.user.vo.*;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.user.query.GetUserPointQuery;
 import org.assertj.core.api.Assertions;
 import org.assertj.core.api.SoftAssertions;
+import org.instancio.Instancio;
 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.springframework.beans.factory.annotation.Autowired;
 
+import static org.instancio.Select.field;
+
 class UserPointQueryServiceIntegrationTest extends IntegrationTest {
 
     @Autowired
@@ -56,15 +56,20 @@ class 해당_ID의_회원이_존재할_경우 {
             @BeforeEach
             void setUp() {
                 savedUser = userRepository.save(
-                        User.create(
-                                UserIdentifier.create("kilian"),
-                                UserEmail.create("kilian@gmail.com"),
-                                UserBirthDay.create("1997-10-08"),
-                                UserGender.create("MALE")
-                        )
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
                 );
 
-                savedUserPoint = userPointRepository.save(UserPoint.create(savedUser.getUserId()));
+                savedUserPoint = userPointRepository.save(
+                        Instancio.of(UserPoint.class)
+                                .set(field(UserPoint::getId), UserPointId.empty())
+                                .set(field(UserPoint::getUserId), savedUser.getId())
+                                .set(field(UserPoint::getBalance), UserPointBalance.init())
+                                .create()
+                );
             }
 
             @Test
@@ -74,7 +79,7 @@ void setUp() {
 
                 SoftAssertions.assertSoftly(softly -> {
                     softly.assertThat(userPoint).isNotNull();
-                    softly.assertThat(userPoint.getUserId().value()).isEqualTo(savedUser.getUserId().value());
+                    softly.assertThat(userPoint.getUserId().value()).isEqualTo(savedUser.getId().value());
                     softly.assertThat(userPoint.getBalance().value().intValue()).isEqualTo(0);
                 });
             }
diff --git a/core/service/user-service/src/test/java/com/loopers/core/service/user/UserPointServiceIntegrationTest.java b/core/service/user-service/src/test/java/com/loopers/core/service/user/UserPointServiceIntegrationTest.java
index 6a51cb3bb..dcffa0561 100644
--- a/core/service/user-service/src/test/java/com/loopers/core/service/user/UserPointServiceIntegrationTest.java
+++ b/core/service/user-service/src/test/java/com/loopers/core/service/user/UserPointServiceIntegrationTest.java
@@ -5,14 +5,11 @@
 import com.loopers.core.domain.user.UserPoint;
 import com.loopers.core.domain.user.repository.UserPointRepository;
 import com.loopers.core.domain.user.repository.UserRepository;
-import com.loopers.core.domain.user.type.UserGender;
-import com.loopers.core.domain.user.vo.UserBirthDay;
-import com.loopers.core.domain.user.vo.UserEmail;
-import com.loopers.core.domain.user.vo.UserIdentifier;
+import com.loopers.core.domain.user.vo.*;
+import com.loopers.core.service.ConcurrencyTestUtil;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.user.command.UserPointChargeCommand;
-import org.assertj.core.api.Assertions;
-import org.assertj.core.api.SoftAssertions;
+import org.instancio.Instancio;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -20,6 +17,11 @@
 import org.springframework.beans.factory.annotation.Autowired;
 
 import java.math.BigDecimal;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.instancio.Select.field;
 
 class UserPointServiceIntegrationTest extends IntegrationTest {
 
@@ -45,14 +47,19 @@ class 정상_충전 {
             @BeforeEach
             void setUp() {
                 user = userRepository.save(
-                        User.create(
-                                UserIdentifier.create("kilian"),
-                                UserEmail.create("kilian@gmail.com"),
-                                UserBirthDay.create("1997-10-08"),
-                                UserGender.create("MALE")
-                        )
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
+                );
+                userPointRepository.save(
+                        Instancio.of(UserPoint.class)
+                                .set(field(UserPoint::getId), UserPointId.empty())
+                                .set(field(UserPoint::getUserId), user.getId())
+                                .set(field(UserPoint::getBalance), UserPointBalance.init())
+                                .create()
                 );
-                userPointRepository.save(UserPoint.create(user.getUserId()));
             }
 
             @Test
@@ -63,10 +70,10 @@ void setUp() {
 
                 // when
                 service.charge(command);
-                UserPoint chargedPoint = userPointRepository.getByUserId(user.getUserId());
+                UserPoint chargedPoint = userPointRepository.getByUserId(user.getId());
 
                 // then
-                SoftAssertions.assertSoftly(softly -> {
+                assertSoftly(softly -> {
                     softly.assertThat(chargedPoint).isNotNull();
                     softly.assertThat(chargedPoint.getBalance().value().intValue()).isEqualTo(1000);
                 });
@@ -84,10 +91,62 @@ class 존재하지_않는_유저_ID로_충전 {
                 UserPointChargeCommand command = new UserPointChargeCommand("nonExist", new BigDecimal(1000));
 
                 // when & then
-                Assertions.assertThatThrownBy(() -> service.charge(command))
+                assertThatThrownBy(() -> service.charge(command))
                         .isInstanceOf(NotFoundException.class);
             }
         }
-    }
 
+        @Nested
+        @DisplayName("동시성 테스트")
+        class ConcurrencyTest {
+
+            @Nested
+            @DisplayName("100명이 동시에 10,000 포인트씩 적립하면")
+            class 동시_포인트_적립_정합성 {
+
+                User user;
+
+                @BeforeEach
+                void setUp() {
+                    user = userRepository.save(
+                            Instancio.of(User.class)
+                                    .set(field(User::getId), UserId.empty())
+                                    .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                    .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                    .create()
+                    );
+                    userPointRepository.save(
+                            Instancio.of(UserPoint.class)
+                                    .set(field(UserPoint::getId), UserPointId.empty())
+                                    .set(field(UserPoint::getUserId), user.getId())
+                                    .set(field(UserPoint::getBalance), UserPointBalance.init())
+                                    .create()
+                    );
+                }
+
+                @Test
+                @DisplayName("정확히 1,000,000 포인트가 된다")
+                void 포인트_충전_결과_확인() throws InterruptedException {
+                    // Given
+                    int requestCount = 100;
+                    BigDecimal chargeAmount = new BigDecimal(10_000);
+                    String userIdentifier = "kilian";
+
+                    // When - 100개의 동시 충전 요청 실행
+                    List<UserPoint> results = ConcurrencyTestUtil.executeInParallel(
+                            requestCount,
+                            index -> service.charge(new UserPointChargeCommand(userIdentifier, chargeAmount))
+                    );
+
+                    // Then
+                    UserPoint userPoint = userPointRepository.getByUserId(user.getId());
+
+                    assertSoftly(softly -> {
+                        softly.assertThat(results).as("동시 요청 결과 수").hasSize(requestCount);
+                        softly.assertThat(userPoint.getBalance().value().intValue()).as("최종 포인트 잔액").isEqualTo(1_000_000);
+                    });
+                }
+            }
+        }
+    }
 }
diff --git a/core/service/user-service/src/test/java/com/loopers/core/service/user/UserQueryServiceTest.java b/core/service/user-service/src/test/java/com/loopers/core/service/user/UserQueryServiceTest.java
index 099d6427f..a8564cbc3 100644
--- a/core/service/user-service/src/test/java/com/loopers/core/service/user/UserQueryServiceTest.java
+++ b/core/service/user-service/src/test/java/com/loopers/core/service/user/UserQueryServiceTest.java
@@ -3,19 +3,22 @@
 import com.loopers.core.domain.user.User;
 import com.loopers.core.domain.user.repository.UserRepository;
 import com.loopers.core.domain.user.type.UserGender;
-import com.loopers.core.domain.user.vo.UserBirthDay;
 import com.loopers.core.domain.user.vo.UserEmail;
 import com.loopers.core.domain.user.vo.UserIdentifier;
+import com.loopers.core.domain.user.vo.UserId;
 import com.loopers.core.service.IntegrationTest;
 import com.loopers.core.service.user.query.GetUserQuery;
 import org.assertj.core.api.Assertions;
 import org.assertj.core.api.SoftAssertions;
+import org.instancio.Instancio;
 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.springframework.beans.factory.annotation.Autowired;
 
+import static org.instancio.Select.field;
+
 class UserQueryServiceTest extends IntegrationTest {
 
     @Autowired
@@ -47,12 +50,11 @@ class ID에_해당하는_사용자가_존재할_경우 {
             @BeforeEach
             void setUp() {
                 userRepository.save(
-                        User.create(
-                                UserIdentifier.create("kilian"),
-                                UserEmail.create("kilian@gmail.com"),
-                                UserBirthDay.create("1997-10-08"),
-                                UserGender.create("MALE")
-                        )
+                        Instancio.of(User.class)
+                                .set(field(User::getId), UserId.empty())
+                                .set(field(User::getIdentifier), new UserIdentifier("kilian"))
+                                .set(field(User::getEmail), new UserEmail("kilian@gmail.com"))
+                                .create()
                 );
             }
 
@@ -65,7 +67,7 @@ void setUp() {
                     softly.assertThat(user).isNotNull();
                     softly.assertThat(user.getIdentifier().value()).isEqualTo("kilian");
                     softly.assertThat(user.getEmail().value()).isEqualTo("kilian@gmail.com");
-                    softly.assertThat(user.getGender()).isEqualTo(UserGender.MALE);
+                    softly.assertThat(user.getGender()).isNotNull();
                 });
             }
         }
