diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/space/membership/controller/ApiV1InviteController.java b/src/main/java/org/tuna/zoopzoop/backend/domain/space/membership/controller/ApiV1InviteController.java index 5309da40..5543d697 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/space/membership/controller/ApiV1InviteController.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/space/membership/controller/ApiV1InviteController.java @@ -10,7 +10,7 @@ import org.tuna.zoopzoop.backend.domain.space.membership.service.MembershipService; import org.tuna.zoopzoop.backend.domain.space.space.dto.res.ResBodyForSpaceInviteList; import org.tuna.zoopzoop.backend.domain.space.space.dto.res.ResBodyForSpaceSave; -import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceMembershipInfoWithoutAuthority; +import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceInfoWithoutAuthority; import org.tuna.zoopzoop.backend.domain.space.space.service.SpaceService; import org.tuna.zoopzoop.backend.global.rsData.RsData; import org.tuna.zoopzoop.backend.global.security.jwt.CustomUserDetails; @@ -81,8 +81,8 @@ public RsData getMyInvites( // 멤버십(초대) 목록 조회 List invitations = membershipService.findByMember(member, "PENDING"); - List invitationInfos = invitations.stream() - .map(membership -> new SpaceMembershipInfoWithoutAuthority( + List invitationInfos = invitations.stream() + .map(membership -> new SpaceInfoWithoutAuthority( membership.getSpace().getId(), membership.getSpace().getName(), membership.getSpace().getThumbnailUrl() diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/controller/ApiV1SpaceController.java b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/controller/ApiV1SpaceController.java index f6a96d17..0fec753c 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/controller/ApiV1SpaceController.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/controller/ApiV1SpaceController.java @@ -9,18 +9,17 @@ import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.tuna.zoopzoop.backend.domain.member.entity.Member; +import org.tuna.zoopzoop.backend.domain.space.membership.dto.etc.SpaceMemberInfo; import org.tuna.zoopzoop.backend.domain.space.membership.entity.Membership; import org.tuna.zoopzoop.backend.domain.space.membership.enums.Authority; import org.tuna.zoopzoop.backend.domain.space.membership.enums.JoinState; import org.tuna.zoopzoop.backend.domain.space.membership.service.MembershipService; import org.tuna.zoopzoop.backend.domain.space.space.dto.req.ReqBodyForSpaceSave; import org.tuna.zoopzoop.backend.domain.space.space.dto.res.ResBodyForSpaceInfo; -import org.tuna.zoopzoop.backend.domain.space.space.dto.res.ResBodyForSpaceList; -import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceMembershipInfo; +import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceInfo; import org.tuna.zoopzoop.backend.domain.space.space.dto.res.ResBodyForSpaceListPage; import org.tuna.zoopzoop.backend.domain.space.space.dto.res.ResBodyForSpaceSave; import org.tuna.zoopzoop.backend.domain.space.space.entity.Space; @@ -30,7 +29,6 @@ import java.nio.file.AccessDeniedException; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; @RestController @@ -131,6 +129,7 @@ public RsData updateSpaceThumbnail( public RsData getAllSpaces( @AuthenticationPrincipal CustomUserDetails userDetails, @RequestParam(required = false) JoinState state, + @RequestParam(defaultValue = "false") boolean includeMembers, @PageableDefault(size = 10, sort = "createDate", direction = Sort.Direction.DESC) Pageable pageable ) { // 현재 로그인한 사용자 정보 가져오기 @@ -141,13 +140,32 @@ public RsData getAllSpaces( Page membershipsPage = membershipService.findByMember(member, stateStr, pageable); // Page를 Page로 변환 - // Page 객체의 map() 메서드를 사용하면 페이징 정보는 그대로 유지하면서 내용물만 쉽게 바꿀 수 있습니다. - Page spaceInfosPage = membershipsPage.map(membership -> new SpaceMembershipInfo( - membership.getSpace().getId(), - membership.getSpace().getName(), - membership.getSpace().getThumbnailUrl(), - membership.getAuthority() - )); + Page spaceInfosPage = membershipsPage.map(membership -> { + Space space = membership.getSpace(); + List memberInfos = null; + + if (includeMembers) { + // 스페이스에 속한 멤버 목록 조회 (가입 상태만) + List spaceMemberships = membershipService.findMembersBySpace(space); + // 멤버 목록을 DTO로 변환 + memberInfos = spaceMemberships.stream() + .map(spaceMembership -> new SpaceMemberInfo( + spaceMembership.getMember().getId(), + spaceMembership.getMember().getName(), + spaceMembership.getMember().getProfileImageUrl(), + spaceMembership.getAuthority() + )) + .collect(Collectors.toList()); + } + + return new SpaceInfo( + space.getId(), + space.getName(), + space.getThumbnailUrl(), + membership.getAuthority(), + memberInfos // 조회된 멤버 목록 (null일 수도 있음) + ); + }); // 새로운 응답 DTO 생성 ResBodyForSpaceListPage resBody = new ResBodyForSpaceListPage(spaceInfosPage); diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceInfo.java b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceInfo.java new file mode 100644 index 00000000..dcfad54b --- /dev/null +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceInfo.java @@ -0,0 +1,20 @@ +package org.tuna.zoopzoop.backend.domain.space.space.dto.etc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.tuna.zoopzoop.backend.domain.space.membership.dto.etc.SpaceMemberInfo; +import org.tuna.zoopzoop.backend.domain.space.membership.enums.Authority; + +import java.util.List; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public record SpaceInfo( + Integer id, + String name, + String thumbnailUrl, + Authority authority, + List members +) { + public SpaceInfo(Integer id, String name, String thumbnailUrl, Authority authority) { + this(id, name, thumbnailUrl, authority, null); + } +} diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceMembershipInfoWithoutAuthority.java b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceInfoWithoutAuthority.java similarity index 73% rename from src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceMembershipInfoWithoutAuthority.java rename to src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceInfoWithoutAuthority.java index f5452b40..8e96d59e 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceMembershipInfoWithoutAuthority.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceInfoWithoutAuthority.java @@ -1,6 +1,6 @@ package org.tuna.zoopzoop.backend.domain.space.space.dto.etc; -public record SpaceMembershipInfoWithoutAuthority( +public record SpaceInfoWithoutAuthority( Integer id, String name, String thumbnailUrl diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceMembershipInfo.java b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceMembershipInfo.java deleted file mode 100644 index 1ee8ec16..00000000 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/etc/SpaceMembershipInfo.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.tuna.zoopzoop.backend.domain.space.space.dto.etc; - -import org.tuna.zoopzoop.backend.domain.space.membership.enums.Authority; - -public record SpaceMembershipInfo( - Integer id, - String name, - String thumbnailUrl, - Authority authority -) { -} diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceInviteList.java b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceInviteList.java index 40a0e83e..2e813f33 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceInviteList.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceInviteList.java @@ -1,10 +1,10 @@ package org.tuna.zoopzoop.backend.domain.space.space.dto.res; -import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceMembershipInfoWithoutAuthority; +import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceInfoWithoutAuthority; import java.util.List; public record ResBodyForSpaceInviteList( - List spaces + List spaces ) { } diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceList.java b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceList.java index de2cf46c..ba609cb6 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceList.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceList.java @@ -1,10 +1,10 @@ package org.tuna.zoopzoop.backend.domain.space.space.dto.res; -import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceMembershipInfo; +import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceInfo; import java.util.List; public record ResBodyForSpaceList( - List spaces + List spaces ) { } diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceListPage.java b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceListPage.java index 4e3f1dd4..dad04a11 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceListPage.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/space/space/dto/res/ResBodyForSpaceListPage.java @@ -2,20 +2,20 @@ import lombok.Getter; import org.springframework.data.domain.Page; -import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceMembershipInfo; +import org.tuna.zoopzoop.backend.domain.space.space.dto.etc.SpaceInfo; import java.util.List; @Getter public class ResBodyForSpaceListPage { - private final List spaces; // 현재 페이지의 데이터 + private final List spaces; // 현재 페이지의 데이터 private final int page; // 현재 페이지 번호 (0부터 시작) private final int size; // 페이지 크기 private final long totalElements; // 전체 요소 수 private final int totalPages; // 전체 페이지 수 private final boolean isLast; // 마지막 페이지 여부 - public ResBodyForSpaceListPage(Page page) { + public ResBodyForSpaceListPage(Page page) { this.spaces = page.getContent(); this.page = page.getNumber(); this.size = page.getSize(); diff --git a/src/test/java/org/tuna/zoopzoop/backend/domain/space/space/controller/ApiV1SpaceControllerTest.java b/src/test/java/org/tuna/zoopzoop/backend/domain/space/space/controller/ApiV1SpaceControllerTest.java index 6bca9b02..eb3fb736 100644 --- a/src/test/java/org/tuna/zoopzoop/backend/domain/space/space/controller/ApiV1SpaceControllerTest.java +++ b/src/test/java/org/tuna/zoopzoop/backend/domain/space/space/controller/ApiV1SpaceControllerTest.java @@ -18,6 +18,7 @@ import org.tuna.zoopzoop.backend.testSupport.ControllerTestSupport; import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.startsWith; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -81,6 +82,12 @@ void setUpMembership() { spaceService.findByName("기존 스페이스 1_forSpaceControllerTest"), Authority.PENDING ); + // test3 -> 스페이스 1 가입 (READ_ONLY) + membershipService.addMemberToSpace( + memberService.findByKakaoKey("sc3333"), + spaceService.findByName("기존 스페이스 1_forSpaceControllerTest"), + Authority.READ_ONLY + ); // test1 -> 스페이스 2 가입 (PENDING) membershipService.addMemberToSpace( memberService.findByKakaoKey("sc1111"), @@ -408,6 +415,45 @@ void getMySpaces_Success() throws Exception { .andExpect(jsonPath("$.data.spaces[1].thumbnailUrl").value("thumbnailUrl2")); } + @Test + @WithUserDetails(value = "KAKAO:sc1111", setupBefore = TestExecutionEvent.TEST_METHOD) + @DisplayName("나의 스페이스 전체 조회 (멤버 포함) - 성공") + void getMySpaces_withMembers_Success() throws Exception { + // Given + String url = "/api/v1/space?includeMembers=true"; + + // When + ResultActions resultActions = performGet(url); + + // Then + expectOk( + resultActions, + "스페이스 목록이 조회됐습니다." + ); + resultActions + .andExpect(jsonPath("$.data.spaces").isArray()) + .andExpect(jsonPath("$.data.spaces.length()").value(2)); + + // 첫 번째 스페이스 (기존 스페이스 1) 검증 + resultActions + .andExpect(jsonPath("$.data.spaces[0].name").value("기존 스페이스 1_forSpaceControllerTest")) + .andExpect(jsonPath("$.data.spaces[0].authority").value("ADMIN")) + .andExpect(jsonPath("$.data.spaces[0].members").isArray()) + .andExpect(jsonPath("$.data.spaces[0].members.length()").value(2)) // PENDING 제외 2명 + .andExpect(jsonPath("$.data.spaces[0].members[0].name", startsWith("spaceControllerTester1"))) + .andExpect(jsonPath("$.data.spaces[0].members[0].authority").value("ADMIN")) + .andExpect(jsonPath("$.data.spaces[0].members[1].name", startsWith("spaceControllerTester3"))) + .andExpect(jsonPath("$.data.spaces[0].members[1].authority").value("READ_ONLY")); + + + // 두 번째 스페이스 (기존 스페이스 2) 검증 + resultActions + .andExpect(jsonPath("$.data.spaces[1].name").value("기존 스페이스 2_forSpaceControllerTest")) + .andExpect(jsonPath("$.data.spaces[1].authority").value("PENDING")) + .andExpect(jsonPath("$.data.spaces[1].members").isArray()) + .andExpect(jsonPath("$.data.spaces[1].members.length()").value(0)); // 가입된 멤버 없음 + } + @Test @WithUserDetails(value = "KAKAO:sc1111", setupBefore = TestExecutionEvent.TEST_METHOD) @DisplayName("초대받은 스페이스 전체 조회 - 성공") @@ -544,7 +590,7 @@ void getSpace_Fail_NotFound() throws Exception { @DisplayName("스페이스 단건 조회 - 실패 : 스페이스 멤버가 아닌 사용자") void getSpace_Fail_NotMember() throws Exception { // Given - Space space = spaceService.findByName("기존 스페이스 1_forSpaceControllerTest"); + Space space = spaceService.findByName("기존 스페이스 2_forSpaceControllerTest"); Integer spaceId = space.getId(); String url = String.format("/api/v1/space/%d", spaceId);