Repository: popi-official/popi-user-server
Pull request #47: feat: 상품 검색 기능 구현
Author: login: woogieon8on
Labels: name: ✨ feature
Created: 2025-05-23T11:15:07Z | Merged: 2025-05-26T00:57:49Z

## 🌱 관련 이슈

- [LCR-191]

---
## 📌 작업 내용 및 특이사항

- 상품 검색 기능을 구현하였습니다.
- 전체 상품 조회 API와 통합하여 구현하였으며, keyWord의 포함 여부에 따라 전체 상품 또는 검색 결과가 반환됩니다.
- ManagerServiceClient의 설정 파일을 common 모듈의 FeignConfig로 변경하였습니다.
- Manager Service name, url을 수정하고 application-test.yml 파일을 추가하였습니다.
- 각 테스트가 실행될 때마다 wireMock stub을 제거하도록 resetAll() 메서드를 추가하였습니다.
- 상품 검색이 성공한 경우, 검색 결과가 없는 경우에 대해 테스트 코드를 작성하였으며, 별도의 예외 처리는 추가하지 않았습니다.

---
## 📚 참고사항

-


[LCR-191]: https://lgcns-retail.atlassian.net/browse/LCR-191?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/popi-popup-service/src/main/java/com/lgcns/client/ManagerServiceClient.java b/popi-popup-service/src/main/java/com/lgcns/client/ManagerServiceClient.java
index 08c2c762..ebad3a81 100644
--- a/popi-popup-service/src/main/java/com/lgcns/client/ManagerServiceClient.java
+++ b/popi-popup-service/src/main/java/com/lgcns/client/ManagerServiceClient.java
@@ -11,7 +11,7 @@
 
 @FeignClient(
         name = "${manager.service.name}",
-        url = "${manager.service.url}",
+        url = "${manager.service.url:}",
         configuration = FeignConfig.class)
 public interface ManagerServiceClient {
 
@@ -21,8 +21,9 @@ SliceResponse<PopupInfoResponse> findAllPopups(
             @RequestParam(name = "size", defaultValue = "8") int size);
 
     @GetMapping("/internal/popups/{popupId}/items")
-    SliceResponse<ItemInfoResponse> findAllItems(
+    SliceResponse<ItemInfoResponse> findItemsByName(
             @PathVariable(name = "popupId") Long popupId,
+            @RequestParam(name = "searchName", required = false) String searchName,
             @RequestParam(name = "lastItemId", required = false) Long lastItemId,
             @RequestParam(name = "size", defaultValue = "8") int size);
 }
diff --git a/popi-popup-service/src/main/java/com/lgcns/externalApi/ItemController.java b/popi-popup-service/src/main/java/com/lgcns/externalApi/ItemController.java
index 08ee894c..164a18be 100644
--- a/popi-popup-service/src/main/java/com/lgcns/externalApi/ItemController.java
+++ b/popi-popup-service/src/main/java/com/lgcns/externalApi/ItemController.java
@@ -4,6 +4,7 @@
 import com.lgcns.response.SliceResponse;
 import com.lgcns.service.item.ItemService;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.RequiredArgsConstructor;
 import org.springframework.web.bind.annotation.*;
@@ -17,11 +18,23 @@ public class ItemController {
     private final ItemService itemService;
 
     @GetMapping
-    @Operation(summary = "상품 목록 조회", description = "현재 팝업의 모든 상품을 무한 스크롤을 위하여 페이징 처리한 뒤 반환합니다.")
-    public SliceResponse<ItemInfoResponse> userItemFindAll(
-            @PathVariable(name = "popupId") Long popupId,
-            @RequestParam(name = "lastItemId", required = false) Long lastItemId,
-            @RequestParam(name = "size", defaultValue = "8") int size) {
-        return itemService.findAllItems(popupId, lastItemId, size);
+    @Operation(
+            summary = "상품 목록 조회",
+            description =
+                    "searchName을 포함하지 않으면 현재 팝업의 모든 상품을 무한 스크롤을 위하여 페이징 처리한 뒤 반환합니다.</br>"
+                            + "searchName을 포함하여 호출하면 상품명에 searchName이 포함된 모든 상품을 페이징 처리한 뒤 반환합니다.")
+    public SliceResponse<ItemInfoResponse> userItemFindByName(
+            @Parameter(description = "팝업 ID", example = "1") @PathVariable(name = "popupId")
+                    Long popupId,
+            @Parameter(description = "검색할 상품 이름 (비워두면 모든 상품을 반환합니다.)", example = "포토카드")
+                    @RequestParam(name = "keyWord", required = false)
+                    String keyWord,
+            @Parameter(description = "이전 페이지의 마지막 ID (첫 요청 시 비워두세요.)", example = "2")
+                    @RequestParam(name = "lastItemId", required = false)
+                    Long lastItemId,
+            @Parameter(description = "페이지 크기 (기본 8)", example = "8")
+                    @RequestParam(name = "size", defaultValue = "8")
+                    int size) {
+        return itemService.findItemsByName(popupId, keyWord, lastItemId, size);
     }
 }
diff --git a/popi-popup-service/src/main/java/com/lgcns/service/item/ItemService.java b/popi-popup-service/src/main/java/com/lgcns/service/item/ItemService.java
index 2ad86ceb..3e22968a 100644
--- a/popi-popup-service/src/main/java/com/lgcns/service/item/ItemService.java
+++ b/popi-popup-service/src/main/java/com/lgcns/service/item/ItemService.java
@@ -4,5 +4,6 @@
 import com.lgcns.response.SliceResponse;
 
 public interface ItemService {
-    SliceResponse<ItemInfoResponse> findAllItems(Long popupId, Long lastItemId, int size);
+    SliceResponse<ItemInfoResponse> findItemsByName(
+            Long popupId, String searchName, Long lastItemId, int size);
 }
diff --git a/popi-popup-service/src/main/java/com/lgcns/service/item/ItemServiceImpl.java b/popi-popup-service/src/main/java/com/lgcns/service/item/ItemServiceImpl.java
index 2cfc0e70..96b09b4f 100644
--- a/popi-popup-service/src/main/java/com/lgcns/service/item/ItemServiceImpl.java
+++ b/popi-popup-service/src/main/java/com/lgcns/service/item/ItemServiceImpl.java
@@ -13,7 +13,8 @@ public class ItemServiceImpl implements ItemService {
     private final ManagerServiceClient managerServiceClient;
 
     @Override
-    public SliceResponse<ItemInfoResponse> findAllItems(Long popupId, Long lastItemId, int size) {
-        return managerServiceClient.findAllItems(popupId, lastItemId, size);
+    public SliceResponse<ItemInfoResponse> findItemsByName(
+            Long popupId, String searchName, Long lastItemId, int size) {
+        return managerServiceClient.findItemsByName(popupId, searchName, lastItemId, size);
     }
 }
diff --git a/popi-popup-service/src/main/resources/application-local.yml b/popi-popup-service/src/main/resources/application-local.yml
index f696593b..77908cc5 100644
--- a/popi-popup-service/src/main/resources/application-local.yml
+++ b/popi-popup-service/src/main/resources/application-local.yml
@@ -31,5 +31,5 @@ spring-doc:
 
 manager:
   service:
-    name: popi-manager-service
+    name: managers
     url: http://localhost:8080
diff --git a/popi-popup-service/src/test/java/com/lgcns/WireMockIntegrationTest.java b/popi-popup-service/src/test/java/com/lgcns/WireMockIntegrationTest.java
index adf0e254..46237b31 100644
--- a/popi-popup-service/src/test/java/com/lgcns/WireMockIntegrationTest.java
+++ b/popi-popup-service/src/test/java/com/lgcns/WireMockIntegrationTest.java
@@ -16,12 +16,13 @@ public abstract class WireMockIntegrationTest {
     @Autowired protected WireMockServer wireMockServer;
 
     @BeforeEach
-    void restartWireMock() {
-        wireMockServer.resetAll();
+    void setUp() {
+        wireMockServer.stop();
+        wireMockServer.start();
     }
 
     @AfterEach
-    void resetWireMock() {
+    void afterEach() {
         wireMockServer.resetAll();
     }
 }
diff --git a/popi-popup-service/src/test/java/com/lgcns/service/ItemServiceTest.java b/popi-popup-service/src/test/java/com/lgcns/service/ItemServiceTest.java
index d43b54b2..0d47ee73 100644
--- a/popi-popup-service/src/test/java/com/lgcns/service/ItemServiceTest.java
+++ b/popi-popup-service/src/test/java/com/lgcns/service/ItemServiceTest.java
@@ -13,7 +13,6 @@
 import java.util.List;
 import java.util.Map;
 import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -26,108 +25,62 @@ public class ItemServiceTest extends WireMockIntegrationTest {
 
     private final Long popupId = 1L;
 
-    @BeforeEach
-    void setUp() throws JsonProcessingException {
-        String responseBody1 =
-                objectMapper.writeValueAsString(
-                        Map.of(
-                                "content",
-                                List.of(
-                                        Map.of(
-                                                "itemId",
-                                                24,
-                                                "name",
-                                                "크룽크 빅쿠션",
-                                                "imageUrl",
-                                                "https://ygselect.com/web/product/big/shop1_c46529e55a48ad83a68b0f78540465e8.jpg",
-                                                "price",
-                                                25000),
-                                        Map.of(
-                                                "itemId",
-                                                23,
-                                                "name",
-                                                "크룽크 미니쿠션",
-                                                "imageUrl",
-                                                "https://ygselect.com/web/product/big/shop1_6e08c64b2daaf6f2142cbd32c9c7a38a.jpg",
-                                                "price",
-                                                18000),
-                                        Map.of(
-                                                "itemId",
-                                                22,
-                                                "name",
-                                                "크룽크 키링",
-                                                "imageUrl",
-                                                "https://ygselect.com/web/product/big/shop1_d2725a478a33ccaa10b1b7f8697f5c9d.png",
-                                                "price",
-                                                14000),
-                                        Map.of(
-                                                "itemId",
-                                                21,
-                                                "name",
-                                                "크룽크 손목인형",
-                                                "imageUrl",
-                                                "https://ygselect.com/web/product/big/202403/P0000HDL.jpg",
-                                                "price",
-                                                7000)),
-                                "isLast",
-                                false));
-
-        String responseBody2 =
-                objectMapper.writeValueAsString(
-                        Map.of(
-                                "content",
-                                List.of(
-                                        Map.of(
-                                                "itemId",
-                                                4,
-                                                "name",
-                                                "DAZED 리사",
-                                                "imageUrl",
-                                                "https://image.aladin.co.kr/product/17920/59/cover500/k142534034_1.jpg",
-                                                "price",
-                                                8000),
-                                        Map.of(
-                                                "itemId",
-                                                3,
-                                                "name",
-                                                "DAZED 제니",
-                                                "imageUrl",
-                                                "https://image.aladin.co.kr/product/28453/14/cover500/k572835617_1.jpg",
-                                                "price",
-                                                9500),
-                                        Map.of(
-                                                "itemId",
-                                                2,
-                                                "name",
-                                                "DAZED 로제",
-                                                "imageUrl",
-                                                "https://ygselect.com/web/product/big/202404/257d66b0a1eca57af67b6c792e68ed75.jpg",
-                                                "price",
-                                                15000),
-                                        Map.of(
-                                                "itemId",
-                                                1,
-                                                "name",
-                                                "DAZED 지수",
-                                                "imageUrl",
-                                                "https://ygselect.com/web/product/big/202402/P0000NMY.jpg",
-                                                "price",
-                                                15000)),
-                                "isLast",
-                                true));
-        stubFindAllItems(popupId, null, 4, 200, responseBody1);
-        stubFindAllItems(popupId, 5L, 4, 200, responseBody2);
-    }
-
     @Nested
-    class 상품_목록_조회 {
+    class 상품_목록을_조회할_때 {
         @Test
-        void 상품_목록_조회에_성공한다() {
+        void 데이터가_존재하면_상품_목록_조회에_성공한다() throws JsonProcessingException {
             // given
             int size = 4;
 
+            String responseBody =
+                    objectMapper.writeValueAsString(
+                            Map.of(
+                                    "content",
+                                    List.of(
+                                            Map.of(
+                                                    "itemId",
+                                                    24,
+                                                    "name",
+                                                    "크룽크 빅쿠션",
+                                                    "imageUrl",
+                                                    "https://ygselect.com/web/product/big/shop1_c46529e55a48ad83a68b0f78540465e8.jpg",
+                                                    "price",
+                                                    25000),
+                                            Map.of(
+                                                    "itemId",
+                                                    23,
+                                                    "name",
+                                                    "크룽크 미니쿠션",
+                                                    "imageUrl",
+                                                    "https://ygselect.com/web/product/big/shop1_6e08c64b2daaf6f2142cbd32c9c7a38a.jpg",
+                                                    "price",
+                                                    18000),
+                                            Map.of(
+                                                    "itemId",
+                                                    22,
+                                                    "name",
+                                                    "크룽크 키링",
+                                                    "imageUrl",
+                                                    "https://ygselect.com/web/product/big/shop1_d2725a478a33ccaa10b1b7f8697f5c9d.png",
+                                                    "price",
+                                                    14000),
+                                            Map.of(
+                                                    "itemId",
+                                                    21,
+                                                    "name",
+                                                    "크룽크 손목인형",
+                                                    "imageUrl",
+                                                    "https://ygselect.com/web/product/big/202403/P0000HDL.jpg",
+                                                    "price",
+                                                    7000)),
+                                    "isLast",
+                                    false));
+
+            stubFindItemsByName(popupId, null, null, 4, 200, responseBody);
+
             // when
-            SliceResponse<ItemInfoResponse> result = itemService.findAllItems(popupId, null, size);
+            SliceResponse<ItemInfoResponse> result =
+                    itemService.findItemsByName(popupId, null, null, size);
 
             // then
             Assertions.assertAll(
@@ -144,14 +97,60 @@ class 상품_목록_조회 {
         }
 
         @Test
-        void 마지막_상품까지_조회하면_is_last가_true를_반환한다() {
+        void 마지막_상품까지_조회하면_is_last가_true를_반환한다() throws JsonProcessingException {
             // given
             Long lastItemId = 5L;
             int size = 4;
 
+            String responseBody =
+                    objectMapper.writeValueAsString(
+                            Map.of(
+                                    "content",
+                                    List.of(
+                                            Map.of(
+                                                    "itemId",
+                                                    4,
+                                                    "name",
+                                                    "DAZED 리사",
+                                                    "imageUrl",
+                                                    "https://image.aladin.co.kr/product/17920/59/cover500/k142534034_1.jpg",
+                                                    "price",
+                                                    8000),
+                                            Map.of(
+                                                    "itemId",
+                                                    3,
+                                                    "name",
+                                                    "DAZED 제니",
+                                                    "imageUrl",
+                                                    "https://image.aladin.co.kr/product/28453/14/cover500/k572835617_1.jpg",
+                                                    "price",
+                                                    9500),
+                                            Map.of(
+                                                    "itemId",
+                                                    2,
+                                                    "name",
+                                                    "DAZED 로제",
+                                                    "imageUrl",
+                                                    "https://ygselect.com/web/product/big/202404/257d66b0a1eca57af67b6c792e68ed75.jpg",
+                                                    "price",
+                                                    15000),
+                                            Map.of(
+                                                    "itemId",
+                                                    1,
+                                                    "name",
+                                                    "DAZED 지수",
+                                                    "imageUrl",
+                                                    "https://ygselect.com/web/product/big/202402/P0000NMY.jpg",
+                                                    "price",
+                                                    15000)),
+                                    "isLast",
+                                    true));
+
+            stubFindItemsByName(popupId, null, 5L, 4, 200, responseBody);
+
             // when
             SliceResponse<ItemInfoResponse> result =
-                    itemService.findAllItems(popupId, lastItemId, size);
+                    itemService.findItemsByName(popupId, null, lastItemId, size);
 
             // then
             Assertions.assertAll(
@@ -166,19 +165,108 @@ class 상품_목록_조회 {
                     () -> assertThat(result.isLast()).isTrue() // isLast=true 검증
                     );
         }
+
+        @Test
+        void 검색어에_대해_결과가_존재하면_상품_검색에_성공한다() throws JsonProcessingException {
+            // given
+            String searchName = "DAZED";
+            int size = 4;
+
+            String responseBody =
+                    objectMapper.writeValueAsString(
+                            Map.of(
+                                    "content",
+                                    List.of(
+                                            Map.of(
+                                                    "itemId",
+                                                    4,
+                                                    "name",
+                                                    "DAZED 리사",
+                                                    "imageUrl",
+                                                    "https://image.aladin.co.kr/product/17920/59/cover500/k142534034_1.jpg",
+                                                    "price",
+                                                    8000),
+                                            Map.of(
+                                                    "itemId",
+                                                    3,
+                                                    "name",
+                                                    "DAZED 제니",
+                                                    "imageUrl",
+                                                    "https://image.aladin.co.kr/product/28453/14/cover500/k572835617_1.jpg",
+                                                    "price",
+                                                    9500),
+                                            Map.of(
+                                                    "itemId",
+                                                    2,
+                                                    "name",
+                                                    "DAZED 로제",
+                                                    "imageUrl",
+                                                    "https://ygselect.com/web/product/big/202404/257d66b0a1eca57af67b6c792e68ed75.jpg",
+                                                    "price",
+                                                    15000),
+                                            Map.of(
+                                                    "itemId",
+                                                    1,
+                                                    "name",
+                                                    "DAZED 지수",
+                                                    "imageUrl",
+                                                    "https://ygselect.com/web/product/big/202402/P0000NMY.jpg",
+                                                    "price",
+                                                    15000)),
+                                    "isLast",
+                                    true));
+
+            stubFindItemsByName(popupId, searchName, null, 4, 200, responseBody);
+
+            // when
+            SliceResponse<ItemInfoResponse> result =
+                    itemService.findItemsByName(popupId, searchName, null, size);
+
+            // then
+            Assertions.assertAll(
+                    () -> assertThat(result).isNotNull(),
+                    () -> assertThat(result.content()).hasSize(4),
+                    () -> assertThat(result.isLast()).isTrue(),
+                    () ->
+                            assertThat(result.content())
+                                    .allSatisfy(
+                                            item ->
+                                                    assertThat(item.name())
+                                                            .contains("DAZED")) // 검색 결과 검증
+                    );
+        }
+
+        @Test
+        void 검색어에_대해_결과가_없으면_빈_리스트를_반환한다() throws JsonProcessingException {
+            // given
+            String searchName = "EMPTY";
+            int size = 4;
+
+            String emptyResponseBody =
+                    objectMapper.writeValueAsString(Map.of("content", List.of(), "isLast", true));
+
+            stubFindItemsByName(popupId, searchName, null, 4, 200, emptyResponseBody);
+
+            // when
+            SliceResponse<ItemInfoResponse> result =
+                    itemService.findItemsByName(popupId, searchName, null, size);
+
+            // then
+            Assertions.assertAll(
+                    () -> assertThat(result).isNotNull(),
+                    () -> assertThat(result.content()).isEmpty(), // 빈 리스트 검증
+                    () -> assertThat(result.isLast()).isTrue());
+        }
     }
 
-    private void stubFindAllItems(
-            Long popupId, Long lastItemId, int size, int status, String body) {
+    private void stubFindItemsByName(
+            Long popupId, String searchName, Long lastItemId, int size, int status, String body) {
         MappingBuilder mappingBuilder =
                 get(urlPathEqualTo("/internal/popups/" + popupId + "/items"))
                         .withQueryParam("size", equalTo(String.valueOf(size)));
 
-        if (lastItemId != null) {
-            mappingBuilder =
-                    mappingBuilder.withQueryParam(
-                            "lastItemId", equalTo(String.valueOf(lastItemId)));
-        }
+        mappingBuilder = applySearchNameIfPresent(mappingBuilder, searchName);
+        mappingBuilder = applyLastItemIdIfPresent(mappingBuilder, lastItemId);
 
         wireMockServer.stubFor(
                 mappingBuilder.willReturn(
@@ -187,4 +275,16 @@ private void stubFindAllItems(
                                 .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
                                 .withBody(body)));
     }
+
+    private MappingBuilder applySearchNameIfPresent(MappingBuilder builder, String searchName) {
+        return (searchName != null)
+                ? builder.withQueryParam("searchName", equalTo(searchName))
+                : builder;
+    }
+
+    private MappingBuilder applyLastItemIdIfPresent(MappingBuilder builder, Long lastItemId) {
+        return (lastItemId != null)
+                ? builder.withQueryParam("lastItemId", equalTo(String.valueOf(lastItemId)))
+                : builder;
+    }
 }
diff --git a/popi-popup-service/src/test/resources/application-test.yml b/popi-popup-service/src/test/resources/application-test.yml
index da5d88a8..fb329ff4 100644
--- a/popi-popup-service/src/test/resources/application-test.yml
+++ b/popi-popup-service/src/test/resources/application-test.yml
@@ -5,5 +5,5 @@ spring:
 
 manager:
   service:
-    name: popi-manager-service
+    name: managers
     url: http://localhost:${wiremock.server.port}
\ No newline at end of file


Reviews:
user: login: ohhyungsuh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T16:04:58Z
user: login: 0ssang
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-24T08:35:12Z
user: login: woogieon8on
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-24T08:41:05Z
user: login: ht3064
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T11:23:05Z
user: login: ht3064
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T11:24:14Z
user: login: woogieon8on
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T12:18:33Z
user: login: woogieon8on
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T12:18:53Z
user: login: ohhyungsuh
body: LGTM
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-25T14:14:49Z
user: login: 0ssang
body: LGTM 👍👍
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-26T00:33:55Z

Review comments:
diff_hunk: @@ -1,18 +1,23 @@
 package com.lgcns.client;
 
+import com.lgcns.config.FeignConfig;
 import com.lgcns.dto.item.response.ItemInfoResponse;
 import com.lgcns.response.SliceResponse;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestParam;
 
-@FeignClient(name = "popi-manager-service", url = "${manager-service-url:}")
+@FeignClient(
+        name = "popi-manager-service",
+        url = "${manager-service-url:}",
path: popi-popup-service/src/main/java/com/lgcns/client/ManagerServiceClient.java
user: login: ohhyungsuh
body: 영상이랑 같은 파일이라 중복되는 답변이긴한데, 제가 저 때 name과 url 둘 중 뭘 써야 로컬과 k8s 환경에서 동일하게 코드를 가져갈 수 있을지 혼동하던 시점입니다...
```
manager:
  service:
    name: ~
    url: ~
```
영상이랑 얘기해서 둘 중 한 명이 위와 같은 식으로 바꿔주시면 감사하겠습니다.
created_at: 2025-05-23T16:04:58Z
author_association: MEMBER
diff_hunk: @@ -1,18 +1,23 @@
 package com.lgcns.client;
 
+import com.lgcns.config.FeignConfig;
 import com.lgcns.dto.item.response.ItemInfoResponse;
 import com.lgcns.response.SliceResponse;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestParam;
 
-@FeignClient(name = "popi-manager-service", url = "${manager-service-url:}")
+@FeignClient(
+        name = "popi-manager-service",
+        url = "${manager-service-url:}",
path: popi-popup-service/src/main/java/com/lgcns/client/ManagerServiceClient.java
user: login: 0ssang
body: 제가 수정하도록 하겠습니다!!
created_at: 2025-05-24T08:35:12Z
author_association: MEMBER
diff_hunk: @@ -1,18 +1,23 @@
 package com.lgcns.client;
 
+import com.lgcns.config.FeignConfig;
 import com.lgcns.dto.item.response.ItemInfoResponse;
 import com.lgcns.response.SliceResponse;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestParam;
 
-@FeignClient(name = "popi-manager-service", url = "${manager-service-url:}")
+@FeignClient(
+        name = "popi-manager-service",
+        url = "${manager-service-url:}",
path: popi-popup-service/src/main/java/com/lgcns/client/ManagerServiceClient.java
user: login: woogieon8on
body: 영상님 감사합니다!!
created_at: 2025-05-24T08:41:05Z
author_association: MEMBER
diff_hunk: @@ -28,6 +28,8 @@ public class ItemServiceTest extends WireMockIntegrationTest {
 
     @BeforeEach
     void setUp() throws JsonProcessingException {
+        wireMockServer.resetAll();
+
path: popi-popup-service/src/test/java/com/lgcns/service/ItemServiceTest.java
user: login: ht3064
body: 해당 부분 제거해주세요~
created_at: 2025-05-25T11:23:05Z
author_association: MEMBER
diff_hunk: @@ -115,8 +117,14 @@ void setUp() throws JsonProcessingException {
                                                 15000)),
                                 "isLast",
                                 true));
-        stubFindAllItems(popupId, null, 4, 200, responseBody1);
-        stubFindAllItems(popupId, 5L, 4, 200, responseBody2);
+
+        String emptyResponseBody =
+                objectMapper.writeValueAsString(Map.of("content", List.of(), "isLast", true));
+
+        stubFindItemsByName(popupId, null, null, 4, 200, responseBody1);
+        stubFindItemsByName(popupId, null, 5L, 4, 200, responseBody2);
+        stubFindItemsByName(popupId, "DAZED", null, 4, 200, responseBody2);
+        stubFindItemsByName(popupId, "EMPTY", null, 4, 200, emptyResponseBody);
path: popi-popup-service/src/test/java/com/lgcns/service/ItemServiceTest.java
user: login: ht3064
body: WireMock의 stubFor 설정은 개별 테스트 메서드 내에서 해주시고 공지에 올라간 테스트 컨벤션 대로 전체적으로 수정 부탁드립니다!
created_at: 2025-05-25T11:24:14Z
author_association: MEMBER
diff_hunk: @@ -28,6 +28,8 @@ public class ItemServiceTest extends WireMockIntegrationTest {
 
     @BeforeEach
     void setUp() throws JsonProcessingException {
+        wireMockServer.resetAll();
+
path: popi-popup-service/src/test/java/com/lgcns/service/ItemServiceTest.java
user: login: woogieon8on
body: 확인했습니다!
created_at: 2025-05-25T12:18:32Z
author_association: MEMBER
diff_hunk: @@ -115,8 +117,14 @@ void setUp() throws JsonProcessingException {
                                                 15000)),
                                 "isLast",
                                 true));
-        stubFindAllItems(popupId, null, 4, 200, responseBody1);
-        stubFindAllItems(popupId, 5L, 4, 200, responseBody2);
+
+        String emptyResponseBody =
+                objectMapper.writeValueAsString(Map.of("content", List.of(), "isLast", true));
+
+        stubFindItemsByName(popupId, null, null, 4, 200, responseBody1);
+        stubFindItemsByName(popupId, null, 5L, 4, 200, responseBody2);
+        stubFindItemsByName(popupId, "DAZED", null, 4, 200, responseBody2);
+        stubFindItemsByName(popupId, "EMPTY", null, 4, 200, emptyResponseBody);
path: popi-popup-service/src/test/java/com/lgcns/service/ItemServiceTest.java
user: login: woogieon8on
body: 전체적으로 리팩토링해서 다시 올리겠습니당
created_at: 2025-05-25T12:18:53Z
author_association: MEMBER

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-05-26T00:42:33Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=popi-official_popi-user-server&pullRequest=47) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=popi-official_popi-user-server&pullRequest=47&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=popi-official_popi-user-server&pullRequest=47&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=popi-official_popi-user-server&pullRequest=47&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=popi-official_popi-user-server&pullRequest=47&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=popi-official_popi-user-server&pullRequest=47&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=popi-official_popi-user-server&pullRequest=47)


author_association: NONE