From 0bb8994f58b3b4ec59d59cfcc6e758bd000b6f50 Mon Sep 17 00:00:00 2001 From: eedo_y Date: Thu, 28 Aug 2025 23:32:48 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor/KIKI-64=20:?= =?UTF-8?q?=20=EA=B8=B0=EC=A1=B4=20=EB=B6=81=EB=A7=88=ED=81=AC=20DTO=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/BookmarkController.java | 3 +- .../dto/request/bookmark/BookmarkRequest.java | 5 +-- .../web/swagger/BookmarkControllerSpec.java | 22 +--------- .../application/service/BookmarkService.java | 7 ++-- .../service/BookmarkServiceIntTest.java | 26 +++++------- .../service/ProductServiceIntTest.java | 33 ++++++++------- .../service/RecommendationServiceIntTest.java | 40 +++++++++---------- 7 files changed, 51 insertions(+), 85 deletions(-) diff --git a/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java b/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java index b21139b..6580ca8 100644 --- a/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java +++ b/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java @@ -41,10 +41,9 @@ public ApiResponse saveBookmark( // 아이디 추출 UUID userId = principalDetails.getId(); - request.setUserId(userId); // 서비스 계층 - service.saveBookmark(request); + service.saveBookmark(userId, request); // 리턴 return ApiResponse.created(); diff --git a/src/main/java/site/kikihi/custom/platform/adapter/in/web/dto/request/bookmark/BookmarkRequest.java b/src/main/java/site/kikihi/custom/platform/adapter/in/web/dto/request/bookmark/BookmarkRequest.java index e9f7101..98f39b2 100644 --- a/src/main/java/site/kikihi/custom/platform/adapter/in/web/dto/request/bookmark/BookmarkRequest.java +++ b/src/main/java/site/kikihi/custom/platform/adapter/in/web/dto/request/bookmark/BookmarkRequest.java @@ -14,10 +14,7 @@ ) public class BookmarkRequest { - @Schema(description = "북마크할 상품 ID", example = "101") + @Schema(description = "북마크할 상품 ID", example = "6896ed7d5198cf586e933d6e") private String productId; - @Schema(description = "사용자 UUID", example = "95ea60b2-f63b-434e-afc5-d5e5d6a505e7") - private UUID userId; - } diff --git a/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java b/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java index 2563a13..f3b9bb0 100644 --- a/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java +++ b/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java @@ -9,11 +9,8 @@ import site.kikihi.custom.security.oauth2.domain.PrincipalDetails; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import org.springframework.http.MediaType; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -32,15 +29,7 @@ public interface BookmarkControllerSpec { */ @Operation( summary = "북마크 생성 API", - description = "JWT 기반으로 북마크를 생성할 수 있습니다.", - requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( - content = @Content( - mediaType = MediaType.APPLICATION_JSON_VALUE, - examples = { - @ExampleObject(name = "북마크 생성 성공 예시", value = SUCCESS_PAYLOAD), - } - ) - ) + description = "JWT 기반으로 북마크를 생성할 수 있습니다." ) ApiResponse saveBookmark( @RequestBody @Valid BookmarkRequest request, @@ -82,13 +71,4 @@ ApiResponse deleteBookmark( @Parameter(hidden = true) @AuthenticationPrincipal PrincipalDetails principalDetails); - - - - - String SUCCESS_PAYLOAD = """ - { - "productId": "6896ed675198cf586e933d6c" - } - """; } diff --git a/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java b/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java index d5172d9..9c03970 100644 --- a/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java +++ b/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java @@ -2,6 +2,7 @@ import site.kikihi.custom.global.response.ErrorCode; import site.kikihi.custom.platform.adapter.in.web.dto.request.bookmark.BookmarkRequest; +import site.kikihi.custom.platform.adapter.in.web.dto.request.bookmark.BookmarkSyncRequest; import site.kikihi.custom.platform.adapter.in.web.dto.response.bookmark.BookmarkListResponse; import site.kikihi.custom.platform.application.in.bookmark.BookmarkUseCase; import site.kikihi.custom.platform.application.out.bookmark.BookmarkPort; @@ -36,17 +37,17 @@ public class BookmarkService implements BookmarkUseCase { * @param request Bookmark 생성을 위한 Request */ @Override - public Bookmark saveBookmark(BookmarkRequest request) { + public Bookmark saveBookmark(UUID userId, BookmarkRequest request) { /// 유저 예외 처리 - User user = getUser(request.getUserId()); + User user = getUser(userId); /// 상품 예외 처리 Product product = productPort.getProduct(request.getProductId()) .orElseThrow(() -> new NoSuchElementException(ErrorCode.PRODUCT_NOT_FOUND.getMessage())); /// 이미 눌렀다면 예외 처리 발생 - boolean checked = port.checkBookmarkByUserIdAndProductId(request.getUserId(), request.getProductId()); + boolean checked = port.checkBookmarkByUserIdAndProductId(userId, request.getProductId()); if (checked) { throw new IllegalStateException(ErrorCode.BOOKMARK_ALREADY.getMessage()); diff --git a/src/test/java/site/kikihi/custom/platform/application/service/BookmarkServiceIntTest.java b/src/test/java/site/kikihi/custom/platform/application/service/BookmarkServiceIntTest.java index 98532cc..199b978 100644 --- a/src/test/java/site/kikihi/custom/platform/application/service/BookmarkServiceIntTest.java +++ b/src/test/java/site/kikihi/custom/platform/application/service/BookmarkServiceIntTest.java @@ -86,12 +86,11 @@ class BookmarkSave { // given var request = BookmarkRequest.builder() - .userId(user.getId()) .productId(product1.getId()) .build(); // when - Bookmark bookmark = sut.saveBookmark(request); + Bookmark bookmark = sut.saveBookmark(user.getId(), request); // then Optional result = port.getBookmark(bookmark.getId()); @@ -109,12 +108,11 @@ void saveBookmark_throw_user_NoSuchException() { User fakeUser = UserFixtures.fakeUser(); var request = BookmarkRequest.builder() - .userId(fakeUser.getId()) .productId(product1.getId()) .build(); // when & then - Assertions.assertThatThrownBy(() -> sut.saveBookmark(request)) + Assertions.assertThatThrownBy(() -> sut.saveBookmark(fakeUser.getId(), request)) .isInstanceOf(NoSuchElementException.class) .hasMessageContaining(ErrorCode.USER_NOT_FOUND.getMessage()); } @@ -127,12 +125,11 @@ void saveBookmark_throw_product_NoSuchException() { ProductDocument fakeProduct = ProductFixtures.fakeProduct(); var request = BookmarkRequest.builder() - .userId(user.getId()) .productId(fakeProduct.getId()) .build(); // when & then - Assertions.assertThatThrownBy(() -> sut.saveBookmark(request)) + Assertions.assertThatThrownBy(() -> sut.saveBookmark(user.getId(),request)) .isInstanceOf(NoSuchElementException.class) .hasMessageContaining(ErrorCode.PRODUCT_NOT_FOUND.getMessage()); } @@ -143,14 +140,13 @@ void saveBookmark_throw_product_IllegalStateException() { // given var request = BookmarkRequest.builder() - .userId(user.getId()) .productId(product1.getId()) .build(); - sut.saveBookmark(request); + sut.saveBookmark(user.getId(),request); // when & then - Assertions.assertThatThrownBy(() -> sut.saveBookmark(request)) + Assertions.assertThatThrownBy(() -> sut.saveBookmark(user.getId(),request)) .isInstanceOf(IllegalStateException.class) .hasMessageContaining(ErrorCode.BOOKMARK_ALREADY.getMessage()); } @@ -170,10 +166,9 @@ class BookmarkLoad { List.of(product1, product2, product3).forEach(product -> { var request = BookmarkRequest.builder() - .userId(user.getId()) .productId(product.getId()) .build(); - savedBookmarks.add(sut.saveBookmark(request)); + savedBookmarks.add(sut.saveBookmark(user.getId(),request)); }); // when @@ -199,10 +194,9 @@ class BookmarkLoad { /// 3개의 상품에 북마크 저장 List.of(product1, product2, product3).forEach(product -> { var request = BookmarkRequest.builder() - .userId(user.getId()) .productId(product.getId()) .build(); - savedBookmarks.add(sut.saveBookmark(request)); + savedBookmarks.add(sut.saveBookmark(user.getId(),request)); }); // when @@ -232,11 +226,10 @@ public void deleteBookmark(){ //given var request = BookmarkRequest.builder() - .userId(user.getId()) .productId(product1.getId()) .build(); - Bookmark saveBookmark = sut.saveBookmark(request); + Bookmark saveBookmark = sut.saveBookmark(user.getId(),request); //when sut.deleteBookmarkById(saveBookmark.getId(), user.getId()); @@ -254,10 +247,9 @@ public void deleteBookmark_throw_otherUser_IllegalStateException(){ //given var request = BookmarkRequest.builder() - .userId(user.getId()) .productId(product1.getId()) .build(); - Bookmark saveBookmark = sut.saveBookmark(request); + Bookmark saveBookmark = sut.saveBookmark(user.getId(), request); //when & then Assertions.assertThatThrownBy(() -> sut.deleteBookmarkById(saveBookmark.getId(), user2.getId())) diff --git a/src/test/java/site/kikihi/custom/platform/application/service/ProductServiceIntTest.java b/src/test/java/site/kikihi/custom/platform/application/service/ProductServiceIntTest.java index ed11086..7832487 100644 --- a/src/test/java/site/kikihi/custom/platform/application/service/ProductServiceIntTest.java +++ b/src/test/java/site/kikihi/custom/platform/application/service/ProductServiceIntTest.java @@ -102,9 +102,9 @@ class detailLoadTest { void detailLoad_user_no_bookmark_happy() { // given - var request = getBookmarkRequest(user1.getId(), product1.getId()); + var request = getBookmarkRequest(product1.getId()); - bookmarkService.saveBookmark(request); + bookmarkService.saveBookmark(user1.getId(), request); // when ProductDetailResponse response = sut.getProduct(user1.getId(), product1.getId()); @@ -175,11 +175,12 @@ void listLoad_category_user_bookmark_happy() { Pageable pageable = PageRequest.of(0, 10); // 상품1,3에 북마크 설정 - var request1 = getBookmarkRequest(user1.getId(), product1.getId()); - var request2 = getBookmarkRequest(user1.getId(), product3.getId()); + var request1 = getBookmarkRequest(product1.getId()); + var request2 = getBookmarkRequest(product3.getId()); + + bookmarkService.saveBookmark(user1.getId(), request1); + bookmarkService.saveBookmark(user1.getId(), request2); - List.of(request1, request2) - .forEach(req -> bookmarkService.saveBookmark(req)); // when Slice products = sut.getProductsByCategoryId(user1.getId(), "test", pageable); @@ -262,11 +263,11 @@ void listLoad_category_manufacturer_user_bookmark_happy() { Pageable pageable = PageRequest.of(0, 10); // 상품1,3에 북마크 설정 - var request1 = getBookmarkRequest(user1.getId(), product2.getId()); - var request2 = getBookmarkRequest(user1.getId(), product4.getId()); + var request1 = getBookmarkRequest(product2.getId()); + var request2 = getBookmarkRequest(product4.getId()); - List.of(request1, request2) - .forEach(req -> bookmarkService.saveBookmark(req)); + bookmarkService.saveBookmark(user1.getId(), request1); + bookmarkService.saveBookmark(user1.getId(), request2); // when Slice products = sut.getProductsByCategoryIdAndManufacturerId( @@ -345,9 +346,9 @@ void listLoad_category_budget_user_bookmark_happy() { double price = product1.getPrice(); // 상품2에 북마크 설정 - var request1 = getBookmarkRequest(user1.getId(), product1.getId()); + var request1 = getBookmarkRequest(product1.getId()); - bookmarkService.saveBookmark(request1); + bookmarkService.saveBookmark(user1.getId(), request1); // when Slice products = sut.getProductsByCategoryIdAndPrice( @@ -442,9 +443,9 @@ void listLoad_category_manufacturer_budget_user_bookmark_happy() { int maxPrice = (int) (product4.getPrice() + 10000); // 상품2에 북마크 설정 - var request1 = getBookmarkRequest(user1.getId(), product4.getId()); + var request1 = getBookmarkRequest( product4.getId()); - bookmarkService.saveBookmark(request1); + bookmarkService.saveBookmark(user1.getId(), request1); // when Slice products = sut.getProductsByCategoryIdAndManufacturerIdAndPrice( @@ -510,12 +511,10 @@ void listLoad_category_manufacturer_budget_user_null_happy() { /** * 공통 북마크 DTO 생성 함수 - * @param userId 유저 ID * @param productId 상품 ID */ - private BookmarkRequest getBookmarkRequest(UUID userId, String productId) { + private BookmarkRequest getBookmarkRequest(String productId) { return BookmarkRequest.builder() - .userId(userId) .productId(productId) .build(); } diff --git a/src/test/java/site/kikihi/custom/platform/application/service/RecommendationServiceIntTest.java b/src/test/java/site/kikihi/custom/platform/application/service/RecommendationServiceIntTest.java index aa081c3..b7bcb7e 100644 --- a/src/test/java/site/kikihi/custom/platform/application/service/RecommendationServiceIntTest.java +++ b/src/test/java/site/kikihi/custom/platform/application/service/RecommendationServiceIntTest.java @@ -114,9 +114,9 @@ public void loadRecommendation_preview() throws Exception { public void recommendation_under_8() throws Exception { // given /// 총 북마크 상품 2개 (< 8) -> 북마크 가중 2개, 새로운 랜덤 6개 추천 - bookmarkService.saveBookmark(getBookmarkRequest(user1.getId(), product1.getId())); - bookmarkService.saveBookmark(getBookmarkRequest(user2.getId(), product1.getId())); - bookmarkService.saveBookmark(getBookmarkRequest(user1.getId(), product2.getId())); + bookmarkService.saveBookmark(user1.getId(), getBookmarkRequest(product1.getId())); + bookmarkService.saveBookmark(user2.getId(), getBookmarkRequest(product1.getId())); + bookmarkService.saveBookmark(user1.getId(), getBookmarkRequest(product2.getId())); Set bookmarkedProductIds = Set.of(product1.getId(), product2.getId()); @@ -152,8 +152,8 @@ public void recommendation_under_20() throws Exception { /// 북마크 10개 저장 bookmarks for (ProductDocument p : documents) { - bookmarkService.saveBookmark(getBookmarkRequest(user1.getId(), p.getId())); - bookmarkService.saveBookmark(getBookmarkRequest(user2.getId(), p.getId())); + bookmarkService.saveBookmark(user1.getId(), getBookmarkRequest(p.getId())); + bookmarkService.saveBookmark(user2.getId(), getBookmarkRequest(p.getId())); } Set bookmarkedProductIds = documents.stream() @@ -192,8 +192,8 @@ public void recommendation_20_50() throws Exception { /// 총 북마크 30개 (20 <= 30 <= 50) for (ProductDocument p : documents) { - bookmarkService.saveBookmark(getBookmarkRequest(user1.getId(), p.getId())); - bookmarkService.saveBookmark(getBookmarkRequest(user2.getId(), p.getId())); + bookmarkService.saveBookmark(user1.getId(), getBookmarkRequest(p.getId())); + bookmarkService.saveBookmark(user2.getId(), getBookmarkRequest(p.getId())); } /// 북마크를 한 상품 ID 목록 @@ -234,9 +234,9 @@ public void recommendation_upper_50() throws Exception { // 총 북마크 60개 (60 > 50) for (ProductDocument p : documents) { - bookmarkService.saveBookmark(getBookmarkRequest(user1.getId(), p.getId())); - bookmarkService.saveBookmark(getBookmarkRequest(user2.getId(), p.getId())); - bookmarkService.saveBookmark(getBookmarkRequest(user3.getId(), p.getId())); + bookmarkService.saveBookmark(user1.getId(), getBookmarkRequest(p.getId())); + bookmarkService.saveBookmark(user2.getId(), getBookmarkRequest(p.getId())); + bookmarkService.saveBookmark(user3.getId(), getBookmarkRequest(p.getId())); } List productIds = documents.stream() @@ -281,7 +281,7 @@ void recommend_weightedByBookmarkUserCount() { // 최소 총 북마크 60개 for (ProductDocument p : documents) { - bookmarkService.saveBookmark(getBookmarkRequest(user1.getId(), p.getId())); + bookmarkService.saveBookmark(user1.getId(), getBookmarkRequest(p.getId())); } /// 상품A: 100명 유저가 북마크 @@ -289,19 +289,19 @@ void recommend_weightedByBookmarkUserCount() { for (int i = 0; i < 100; i++) { User u = userPort.saveUser(UserFixtures.createUser(UUID.randomUUID(), "userA" + i, "socialA" + i)); users.add(u); - bookmarkService.saveBookmark(getBookmarkRequest(u.getId(), product1.getId())); + bookmarkService.saveBookmark(u.getId(), getBookmarkRequest(product1.getId())); } /// 상품B: 2명이 북마크 User userB1 = userPort.saveUser(UserFixtures.createUser(UUID.randomUUID(), "userB1", "socialB1")); User userB2 = userPort.saveUser(UserFixtures.createUser(UUID.randomUUID(), "userB2", "socialB2")); - bookmarkService.saveBookmark(getBookmarkRequest(userB1.getId(), product2.getId())); - bookmarkService.saveBookmark(getBookmarkRequest(userB2.getId(), product2.getId())); + bookmarkService.saveBookmark(userB1.getId(),getBookmarkRequest( product2.getId())); + bookmarkService.saveBookmark(userB2.getId(), getBookmarkRequest(product2.getId())); /// 상품C: 1명이 북마크 User userC = userPort.saveUser(UserFixtures.createUser(UUID.randomUUID(), "userC1", "socialC1")); - bookmarkService.saveBookmark(getBookmarkRequest(userC.getId(), product3.getId())); + bookmarkService.saveBookmark(userC.getId(),getBookmarkRequest( product3.getId())); /// 제대로된 추천을 위해 8개의 상품에 대해서 진행해야된다. @@ -331,9 +331,9 @@ void recommendation_noDuplicates() { // given /// 북마크/랜덤 추천 데이터 준비(여러 상황에서 호출할 수 있으나, 추천 결과 유일성만 체크) - bookmarkService.saveBookmark(getBookmarkRequest(user1.getId(), product1.getId())); - bookmarkService.saveBookmark(getBookmarkRequest(user2.getId(), product1.getId())); - bookmarkService.saveBookmark(getBookmarkRequest(user1.getId(), product2.getId())); + bookmarkService.saveBookmark(user1.getId(),getBookmarkRequest(product1.getId())); + bookmarkService.saveBookmark(user2.getId(), getBookmarkRequest(product1.getId())); + bookmarkService.saveBookmark(user1.getId(), getBookmarkRequest(product2.getId())); /// when List recommended = sut.getProductsByRecommendation(null); @@ -351,12 +351,10 @@ void recommendation_noDuplicates() { /** * 공통 북마크 DTO 생성 함수 * - * @param userId 유저 ID * @param productId 상품 ID */ - private BookmarkRequest getBookmarkRequest(UUID userId, String productId) { + private BookmarkRequest getBookmarkRequest(String productId) { return BookmarkRequest.builder() - .userId(userId) .productId(productId) .build(); } From a88dfc1f117d54baa3ca75b8592369e0061f6590 Mon Sep 17 00:00:00 2001 From: eedo_y Date: Fri, 29 Aug 2025 00:40:43 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20feat/KIKI-64=20:=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EC=8B=B1=ED=81=AC=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/BookmarkController.java | 31 ++++++++++-- .../request/bookmark/BookmarkSyncRequest.java | 14 ++++++ .../web/swagger/BookmarkControllerSpec.java | 48 +++++++++++++++++-- .../in/bookmark/BookmarkUseCase.java | 6 ++- .../application/service/BookmarkService.java | 33 ++++++++++++- 5 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 src/main/java/site/kikihi/custom/platform/adapter/in/web/dto/request/bookmark/BookmarkSyncRequest.java diff --git a/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java b/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java index 6580ca8..e333e8e 100644 --- a/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java +++ b/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java @@ -4,6 +4,7 @@ import site.kikihi.custom.global.response.page.PageRequest; import site.kikihi.custom.global.response.page.SliceResponse; import site.kikihi.custom.platform.adapter.in.web.dto.request.bookmark.BookmarkRequest; +import site.kikihi.custom.platform.adapter.in.web.dto.request.bookmark.BookmarkSyncRequest; import site.kikihi.custom.platform.adapter.in.web.dto.request.product.CategoryType; import site.kikihi.custom.platform.adapter.in.web.dto.response.bookmark.BookmarkListResponse; import site.kikihi.custom.platform.adapter.in.web.swagger.BookmarkControllerSpec; @@ -18,7 +19,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; -import java.util.UUID; +import java.util.*; @Slf4j @RestController @@ -39,16 +40,38 @@ public ApiResponse saveBookmark( @RequestBody @Valid BookmarkRequest request, @AuthenticationPrincipal PrincipalDetails principalDetails) { - // 아이디 추출 + /// 아이디 추출 UUID userId = principalDetails.getId(); - // 서비스 계층 + /// 서비스 계층 service.saveBookmark(userId, request); - // 리턴 + /// 리턴 return ApiResponse.created(); } + /** + * 로컬 스토리지에 저장한 북마크를 한번에 저장할 수 있도록 하는 서비스 로직 + * + * @param request 북마크 생성 요청 DTO + */ + @PostMapping("/sync") + public ApiResponse synceBookmark( + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody BookmarkSyncRequest request + ) { + + /// 아이디 추출 + UUID userId = principalDetails.getId(); + + /// 서비스 계층 + service.syncBookmarks(userId, request); + + /// 리턴 + return ApiResponse.created(); + + } + /** * 나의 북마크 목록을 조회합니다. * diff --git a/src/main/java/site/kikihi/custom/platform/adapter/in/web/dto/request/bookmark/BookmarkSyncRequest.java b/src/main/java/site/kikihi/custom/platform/adapter/in/web/dto/request/bookmark/BookmarkSyncRequest.java new file mode 100644 index 0000000..b444240 --- /dev/null +++ b/src/main/java/site/kikihi/custom/platform/adapter/in/web/dto/request/bookmark/BookmarkSyncRequest.java @@ -0,0 +1,14 @@ +package site.kikihi.custom.platform.adapter.in.web.dto.request.bookmark; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class BookmarkSyncRequest { + + @Schema(description = "북마크할 상품들", example = "6896ed7d5198cf586e933d6e") + private List productIds; + +} diff --git a/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java b/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java index f3b9bb0..66638a1 100644 --- a/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java +++ b/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java @@ -1,9 +1,14 @@ package site.kikihi.custom.platform.adapter.in.web.swagger; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; import site.kikihi.custom.global.response.ApiResponse; import site.kikihi.custom.global.response.page.PageRequest; import site.kikihi.custom.global.response.page.SliceResponse; import site.kikihi.custom.platform.adapter.in.web.dto.request.bookmark.BookmarkRequest; +import site.kikihi.custom.platform.adapter.in.web.dto.request.bookmark.BookmarkSyncRequest; import site.kikihi.custom.platform.adapter.in.web.dto.request.product.CategoryType; import site.kikihi.custom.platform.adapter.in.web.dto.response.bookmark.BookmarkListResponse; import site.kikihi.custom.security.oauth2.domain.PrincipalDetails; @@ -12,10 +17,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; @Tag(name = "북마크 API", description = "북마크 기능을 수행하는 API 입니다.") public interface BookmarkControllerSpec { @@ -38,6 +39,31 @@ ApiResponse saveBookmark( @AuthenticationPrincipal PrincipalDetails principalDetails); + /** + * 북마크 싱크합니다. + * + * @param request 북마크 생성 요청 DTO + * @param principalDetails 인증 정보 + */ + @Operation( + summary = "북마크 싱크 API", + description = "JWT 기반으로 한번에 로컬 스토리지에 저장한 내용을 여러 개의 북마크에 대해서 싱크 맞춥니다.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + examples = { + @ExampleObject(name = "북마크 생성 성공 예시", value = SUCCESS_PAYLOAD), + } + ) + ) + ) + ApiResponse synceBookmark( + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody BookmarkSyncRequest request + ); + + + /** * 나의 카테고리별 북마크 목록을 조회합니다. * @@ -71,4 +97,18 @@ ApiResponse deleteBookmark( @Parameter(hidden = true) @AuthenticationPrincipal PrincipalDetails principalDetails); + + + String SUCCESS_PAYLOAD = """ + { + "productIds": [ + { "productId": "689b3eb15198cf586e9341ba" }, + { "productId": "689b3ea65198cf586e9341b9" }, + { "productId": "689b3e995198cf586e9341b8" }, + { "productId": "689b3e8b5198cf586e9341b7" }, + { "productId": "689b3e785198cf586e9341b6" } + ] + } + + """; } diff --git a/src/main/java/site/kikihi/custom/platform/application/in/bookmark/BookmarkUseCase.java b/src/main/java/site/kikihi/custom/platform/application/in/bookmark/BookmarkUseCase.java index 367c651..89fdd02 100644 --- a/src/main/java/site/kikihi/custom/platform/application/in/bookmark/BookmarkUseCase.java +++ b/src/main/java/site/kikihi/custom/platform/application/in/bookmark/BookmarkUseCase.java @@ -1,6 +1,7 @@ package site.kikihi.custom.platform.application.in.bookmark; import site.kikihi.custom.platform.adapter.in.web.dto.request.bookmark.BookmarkRequest; +import site.kikihi.custom.platform.adapter.in.web.dto.request.bookmark.BookmarkSyncRequest; import site.kikihi.custom.platform.adapter.in.web.dto.response.bookmark.BookmarkListResponse; import site.kikihi.custom.platform.domain.bookmark.Bookmark; import org.springframework.data.domain.Pageable; @@ -20,10 +21,13 @@ public interface BookmarkUseCase { /// 저장하기 - Bookmark saveBookmark(BookmarkRequest request); + Bookmark saveBookmark(UUID userId, BookmarkRequest request); Slice loadBookmarksByUserIdAndCategory(UUID userId, String category, Pageable pageable); + /// 비회원을 위한 싱크 맞추기 + void syncBookmarks(UUID userId, BookmarkSyncRequest request); + /// 삭제하기 void deleteBookmarkById(Long id, UUID userId); diff --git a/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java b/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java index 9c03970..f562bee 100644 --- a/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java +++ b/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java @@ -43,8 +43,7 @@ public Bookmark saveBookmark(UUID userId, BookmarkRequest request) { User user = getUser(userId); /// 상품 예외 처리 - Product product = productPort.getProduct(request.getProductId()) - .orElseThrow(() -> new NoSuchElementException(ErrorCode.PRODUCT_NOT_FOUND.getMessage())); + Product product = getProduct(request.getProductId()); /// 이미 눌렀다면 예외 처리 발생 boolean checked = port.checkBookmarkByUserIdAndProductId(userId, request.getProductId()); @@ -59,6 +58,27 @@ public Bookmark saveBookmark(UUID userId, BookmarkRequest request) { return port.saveBookmark(bookmark); } + + + /** + * 로컬 스토리지에 저장한 북마크를 한번에 저장할 수 있도록 하는 서비스 로직 + * + * @param userId 북마크를 저장할 유저 Id + * @param request 저장할 요청 DTO + */ + @Override + public void syncBookmarks(UUID userId, BookmarkSyncRequest request) { + + /// 상품 ID들 + List productIds = request.getProductIds(); + + /// 서비스를 연속으로 실현하여 작동 + List list = productIds.stream() + .map(req -> saveBookmark(userId, req)) + .toList(); + + } + /** * 나의 북마크 조회 * @param userId 북마크 조회할 유저 Id @@ -146,4 +166,13 @@ private static List getProductIds(List bookmarks) { } + /** + * 상품 내부 조회 함수 + * @param productId 상품 ID + */ + private Product getProduct(String productId) { + return productPort.getProduct(productId) + .orElseThrow(() -> new NoSuchElementException(ErrorCode.PRODUCT_NOT_FOUND.getMessage())); + } + } From 8bebd5b3d42a83419005bac5a67795168132d631 Mon Sep 17 00:00:00 2001 From: eedo_y Date: Sun, 31 Aug 2025 13:57:15 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20feat/KIKI-64=20:=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EC=82=AD=EC=A0=9C=20API=20=EB=B0=B0?= =?UTF-8?q?=EC=97=B4=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/BookmarkController.java | 13 +++++++---- .../web/swagger/BookmarkControllerSpec.java | 23 ++++++++++++++----- .../in/bookmark/BookmarkUseCase.java | 2 ++ .../application/service/BookmarkService.java | 19 +++++++++++++++ 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java b/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java index e333e8e..93ef56b 100644 --- a/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java +++ b/src/main/java/site/kikihi/custom/platform/adapter/in/web/BookmarkController.java @@ -106,17 +106,20 @@ public ApiResponse> loadBookmarkByCategory( /** * 북마크를 삭제하는 로직입니다. - * @param bookmarkId 북마크 ID - * @param principalDetails 유저 ID + * + * @param ids 북마크 ID + * @param principalDetails 유저 ID */ - @DeleteMapping("/{bookmarkId}") - public ApiResponse deleteBookmark(@PathVariable Long bookmarkId,@AuthenticationPrincipal PrincipalDetails principalDetails) { + @DeleteMapping() + public ApiResponse deleteBookmark( + @RequestParam List ids, + @AuthenticationPrincipal PrincipalDetails principalDetails) { // 아이디 추출 UUID userId = principalDetails.getId(); // 서비스 계층 - service.deleteBookmarkById(bookmarkId, userId); + service.deleteBookmarkById(ids, userId); return ApiResponse.deleted(); } diff --git a/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java b/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java index 66638a1..910393d 100644 --- a/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java +++ b/src/main/java/site/kikihi/custom/platform/adapter/in/web/swagger/BookmarkControllerSpec.java @@ -18,6 +18,8 @@ import jakarta.validation.Valid; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import java.util.List; + @Tag(name = "북마크 API", description = "북마크 기능을 수행하는 API 입니다.") public interface BookmarkControllerSpec { @@ -52,7 +54,8 @@ ApiResponse saveBookmark( content = @Content( mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { - @ExampleObject(name = "북마크 생성 성공 예시", value = SUCCESS_PAYLOAD), + @ExampleObject(name = "여러개 북마크 생성 성공 예시", value = MULTI_SUCCESS_PAYLOAD), + @ExampleObject(name = "단일 북마크 생성 성공 예시", value = SINGLE_SUCCESS_PAYLOAD), } ) ) @@ -89,17 +92,16 @@ ApiResponse> loadBookmarkByCategory( summary = "북마크를 삭제 API", description = "JWT 기반으로 본인이 기존에 북마크한 상품의 북마크를 해제할 수 있습니다." ) - @DeleteMapping("/{bookmarkId}") - ApiResponse deleteBookmark( + public ApiResponse deleteBookmark( - @Parameter(example = "1") - @PathVariable Long bookmarkId, + @Parameter(example = "[1]", description = "삭제할 북마크 목록 (여러 개 입력 가능)") + @RequestParam List ids, @Parameter(hidden = true) @AuthenticationPrincipal PrincipalDetails principalDetails); - String SUCCESS_PAYLOAD = """ + String MULTI_SUCCESS_PAYLOAD = """ { "productIds": [ { "productId": "689b3eb15198cf586e9341ba" }, @@ -111,4 +113,13 @@ ApiResponse deleteBookmark( } """; + + String SINGLE_SUCCESS_PAYLOAD = """ + { + "productIds": [ + { "productId": "689b3eb15198cf586e9341ba" } + ] + } + + """; } diff --git a/src/main/java/site/kikihi/custom/platform/application/in/bookmark/BookmarkUseCase.java b/src/main/java/site/kikihi/custom/platform/application/in/bookmark/BookmarkUseCase.java index 89fdd02..c75a617 100644 --- a/src/main/java/site/kikihi/custom/platform/application/in/bookmark/BookmarkUseCase.java +++ b/src/main/java/site/kikihi/custom/platform/application/in/bookmark/BookmarkUseCase.java @@ -31,4 +31,6 @@ public interface BookmarkUseCase { /// 삭제하기 void deleteBookmarkById(Long id, UUID userId); + void deleteBookmarkById(List ids, UUID userId); + } diff --git a/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java b/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java index f562bee..b4a4025 100644 --- a/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java +++ b/src/main/java/site/kikihi/custom/platform/application/service/BookmarkService.java @@ -144,6 +144,25 @@ public void deleteBookmarkById(Long id, UUID userId) { } + /** + * 북마크 삭제 기능 구현 + * @param ids 삭제할 북마크 Ids + * @param userId 북마크를 삭제할 유저 Id + */ + @Override + public void deleteBookmarkById(List ids, UUID userId) { + + /// 삭제에 대한 반복문 실행 + + /// TODO! 지울 때마다 유저가 생성한 북마크인지 체크하기 위해서 진행 + for (Long id : ids) { + + /// 서비스 로직 실행 + deleteBookmarkById(id, userId); + } + + } + /// 공통 함수 /** * ID를 바탕으로 DB에서 유저를 조회하는