From 0f283a3a5b95ae6cb9bc243af374c89b8f4fb723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 17:48:29 +0900 Subject: [PATCH 01/18] [feat] FollowingJpaRepository.countByFollowingUserJpaEntity_UserId (#70) --- .../adapter/out/persistence/FollowingJpaRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingJpaRepository.java diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingJpaRepository.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingJpaRepository.java new file mode 100644 index 000000000..a1bfe86b5 --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingJpaRepository.java @@ -0,0 +1,8 @@ +package konkuk.thip.user.adapter.out.persistence; + +import konkuk.thip.user.adapter.out.jpa.FollowingJpaEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface FollowingJpaRepository extends JpaRepository { + int countByFollowingUserJpaEntity_UserId(Long userId); +} From 6de3b78805fbe0a53d951eac7cf22005b40ebffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 17:48:52 +0900 Subject: [PATCH 02/18] [feat] FollowingQueryPersistenceAdapter.countByFollowingUserId (#70) --- .../FollowingQueryPersistenceAdapter.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java new file mode 100644 index 000000000..d831bf652 --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java @@ -0,0 +1,17 @@ +package konkuk.thip.user.adapter.out.persistence; + +import konkuk.thip.user.application.port.out.FollowingQueryPort; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class FollowingQueryPersistenceAdapter implements FollowingQueryPort { + + private final FollowingJpaRepository followingJpaRepository; + + @Override + public int countByFollowingUserId(Long followingUserId) { + return followingJpaRepository.countByFollowingUserJpaEntity_UserId(followingUserId); + } +} From 13e89ee937ff8c7033dcfcb8bedcfe3b0cd868c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 17:49:01 +0900 Subject: [PATCH 03/18] [feat] FollowingQueryPort.countByFollowingUserId (#70) --- .../thip/user/application/port/out/FollowingQueryPort.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java diff --git a/src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java b/src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java new file mode 100644 index 000000000..dbdd1613f --- /dev/null +++ b/src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java @@ -0,0 +1,6 @@ +package konkuk.thip.user.application.port.out; + +public interface FollowingQueryPort { + int countByFollowingUserId(Long followingUserId); +} + From be4eb7724335d62c0310d710e86ca1e3c02ee5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 17:49:13 +0900 Subject: [PATCH 04/18] =?UTF-8?q?[feat]=20RoomGetMemberListResponse=20dto?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/RoomGetMemberListResponse.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/konkuk/thip/room/adapter/in/web/response/RoomGetMemberListResponse.java diff --git a/src/main/java/konkuk/thip/room/adapter/in/web/response/RoomGetMemberListResponse.java b/src/main/java/konkuk/thip/room/adapter/in/web/response/RoomGetMemberListResponse.java new file mode 100644 index 000000000..712a40dd6 --- /dev/null +++ b/src/main/java/konkuk/thip/room/adapter/in/web/response/RoomGetMemberListResponse.java @@ -0,0 +1,20 @@ +package konkuk.thip.room.adapter.in.web.response; + +import lombok.Builder; + +import java.util.List; + +@Builder +public record RoomGetMemberListResponse( + + List userList +){ + @Builder + public record MemberSearchResult( + Long userId, + String nickname, + String imageUrl, + String alias, + int subscriberCount + ) {} +} \ No newline at end of file From 8f28789ea413d91ebad574332c214473288beb65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 17:50:10 +0900 Subject: [PATCH 05/18] =?UTF-8?q?[feat]=20RoomGetMemberListUseCase=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4RoomGetMemberListService.getRoomMemb?= =?UTF-8?q?erList=20=EC=9E=91=EC=84=B1=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/RoomGetMemberListService.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java diff --git a/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java b/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java new file mode 100644 index 000000000..b1883526f --- /dev/null +++ b/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java @@ -0,0 +1,62 @@ +package konkuk.thip.room.application.service; + +import konkuk.thip.room.adapter.in.web.response.RoomGetMemberListResponse; +import konkuk.thip.room.application.port.in.RoomGetMemberListUseCase; +import konkuk.thip.room.application.port.out.RoomCommandPort; +import konkuk.thip.room.domain.Room; +import konkuk.thip.user.application.port.out.FollowingQueryPort; +import konkuk.thip.user.application.port.out.UserCommandPort; +import konkuk.thip.user.application.port.out.UserRoomCommandPort; +import konkuk.thip.user.domain.User; +import konkuk.thip.user.domain.UserRoom; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class RoomGetMemberListService implements RoomGetMemberListUseCase { + + private final RoomCommandPort roomCommandPort; + private final UserRoomCommandPort userRoomCommandPort; + private final UserCommandPort userCommandPort; + private final FollowingQueryPort followingQueryPort; + + @Override + public RoomGetMemberListResponse getRoomMemberList(Long roomId) { + + // 1. 방 정보 조회 + Room room = roomCommandPort.findById(roomId); + + // 2. 방 참여자(UserRoom) 전체 조회 + List userRooms = userRoomCommandPort.findAllByRoomId(roomId); + + // 3. 각 참여자의 userId로 유저정보, 구독자 수(팔로워 수) 조회 + List userList = userRooms.stream() + .map(userRoom -> { + + Long memberUserId = userRoom.getUserId(); + // 팔로워 수 = Following 테이블에서 followingUserId == memberUserId 인 row 개수 + int subscriberCount = followingQueryPort.countByFollowingUserId(memberUserId); + // 유저 정보 조회 + User user = userCommandPort.findById(memberUserId); + + return RoomGetMemberListResponse.MemberSearchResult.builder() + .userId(memberUserId) + .nickname(user.getNickname()) + .imageUrl(user.getAlias().getImageUrl()) + .alias(user.getAlias().getValue()) + .subscriberCount(subscriberCount) + .build(); + }) + .toList(); + + // 4. DTO 조립 후 반환 + return RoomGetMemberListResponse.builder() + .userList(userList) + .build(); + } + + +} From 9041b22921eda80e13b5226a754b9ab9e92f5953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 17:50:17 +0900 Subject: [PATCH 06/18] =?UTF-8?q?[feat]=20RoomGetMemberListUseCase=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../room/application/port/in/RoomGetMemberListUseCase.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/konkuk/thip/room/application/port/in/RoomGetMemberListUseCase.java diff --git a/src/main/java/konkuk/thip/room/application/port/in/RoomGetMemberListUseCase.java b/src/main/java/konkuk/thip/room/application/port/in/RoomGetMemberListUseCase.java new file mode 100644 index 000000000..8abda1f1e --- /dev/null +++ b/src/main/java/konkuk/thip/room/application/port/in/RoomGetMemberListUseCase.java @@ -0,0 +1,7 @@ +package konkuk.thip.room.application.port.in; + +import konkuk.thip.room.adapter.in.web.response.RoomGetMemberListResponse; + +public interface RoomGetMemberListUseCase { + RoomGetMemberListResponse getRoomMemberList(Long roomId); +} From f05874abd8d61bc777c729c01f4dda8696ecc4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 17:50:29 +0900 Subject: [PATCH 07/18] =?UTF-8?q?[feat]=20=EB=8F=85=EC=84=9C=EB=A9=94?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20=EC=9E=91=EC=84=B1=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/room/adapter/in/web/RoomQueryController.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java b/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java index 48e28536a..6f57e8ff7 100644 --- a/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java +++ b/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java @@ -1,7 +1,9 @@ package konkuk.thip.room.adapter.in.web; import konkuk.thip.common.dto.BaseResponse; +import konkuk.thip.room.adapter.in.web.response.RoomGetMemberListResponse; import konkuk.thip.room.adapter.in.web.response.RoomSearchResponse; +import konkuk.thip.room.application.port.in.RoomGetMemberListUseCase; import konkuk.thip.room.application.port.in.RoomSearchUseCase; import jakarta.validation.Valid; import konkuk.thip.room.adapter.in.web.request.RoomVerifyPasswordRequest; @@ -17,6 +19,7 @@ public class RoomQueryController { private final RoomSearchUseCase roomSearchUseCase; + private final RoomGetMemberListUseCase roomGetMemberListUseCase; @GetMapping("/rooms/search") public BaseResponse searchRooms( @@ -37,4 +40,10 @@ public BaseResponse verifyRoomPassword(@PathVariable("roomId") final Long return BaseResponse.ok(roomVerifyPasswordUseCase.verifyRoomPassword(roomVerifyPasswordRequest.toQuery(roomId))); } + // 독서메이트 조회 + @GetMapping("/rooms/{roomId}/users") + public BaseResponse getRoomMemberList(@PathVariable("roomId") final Long roomId){ + return BaseResponse.ok(roomGetMemberListUseCase.getRoomMemberList(roomId)); + } + } From 802fc6bdb2f889d79368fd2128b91336cb8d9b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 17:52:26 +0900 Subject: [PATCH 08/18] =?UTF-8?q?[refactor]=20=EB=A8=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/room/adapter/in/web/RoomQueryController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java b/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java index 78f35fa21..62ab323ea 100644 --- a/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java +++ b/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java @@ -36,7 +36,6 @@ public BaseResponse searchRooms( ) { return BaseResponse.ok(roomSearchUseCase.searchRoom(keyword, category, sort, page)); } - private final RoomVerifyPasswordUseCase roomVerifyPasswordUseCase; //비공개 방 비밀번호 입력 검증 @PostMapping("/rooms/{roomId}/password") From 7702387ef0025aeb17833418482c57a300772159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 18:23:54 +0900 Subject: [PATCH 09/18] =?UTF-8?q?[refactor]=20=EC=97=90=EB=9F=AC=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/konkuk/thip/common/exception/code/ErrorCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java index 8d363aa16..52b8953a3 100644 --- a/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java +++ b/src/main/java/konkuk/thip/common/exception/code/ErrorCode.java @@ -51,7 +51,7 @@ public enum ErrorCode implements ResponseCode { BOOK_KEYWORD_REQUIRED(HttpStatus.BAD_REQUEST, 80007, "검색어는 필수 입력값입니다."), BOOK_PAGE_NUMBER_INVALID(HttpStatus.BAD_REQUEST, 80008, "페이지 번호는 1 이상의 값이어야 합니다."), BOOK_NAVER_API_ISBN_NOT_FOUND(HttpStatus.BAD_REQUEST, 80009, "네이버 API 에서 ISBN으로 검색한 결과가 존재하지 않습니다."), - BOOK_NOT_FOUND(HttpStatus.BAD_REQUEST, 80010, "존재하지 않는 BOOK 입니다."), + BOOK_NOT_FOUND(HttpStatus.NOT_FOUND, 80010, "존재하지 않는 BOOK 입니다."), BOOK_ALREADY_SAVED(HttpStatus.BAD_REQUEST, 80011, "사용자가 이미 저장한 책입니다."), DUPLICATED_BOOKS_IN_COLLECTION(HttpStatus.INTERNAL_SERVER_ERROR, 80012, "중복된 책이 존재합니다."), BOOK_NOT_SAVED_CANNOT_DELETE(HttpStatus.BAD_REQUEST, 80013, "사용자가 저장하지 않은 책은 저장삭제 할 수 없습니다."), From 5f8ac0cf043dc4f0d2f203a8082799a67ae6066b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 18:24:29 +0900 Subject: [PATCH 10/18] =?UTF-8?q?[test]=20=ED=86=B5=ED=95=A9=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/web/RoomGetMemberListApiTest.java | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java new file mode 100644 index 000000000..40d645f0f --- /dev/null +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java @@ -0,0 +1,189 @@ +package konkuk.thip.room.adapter.in.web; + +import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.BookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; +import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; +import konkuk.thip.room.adapter.out.persistence.CategoryJpaRepository; +import konkuk.thip.room.adapter.out.persistence.RoomJpaRepository; +import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.jpa.UserRoomRole; +import konkuk.thip.user.adapter.out.persistence.AliasJpaRepository; +import konkuk.thip.user.adapter.out.persistence.FollowingJpaRepository; +import konkuk.thip.user.adapter.out.persistence.UserJpaRepository; +import konkuk.thip.user.adapter.out.persistence.UserRoomJpaRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ActiveProfiles("test") +@AutoConfigureMockMvc(addFilters = false) +@DisplayName("[통합] 독서 메이트(방 멤버) 조회 api 통합 테스트") +class RoomGetMemberListApiTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private AliasJpaRepository aliasJpaRepository; + + @Autowired + private UserJpaRepository userJpaRepository; + + @Autowired + private CategoryJpaRepository categoryJpaRepository; + + @Autowired + private BookJpaRepository bookJpaRepository; + + @Autowired + private RoomJpaRepository roomJpaRepository; + + @Autowired + private UserRoomJpaRepository userRoomJpaRepository; + + @Autowired + private FollowingJpaRepository followingJpaRepository; + + private RoomJpaEntity room1; + private UserJpaEntity user1; + private UserJpaEntity user2; + private UserJpaEntity user3; + private BookJpaEntity book; + private CategoryJpaEntity category; + + @BeforeEach + void setUp() { + AliasJpaEntity alias = TestEntityFactory.createLiteratureAlias(); + aliasJpaRepository.save(alias); + + user1 = userJpaRepository.save(TestEntityFactory.createUser(alias)); + user2 = userJpaRepository.save(TestEntityFactory.createUser(alias)); + user3 = userJpaRepository.save(TestEntityFactory.createUser(alias)); + + book = bookJpaRepository.save(TestEntityFactory.createBook()); + category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); + + room1 = roomJpaRepository.save(TestEntityFactory.createRoom(book, category)); + + // 유저1(호스트), 유저2(멤버), 유저3(멤버)로 참여 + userRoomJpaRepository.save(TestEntityFactory.createUserRoom(room1, user1, UserRoomRole.HOST, 80.0)); + userRoomJpaRepository.save(TestEntityFactory.createUserRoom(room1, user2, UserRoomRole.MEMBER, 60.0)); + userRoomJpaRepository.save(TestEntityFactory.createUserRoom(room1, user3, UserRoomRole.MEMBER, 50.0)); + + + // 팔로잉 관계 설정 + // user1이 user2, user3을 팔로우 + followingJpaRepository.save(TestEntityFactory.createFollowing(user1, user2)); + followingJpaRepository.save(TestEntityFactory.createFollowing(user1, user3)); + // user2가 user3을 팔로우 + followingJpaRepository.save(TestEntityFactory.createFollowing(user2, user3)); + // user3이 user1을 팔로우 + followingJpaRepository.save(TestEntityFactory.createFollowing(user3, user1)); + } + + @AfterEach + void tearDown() { + followingJpaRepository.deleteAll(); + userRoomJpaRepository.deleteAll(); + roomJpaRepository.deleteAll(); + bookJpaRepository.deleteAll(); + userJpaRepository.deleteAll(); + categoryJpaRepository.deleteAll(); + aliasJpaRepository.deleteAll(); + } + + @Test + @DisplayName("방 멤버 리스트(독서메이트)가 userId, nickname, imageUrl, alias, subscriberCount로 조회된다.") + void getRoomMemberList_success() throws Exception { + //given + //room1에 user1,user2,user3가 참여 + Long roomId = room1.getRoomId(); + + //when + ResultActions result = mockMvc.perform(get("/rooms/{roomId}/users", roomId)); + + //then + result.andExpect(status().isOk()) + .andExpect(jsonPath("$.data.userList", hasSize(3))) + .andExpect(jsonPath("$.data.userList[0].userId").value(user1.getUserId().intValue())) + .andExpect(jsonPath("$.data.userList[0].nickname").exists()) + .andExpect(jsonPath("$.data.userList[0].imageUrl").exists()) + .andExpect(jsonPath("$.data.userList[0].alias").exists()) + .andExpect(jsonPath("$.data.userList[0].subscriberCount").isNumber()) + .andExpect(jsonPath("$.data.userList[1].userId").value(user2.getUserId().intValue())) + .andExpect(jsonPath("$.data.userList[1].nickname").exists()) + .andExpect(jsonPath("$.data.userList[1].imageUrl").exists()) + .andExpect(jsonPath("$.data.userList[1].alias").exists()) + .andExpect(jsonPath("$.data.userList[1].subscriberCount").isNumber()) + .andExpect(jsonPath("$.data.userList[2].userId").value(user3.getUserId().intValue())) + .andExpect(jsonPath("$.data.userList[2].nickname").exists()) + .andExpect(jsonPath("$.data.userList[2].imageUrl").exists()) + .andExpect(jsonPath("$.data.userList[2].alias").exists()) + .andExpect(jsonPath("$.data.userList[2].subscriberCount").isNumber()); + } + + @Test + @DisplayName("방에 멤버가 없으면 빈 리스트를 반환한다.") + void getRoomMemberList_empty() throws Exception { + //given + RoomJpaEntity emptyRoom = roomJpaRepository.save(TestEntityFactory.createRoom(book, category)); + + //when + ResultActions result = mockMvc.perform(get("/rooms/{roomId}/users", emptyRoom.getRoomId())); + + //then + result.andExpect(status().isOk()) + .andExpect(jsonPath("$.data.userList", hasSize(0))); + } + + @Test + @DisplayName("팔로워(구독자) 수가 올바르게 집계된다.") + void getRoomMemberList_subscriberCount() throws Exception { + //given + Long roomId = room1.getRoomId(); + + //when + ResultActions result = mockMvc.perform(get("/rooms/{roomId}/users", roomId)); + + //then + // user1: user3이 팔로우(1명) + // user2: user1이 팔로우(1명) + // user3: user1, user2가 팔로우(2명) + result.andExpect(status().isOk()) + .andExpect(jsonPath("$.data.userList[?(@.userId==" + user1.getUserId() + ")].subscriberCount").value(contains(1))) + .andExpect(jsonPath("$.data.userList[?(@.userId==" + user2.getUserId() + ")].subscriberCount").value(contains(1))) + .andExpect(jsonPath("$.data.userList[?(@.userId==" + user3.getUserId() + ")].subscriberCount").value(contains(2))); + } + + @Test + @DisplayName("팔로워가 한 명도 없는 사용자는 subscriberCount가 0으로 조회된다.") + void getRoomMemberList_noSubscriber() throws Exception { + //given + UserJpaEntity userNoFollower = userJpaRepository.save(TestEntityFactory.createUser(aliasJpaRepository.findAll().get(0))); + userRoomJpaRepository.save(TestEntityFactory.createUserRoom(room1, userNoFollower, UserRoomRole.MEMBER, 10.0)); + Long roomId = room1.getRoomId(); + + //when + ResultActions result = mockMvc.perform(get("/rooms/{roomId}/users", roomId)); + + //then + result.andExpect(status().isOk()) + .andExpect(jsonPath("$.data.userList[?(@.userId==" + userNoFollower.getUserId() + ")].subscriberCount").value(contains(0))); + } +} \ No newline at end of file From e1d1d06b014936fb02dd0bae4bc64c05b324c64c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 18:24:38 +0900 Subject: [PATCH 11/18] =?UTF-8?q?[test]=20=EB=8B=A8=EC=9C=84=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/RoomGetMemberListControllerTest.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListControllerTest.java diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListControllerTest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListControllerTest.java new file mode 100644 index 000000000..ca2764d40 --- /dev/null +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListControllerTest.java @@ -0,0 +1,73 @@ +package konkuk.thip.room.adapter.in.web; + +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.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.HashMap; +import java.util.Map; + +import static konkuk.thip.common.exception.code.ErrorCode.*; +import static org.hamcrest.Matchers.containsString; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ActiveProfiles("test") +@AutoConfigureMockMvc(addFilters = false) +@DisplayName("[단위] 독서메이트(방 멤버) 조회 api controller 테스트") +class RoomGetMemberListControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + private Map buildValidRequest() { + Map request = new HashMap<>(); + request.put("roomId", 1L); + return request; + } + + private void assertBad(Map req, String msg) throws Exception { + mockMvc.perform(get("/rooms/{roomId}/users", req.get("roomId")) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.code").value(ROOM_NOT_FOUND.getCode())) + .andExpect(jsonPath("$.message", containsString(msg))); + } + + @Nested + @DisplayName("roomId 검증") + class RoomIdValidation { + + @Test + @DisplayName("roomId가 없을 때 400 error") + void missing_roomId() throws Exception { + Map req = buildValidRequest(); + req.remove("roomId"); + mockMvc.perform(get("/rooms//users") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(req))) + .andExpect(status().is4xxClientError()); + } + + @Test + @DisplayName("DB에 존재하지 않는 roomId가 들어오면 400 error") + void not_found_roomId() throws Exception { + Map req = buildValidRequest(); + req.remove("roomId"); + req.put("roomId", 99999L); + assertBad(req, "존재하지 않는 ROOM 입니다."); + } + } +} From 7f1eab31758dfa68cb68b79fc79f57a81c14403a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 18:24:47 +0900 Subject: [PATCH 12/18] =?UTF-8?q?[test]=20=EB=8D=94=EB=AF=B8=20=ED=8C=94?= =?UTF-8?q?=EB=A1=9C=EC=9E=89=20=EC=83=9D=EC=84=B1=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/konkuk/thip/common/util/TestEntityFactory.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java index ffd760b10..ae2bd23d9 100644 --- a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java +++ b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java @@ -133,4 +133,11 @@ public static CommentJpaEntity createComment(PostJpaEntity post, UserJpaEntity u .userJpaEntity(user) .build(); } + + public static FollowingJpaEntity createFollowing(UserJpaEntity user,UserJpaEntity followingUser) { + return FollowingJpaEntity.builder() + .userJpaEntity(user) + .followingUserJpaEntity(followingUser) + .build(); + } } \ No newline at end of file From 09a432465eeea800f8d8977c964b694e5b8d364c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 18:33:47 +0900 Subject: [PATCH 13/18] =?UTF-8?q?[fix]=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../room/application/service/RoomGetMemberListService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java b/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java index b1883526f..6e1e7b406 100644 --- a/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java +++ b/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java @@ -26,11 +26,11 @@ public class RoomGetMemberListService implements RoomGetMemberListUseCase { @Override public RoomGetMemberListResponse getRoomMemberList(Long roomId) { - // 1. 방 정보 조회 + // 1. 방 검증 및 방 조회 Room room = roomCommandPort.findById(roomId); // 2. 방 참여자(UserRoom) 전체 조회 - List userRooms = userRoomCommandPort.findAllByRoomId(roomId); + List userRooms = userRoomCommandPort.findAllByRoomId(room.getId()); // 3. 각 참여자의 userId로 유저정보, 구독자 수(팔로워 수) 조회 List userList = userRooms.stream() From 24051bdbdda3b2aaa17e4c56cfe7ea53e94a4847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 18:34:43 +0900 Subject: [PATCH 14/18] =?UTF-8?q?[refactor]=20=ED=8A=B8=EB=9E=9C=EC=9E=AD?= =?UTF-8?q?=EC=85=98=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/room/application/service/RoomGetMemberListService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java b/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java index 6e1e7b406..760cb5890 100644 --- a/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java +++ b/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java @@ -11,6 +11,7 @@ import konkuk.thip.user.domain.UserRoom; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -24,6 +25,7 @@ public class RoomGetMemberListService implements RoomGetMemberListUseCase { private final FollowingQueryPort followingQueryPort; @Override + @Transactional(readOnly = true) public RoomGetMemberListResponse getRoomMemberList(Long roomId) { // 1. 방 검증 및 방 조회 From 10779952eb01343a831293be38ae6484ac2b43da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 13 Jul 2025 18:47:06 +0900 Subject: [PATCH 15/18] =?UTF-8?q?[test]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/RoomGetMemberListControllerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListControllerTest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListControllerTest.java index ca2764d40..ea4eeb2a5 100644 --- a/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListControllerTest.java +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListControllerTest.java @@ -38,7 +38,7 @@ private Map buildValidRequest() { return request; } - private void assertBad(Map req, String msg) throws Exception { + private void assertRoomNotFound(Map req, String msg) throws Exception { mockMvc.perform(get("/rooms/{roomId}/users", req.get("roomId")) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()) @@ -62,12 +62,12 @@ void missing_roomId() throws Exception { } @Test - @DisplayName("DB에 존재하지 않는 roomId가 들어오면 400 error") + @DisplayName("DB에 존재하지 않는 roomId가 들어오면 404 error") void not_found_roomId() throws Exception { Map req = buildValidRequest(); req.remove("roomId"); req.put("roomId", 99999L); - assertBad(req, "존재하지 않는 ROOM 입니다."); + assertRoomNotFound(req, "존재하지 않는 ROOM 입니다."); } } } From 4572dea99fccf29d9ad31ea36ad5310225c56214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Mon, 14 Jul 2025 13:34:29 +0900 Subject: [PATCH 16/18] =?UTF-8?q?[refactor]=20=EB=B0=B0=EC=B9=98=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/RoomGetMemberListService.java | 26 ++++++++----- .../persistence/FollowingJpaRepository.java | 3 +- .../FollowingQueryPersistenceAdapter.java | 9 ++++- .../persistence/FollowingQueryRepository.java | 8 ++++ .../FollowingQueryRepositoryImpl.java | 38 +++++++++++++++++++ .../UserCommandPersistenceAdapter.java | 13 +++++++ .../port/out/FollowingQueryPort.java | 5 ++- .../application/port/out/UserCommandPort.java | 4 ++ 8 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryRepository.java create mode 100644 src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryRepositoryImpl.java diff --git a/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java b/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java index 760cb5890..6227ea0e5 100644 --- a/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java +++ b/src/main/java/konkuk/thip/room/application/service/RoomGetMemberListService.java @@ -14,6 +14,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Map; @Service @RequiredArgsConstructor @@ -34,18 +35,25 @@ public RoomGetMemberListResponse getRoomMemberList(Long roomId) { // 2. 방 참여자(UserRoom) 전체 조회 List userRooms = userRoomCommandPort.findAllByRoomId(room.getId()); - // 3. 각 참여자의 userId로 유저정보, 구독자 수(팔로워 수) 조회 + + // 3. 참여자 userId 목록 추출 + List userIds = userRooms.stream() + .map(UserRoom::getUserId) + .toList(); + + // 4. 배치 쿼리로 유저 정보, 팔로워 수 조회 + Map userMap = userCommandPort.findByIds(userIds); + Map subscriberCountMap = followingQueryPort.countByFollowingUserIds(userIds); + + // 5. 각 userRoom에 대해 DTO 조립 List userList = userRooms.stream() .map(userRoom -> { - - Long memberUserId = userRoom.getUserId(); - // 팔로워 수 = Following 테이블에서 followingUserId == memberUserId 인 row 개수 - int subscriberCount = followingQueryPort.countByFollowingUserId(memberUserId); - // 유저 정보 조회 - User user = userCommandPort.findById(memberUserId); + Long userId = userRoom.getUserId(); + User user = userMap.get(userId); + int subscriberCount = subscriberCountMap.getOrDefault(userId, 0); return RoomGetMemberListResponse.MemberSearchResult.builder() - .userId(memberUserId) + .userId(userId) .nickname(user.getNickname()) .imageUrl(user.getAlias().getImageUrl()) .alias(user.getAlias().getValue()) @@ -54,7 +62,7 @@ public RoomGetMemberListResponse getRoomMemberList(Long roomId) { }) .toList(); - // 4. DTO 조립 후 반환 + // 6. DTO 반환 return RoomGetMemberListResponse.builder() .userList(userList) .build(); diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingJpaRepository.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingJpaRepository.java index a1bfe86b5..f0ded99ef 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingJpaRepository.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingJpaRepository.java @@ -3,6 +3,5 @@ import konkuk.thip.user.adapter.out.jpa.FollowingJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; -public interface FollowingJpaRepository extends JpaRepository { - int countByFollowingUserJpaEntity_UserId(Long userId); +public interface FollowingJpaRepository extends JpaRepository,FollowingQueryRepository { } diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java index d831bf652..b27b6eca6 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java @@ -1,17 +1,22 @@ package konkuk.thip.user.adapter.out.persistence; +import konkuk.thip.user.adapter.out.mapper.FollowingMapper; import konkuk.thip.user.application.port.out.FollowingQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Map; + @Repository @RequiredArgsConstructor public class FollowingQueryPersistenceAdapter implements FollowingQueryPort { private final FollowingJpaRepository followingJpaRepository; + private final FollowingMapper followingMapper; @Override - public int countByFollowingUserId(Long followingUserId) { - return followingJpaRepository.countByFollowingUserJpaEntity_UserId(followingUserId); + public Map countByFollowingUserIds(List userIds) { + return followingJpaRepository.countByFollowingUserIds(userIds); } } diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryRepository.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryRepository.java new file mode 100644 index 000000000..118cc8a53 --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryRepository.java @@ -0,0 +1,8 @@ +package konkuk.thip.user.adapter.out.persistence; + +import java.util.List; +import java.util.Map; + +public interface FollowingQueryRepository { + Map countByFollowingUserIds(List userIds); +} diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryRepositoryImpl.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryRepositoryImpl.java new file mode 100644 index 000000000..cec44bc76 --- /dev/null +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryRepositoryImpl.java @@ -0,0 +1,38 @@ +package konkuk.thip.user.adapter.out.persistence; + +import com.querydsl.core.Tuple; +import com.querydsl.jpa.impl.JPAQueryFactory; +import konkuk.thip.user.adapter.out.jpa.QFollowingJpaEntity; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Repository +@RequiredArgsConstructor +public class FollowingQueryRepositoryImpl implements FollowingQueryRepository { + + private final JPAQueryFactory jpaQueryFactory; + + // 주어진 userId 리스트에 대해 각 userId의 팔로워(구독자) 수를 집계하여 Map으로 반환 + public Map countByFollowingUserIds(List userIds) { + + QFollowingJpaEntity following = QFollowingJpaEntity.followingJpaEntity; + + List results = jpaQueryFactory + .select(following.followingUserJpaEntity.userId, following.count()) + .from(following) + .where(following.followingUserJpaEntity.userId.in(userIds)) + .groupBy(following.followingUserJpaEntity.userId) + .fetch(); + + // 결과를 Map로 변환 + return results.stream() + .collect(Collectors.toMap( + tuple -> tuple.get(following.followingUserJpaEntity.userId), + tuple -> tuple.get(following.count()).intValue() + )); + } +} diff --git a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java index 6909bf929..6f819812d 100644 --- a/src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/user/adapter/out/persistence/UserCommandPersistenceAdapter.java @@ -9,6 +9,11 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + import static konkuk.thip.common.exception.code.ErrorCode.ALIAS_NOT_FOUND; import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; @@ -37,4 +42,12 @@ public User findById(Long userId) { return userMapper.toDomainEntity(userJpaEntity); } + + @Override + public Map findByIds(List userIds) { + List entities = userJpaRepository.findAllById(userIds); + return entities.stream() + .map(userMapper::toDomainEntity) + .collect(Collectors.toMap(User::getId, Function.identity())); + } } diff --git a/src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java b/src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java index dbdd1613f..ea40bd1ac 100644 --- a/src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java +++ b/src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java @@ -1,6 +1,9 @@ package konkuk.thip.user.application.port.out; +import java.util.List; +import java.util.Map; + public interface FollowingQueryPort { - int countByFollowingUserId(Long followingUserId); + Map countByFollowingUserIds(List userIds); } diff --git a/src/main/java/konkuk/thip/user/application/port/out/UserCommandPort.java b/src/main/java/konkuk/thip/user/application/port/out/UserCommandPort.java index c196f63b6..cac05e1b6 100644 --- a/src/main/java/konkuk/thip/user/application/port/out/UserCommandPort.java +++ b/src/main/java/konkuk/thip/user/application/port/out/UserCommandPort.java @@ -2,8 +2,12 @@ import konkuk.thip.user.domain.User; +import java.util.List; +import java.util.Map; + public interface UserCommandPort { Long save(User user); User findById(Long userId); + Map findByIds(List userIds); } From 59991ff8c494854bd0e823928dbe3519e6aea0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Mon, 14 Jul 2025 13:55:14 +0900 Subject: [PATCH 17/18] =?UTF-8?q?[refactor]=20=EC=B6=A9=EB=8F=8C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/room/adapter/in/web/RoomQueryController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java b/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java index 7cb4c00d6..6ffa7db3b 100644 --- a/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java +++ b/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java @@ -28,7 +28,6 @@ public class RoomQueryController { private final RoomGetHomeJoinedListUseCase roomGetHomeJoinedListUseCase; private final RoomVerifyPasswordUseCase roomVerifyPasswordUseCase; private final RoomShowRecruitingDetailViewUseCase roomShowRecruitingDetailViewUseCase; - private final RoomGetHomeJoinedListUseCase roomGetHomeJoinedListUseCase; private final RoomGetMemberListUseCase roomGetMemberListUseCase; @GetMapping("/rooms/search") From 7598586465b36fc45c66c3654523a8104c155fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Mon, 14 Jul 2025 13:56:30 +0900 Subject: [PATCH 18/18] =?UTF-8?q?[remove]=20=EC=95=88=EC=93=B0=EB=8A=94=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/test/TestExceptionController.java | 50 ------------------- 1 file changed, 50 deletions(-) delete mode 100644 src/main/java/konkuk/thip/test/TestExceptionController.java diff --git a/src/main/java/konkuk/thip/test/TestExceptionController.java b/src/main/java/konkuk/thip/test/TestExceptionController.java deleted file mode 100644 index a89d1f1a9..000000000 --- a/src/main/java/konkuk/thip/test/TestExceptionController.java +++ /dev/null @@ -1,50 +0,0 @@ -package konkuk.thip.test; - -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import konkuk.thip.common.exception.EntityNotFoundException; -import konkuk.thip.common.exception.code.ErrorCode; -import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequestMapping("/test") -public class TestExceptionController { - - @GetMapping("/method-not-allowed") - public void methodNotAllowed() throws HttpRequestMethodNotSupportedException { - throw new HttpRequestMethodNotSupportedException("POST", List.of("GET")); - } - - @GetMapping("/invalid-param") - public void invalidParam(@Valid @RequestBody DummyRequest request) { - // 유효성 검사 실패 유도 - } - - @GetMapping("/type-mismatch") - public void typeMismatch(@RequestParam Integer id) { - // id에 문자열 전달 시 예외 발생 - } - - @GetMapping("/missing-param") - public void missingParam(@RequestParam String requiredParam) { - // 파라미터 누락 시 예외 발생 - } - - @GetMapping("/business") - public void business() { - throw new EntityNotFoundException(ErrorCode.API_BAD_REQUEST); - } - - @GetMapping("/runtime") - public void runtime() { - throw new IllegalStateException("서버 내부 오류"); - } - - public static class DummyRequest { - @NotBlank - public String name; - } -}