From 199b372ac793c71a9c8d57bdc9f8eb1dbd37533c Mon Sep 17 00:00:00 2001 From: young970 Date: Tue, 30 Jan 2024 00:41:29 +0900 Subject: [PATCH 01/12] =?UTF-8?q?[Refactor]:=20dto=20=EB=B3=80=ED=99=98?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20repository=EC=9A=A9=20mapper=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=ED=9B=84=20=ED=95=B4=EB=8B=B9=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RepositoryDtoMapper.java} | 18 +++++++++-------- .../querydsl/FavoriteQueryDslRepository.java | 13 +++++------- .../space/querydsl/DynamicQueryFactory.java | 2 ++ .../querydsl/SpaceQueryDslRepository.java | 20 ++++++------------- src/main/resources/application.yml | 2 +- 5 files changed, 24 insertions(+), 31 deletions(-) rename src/main/java/com/tenten/linkhub/domain/space/repository/common/{dto/SpaceAndSpaceImageOwnerNickNames.java => mapper/RepositoryDtoMapper.java} (54%) diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/common/dto/SpaceAndSpaceImageOwnerNickNames.java b/src/main/java/com/tenten/linkhub/domain/space/repository/common/mapper/RepositoryDtoMapper.java similarity index 54% rename from src/main/java/com/tenten/linkhub/domain/space/repository/common/dto/SpaceAndSpaceImageOwnerNickNames.java rename to src/main/java/com/tenten/linkhub/domain/space/repository/common/mapper/RepositoryDtoMapper.java index f46a7be7..6cacd2e1 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/common/dto/SpaceAndSpaceImageOwnerNickNames.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/common/mapper/RepositoryDtoMapper.java @@ -1,22 +1,25 @@ -package com.tenten.linkhub.domain.space.repository.common.dto; +package com.tenten.linkhub.domain.space.repository.common.mapper; import com.tenten.linkhub.domain.space.model.space.SpaceImage; +import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndOwnerNickName; +import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickName; +import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -public record SpaceAndSpaceImageOwnerNickNames( - List contents -) { - public static SpaceAndSpaceImageOwnerNickNames of(List spaceAndOwnerNickNames, List spaceImages) { +@Component +public class RepositoryDtoMapper { + + public List toSpaceAndSpaceImageOwnerNickNames(List spaceAndOwnerNickNames, List spaceImages) { Map> spaceImageMap = spaceImages .stream() .collect(Collectors.groupingBy( si -> si.getSpace().getId() )); - List mapSpaceAndSpaceImageOwnerNickNames = spaceAndOwnerNickNames + return spaceAndOwnerNickNames .stream() .map(so -> new SpaceAndSpaceImageOwnerNickName( so.space(), @@ -24,7 +27,6 @@ public static SpaceAndSpaceImageOwnerNickNames of(List sp so.ownerNickName() )) .collect(Collectors.toList()); - - return new SpaceAndSpaceImageOwnerNickNames(mapSpaceAndSpaceImageOwnerNickNames); } + } diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/favorite/querydsl/FavoriteQueryDslRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/favorite/querydsl/FavoriteQueryDslRepository.java index 1f9d19cb..79299575 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/favorite/querydsl/FavoriteQueryDslRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/favorite/querydsl/FavoriteQueryDslRepository.java @@ -7,8 +7,9 @@ import com.tenten.linkhub.domain.space.repository.common.dto.QSpaceAndOwnerNickName; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndOwnerNickName; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickName; -import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickNames; +import com.tenten.linkhub.domain.space.repository.common.mapper.RepositoryDtoMapper; import com.tenten.linkhub.domain.space.repository.favorite.dto.MyFavoriteSpacesQueryCondition; +import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Repository; @@ -21,14 +22,12 @@ import static com.tenten.linkhub.domain.space.model.space.QFavorite.favorite; import static com.tenten.linkhub.domain.space.model.space.QSpaceImage.spaceImage; +@RequiredArgsConstructor @Repository public class FavoriteQueryDslRepository { private final JPAQueryFactory queryFactory; - - public FavoriteQueryDslRepository(JPAQueryFactory queryFactory) { - this.queryFactory = queryFactory; - } + private final RepositoryDtoMapper mapper; public Slice findMyFavoriteSpacesByQuery(MyFavoriteSpacesQueryCondition condition) { List spaceAndOwnerNickNames = queryFactory @@ -52,9 +51,7 @@ public Slice findMyFavoriteSpacesByQuery(MyFavo List spaceIds = getSpaceIds(spaceAndOwnerNickNames); List spaceImages = findSpaceImagesBySpaceIds(spaceIds); - SpaceAndSpaceImageOwnerNickNames spaceAndSpaceImageOwnerNickNames = SpaceAndSpaceImageOwnerNickNames.of(spaceAndOwnerNickNames, spaceImages); - - List contents = spaceAndSpaceImageOwnerNickNames.contents(); + List contents = mapper.toSpaceAndSpaceImageOwnerNickNames(spaceAndOwnerNickNames, spaceImages); boolean hasNext = false; if (contents.size() > condition.pageable().getPageSize()) { diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/DynamicQueryFactory.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/DynamicQueryFactory.java index a4addf15..5fb4e76d 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/DynamicQueryFactory.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/DynamicQueryFactory.java @@ -9,12 +9,14 @@ import lombok.NoArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import static com.querydsl.core.types.dsl.Expressions.numberTemplate; import static com.tenten.linkhub.domain.space.model.space.QSpace.space; @NoArgsConstructor +@Component public class DynamicQueryFactory { public OrderSpecifier spaceSort(Pageable pageable) { diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java index 6a391a6e..e03d5433 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java @@ -5,9 +5,10 @@ import com.tenten.linkhub.domain.space.repository.common.dto.QSpaceAndOwnerNickName; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndOwnerNickName; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickName; -import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickNames; import com.tenten.linkhub.domain.space.repository.space.dto.MemberSpacesQueryCondition; import com.tenten.linkhub.domain.space.repository.space.dto.QueryCondition; +import com.tenten.linkhub.domain.space.repository.common.mapper.RepositoryDtoMapper; +import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Repository; @@ -19,16 +20,13 @@ import static com.tenten.linkhub.domain.space.model.space.QSpaceImage.spaceImage; import static com.tenten.linkhub.domain.space.model.space.QSpaceMember.spaceMember; +@RequiredArgsConstructor @Repository public class SpaceQueryDslRepository { private final JPAQueryFactory queryFactory; private final DynamicQueryFactory dynamicQueryFactory; - - public SpaceQueryDslRepository(JPAQueryFactory queryFactory) { - this.queryFactory = queryFactory; - this.dynamicQueryFactory = new DynamicQueryFactory(); - } + private final RepositoryDtoMapper mapper; public Slice findPublicSpacesJoinSpaceImageByCondition(QueryCondition condition) { List spaceAndOwnerNickNames = queryFactory @@ -49,12 +47,9 @@ public Slice findPublicSpacesJoinSpaceImageByCo .fetch(); List spaceIds = getSpaceIds(spaceAndOwnerNickNames); - List spaceImages = findSpaceImagesBySpaceIds(spaceIds); - SpaceAndSpaceImageOwnerNickNames spaceAndSpaceImageOwnerNickNames = SpaceAndSpaceImageOwnerNickNames.of(spaceAndOwnerNickNames, spaceImages); - - List contents = spaceAndSpaceImageOwnerNickNames.contents(); + List contents = mapper.toSpaceAndSpaceImageOwnerNickNames(spaceAndOwnerNickNames, spaceImages); boolean hasNext = false; if (contents.size() > condition.pageable().getPageSize()) { @@ -86,12 +81,9 @@ public Slice findMemberSpacesJoinSpaceImageByCo .fetch(); List spaceIds = getSpaceIds(spaceAndOwnerNickNames); - List spaceImages = findSpaceImagesBySpaceIds(spaceIds); - SpaceAndSpaceImageOwnerNickNames spaceAndSpaceImageOwnerNickNames = SpaceAndSpaceImageOwnerNickNames.of(spaceAndOwnerNickNames, spaceImages); - - List contents = spaceAndSpaceImageOwnerNickNames.contents(); + List contents = mapper.toSpaceAndSpaceImageOwnerNickNames(spaceAndOwnerNickNames, spaceImages);; boolean hasNext = false; if (contents.size() > condition.pageable().getPageSize()) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4bec4cce..d10603b7 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,7 +16,7 @@ spring: url: ${LOKI_URL} datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: ${RDS_URL} + url: jdbc:mysql://localhost:3306/link_hub_query_test?serverTimezone=Asia/Seoul&rewriteBatchedStatements=true username: ${RDS_USERNAME} password: ${RDS_PASSWORD} servlet: From 9ebe890d40d1be5ad833866a2e3b6af0ba9bf9bd Mon Sep 17 00:00:00 2001 From: young970 Date: Tue, 30 Jan 2024 02:18:28 +0900 Subject: [PATCH 02/12] =?UTF-8?q?[Refactor]:=20=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=95=84=ED=84=B0=20=EC=A1=B0=ED=9A=8C=EC=99=80=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../space/controller/SpaceController.java | 2 +- .../space/DefaultSpaceRepository.java | 5 +++ .../repository/space/SpaceRepository.java | 2 ++ .../querydsl/SpaceQueryDslRepository.java | 32 +++++++++++++++++++ .../space/service/DefaultSpaceService.java | 9 ++++++ .../domain/space/service/SpaceService.java | 2 ++ 6 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java b/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java index 8620a7f9..31055279 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java +++ b/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java @@ -129,7 +129,7 @@ public ResponseEntity findPublicSpacesByQue request.pageSize(), StringUtils.hasText(request.sort()) ? Sort.by(request.sort()) : Sort.unsorted()); - SpacesFindByQueryResponses responses = spaceService.findPublicSpacesByQuery( + SpacesFindByQueryResponses responses = spaceService.searchPublicSpacesByQuery( spaceMapper.toPublicSpacesFindByQueryRequest(request, pageRequest) ); diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/DefaultSpaceRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/DefaultSpaceRepository.java index db361ee7..e59066ec 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/DefaultSpaceRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/DefaultSpaceRepository.java @@ -26,6 +26,11 @@ public Slice findPublicSpacesJoinSpaceImageByQu return spaceQueryDslRepository.findPublicSpacesJoinSpaceImageByCondition(queryCondition); } + @Override + public Slice searchPublicSpacesJoinSpaceImageByQuery(QueryCondition queryCondition) { + return spaceQueryDslRepository.searchPublicSpacesJoinSpaceImageByCondition(queryCondition); + } + @Override public Space save(Space space) { return spaceJpaRepository.save(space); diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/SpaceRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/SpaceRepository.java index 072a2b70..e241b593 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/SpaceRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/SpaceRepository.java @@ -10,6 +10,8 @@ public interface SpaceRepository { Slice findPublicSpacesJoinSpaceImageByQuery(QueryCondition queryCondition); + Slice searchPublicSpacesJoinSpaceImageByQuery(QueryCondition queryCondition); + Space save(Space space); Space getById(Long spaceId); diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java index e03d5433..32f0a000 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java @@ -60,6 +60,38 @@ public Slice findPublicSpacesJoinSpaceImageByCo return new SliceImpl<>(contents, condition.pageable(), hasNext); } + public Slice searchPublicSpacesJoinSpaceImageByCondition(QueryCondition condition) { + List spaceAndOwnerNickNames = queryFactory + .select(new QSpaceAndOwnerNickName( + space, + member.nickname + )) + .from(space) + .leftJoin(member).on(space.memberId.eq(member.id)) + .where(space.isDeleted.eq(false), + space.isVisible.eq(true), + dynamicQueryFactory.eqSpaceName(condition.keyWord()), + dynamicQueryFactory.eqCategory(condition.filter()) + ) + .orderBy(dynamicQueryFactory.spaceSort(condition.pageable())) + .offset(condition.pageable().getOffset()) + .limit(condition.pageable().getPageSize() + 1) + .fetch(); + + List spaceIds = getSpaceIds(spaceAndOwnerNickNames); + List spaceImages = findSpaceImagesBySpaceIds(spaceIds); + + List contents = mapper.toSpaceAndSpaceImageOwnerNickNames(spaceAndOwnerNickNames, spaceImages); + boolean hasNext = false; + + if (contents.size() > condition.pageable().getPageSize()) { + contents.remove(condition.pageable().getPageSize()); + hasNext = true; + } + + return new SliceImpl<>(contents, condition.pageable(), hasNext); + } + public Slice findMemberSpacesJoinSpaceImageByCondition(MemberSpacesQueryCondition condition) { List spaceAndOwnerNickNames = queryFactory .select(new QSpaceAndOwnerNickName( diff --git a/src/main/java/com/tenten/linkhub/domain/space/service/DefaultSpaceService.java b/src/main/java/com/tenten/linkhub/domain/space/service/DefaultSpaceService.java index 00e2ade0..c6bbac79 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/service/DefaultSpaceService.java +++ b/src/main/java/com/tenten/linkhub/domain/space/service/DefaultSpaceService.java @@ -68,6 +68,15 @@ public SpacesFindByQueryResponses findPublicSpacesByQuery(PublicSpacesFindByQuer return SpacesFindByQueryResponses.from(spaceAndSpaceImageOwnerNickName); } + @Override + @Transactional(readOnly = true) + public SpacesFindByQueryResponses searchPublicSpacesByQuery(PublicSpacesFindByQueryRequest request) { + validateSearchKeWord(request.keyWord()); + Slice spaceAndSpaceImageOwnerNickName = spaceRepository.findPublicSpacesJoinSpaceImageByQuery(mapper.toQueryCond(request)); + + return SpacesFindByQueryResponses.from(spaceAndSpaceImageOwnerNickName); + } + @Override @Transactional public Long createSpace(SpaceCreateRequest request) { diff --git a/src/main/java/com/tenten/linkhub/domain/space/service/SpaceService.java b/src/main/java/com/tenten/linkhub/domain/space/service/SpaceService.java index 51495c2c..17faf839 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/service/SpaceService.java +++ b/src/main/java/com/tenten/linkhub/domain/space/service/SpaceService.java @@ -15,6 +15,8 @@ public interface SpaceService { SpacesFindByQueryResponses findPublicSpacesByQuery(PublicSpacesFindByQueryRequest request); + SpacesFindByQueryResponses searchPublicSpacesByQuery(PublicSpacesFindByQueryRequest request); + Long createSpace(SpaceCreateRequest spaceCreateRequest); SpaceWithSpaceImageAndSpaceMemberInfo getSpaceWithSpaceImageAndSpaceMemberById(Long spaceId, Long memberId); From 5b2422e33fec62b2f32b2922718bc9271c12cbf1 Mon Sep 17 00:00:00 2001 From: young970 Date: Tue, 30 Jan 2024 20:32:55 +0900 Subject: [PATCH 03/12] =?UTF-8?q?[Refactor]:=20=EB=A9=94=EC=9D=B8=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=9A=A9=20=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=95=84=ED=84=B0=20=EC=A1=B0=ED=9A=8C=20API=20cur?= =?UTF-8?q?sor=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../space/common/CursorPageMataData.java | 9 +++ .../space/common/SpaceCursorPageRequest.java | 25 +++++++ .../domain/space/common/SpaceCursorSlice.java | 49 +++++++++++++ .../space/controller/SpaceController.java | 14 ++-- ...PublicSpaceFindWithFilterApiResponses.java | 24 ++++--- .../PublicSpacesFindWithFilterApiRequest.java | 7 +- .../controller/mapper/SpaceApiMapper.java | 4 +- .../space/DefaultSpaceRepository.java | 4 +- .../repository/space/SpaceRepository.java | 4 +- .../space/dto/CursorPageQueryCondition.java | 10 +++ .../space/querydsl/DynamicQueryFactory.java | 68 +++++++++++++++---- .../querydsl/SpaceQueryDslRepository.java | 38 +++++++---- .../space/service/DefaultSpaceService.java | 12 ++-- .../domain/space/service/SpaceService.java | 4 +- .../PublicSpacesFindWithFilterRequest.java | 10 +++ .../space/SpacesFindWithCursorResponses.java | 30 ++++++++ .../space/service/mapper/SpaceMapper.java | 10 +++ src/main/resources/application.yml | 2 +- 18 files changed, 270 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/tenten/linkhub/domain/space/common/CursorPageMataData.java create mode 100644 src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorPageRequest.java create mode 100644 src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorSlice.java create mode 100644 src/main/java/com/tenten/linkhub/domain/space/repository/space/dto/CursorPageQueryCondition.java create mode 100644 src/main/java/com/tenten/linkhub/domain/space/service/dto/space/PublicSpacesFindWithFilterRequest.java create mode 100644 src/main/java/com/tenten/linkhub/domain/space/service/dto/space/SpacesFindWithCursorResponses.java diff --git a/src/main/java/com/tenten/linkhub/domain/space/common/CursorPageMataData.java b/src/main/java/com/tenten/linkhub/domain/space/common/CursorPageMataData.java new file mode 100644 index 00000000..08f52b48 --- /dev/null +++ b/src/main/java/com/tenten/linkhub/domain/space/common/CursorPageMataData.java @@ -0,0 +1,9 @@ +package com.tenten.linkhub.domain.space.common; + +public record CursorPageMataData( + Long lastFavoriteCount, + Long lastId, + Integer pageSize, + Boolean hasNext +) { +} diff --git a/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorPageRequest.java b/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorPageRequest.java new file mode 100644 index 00000000..20565b9c --- /dev/null +++ b/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorPageRequest.java @@ -0,0 +1,25 @@ +package com.tenten.linkhub.domain.space.common; + +import com.tenten.linkhub.domain.space.model.category.Category; +import org.springframework.data.domain.Sort; +import org.springframework.util.StringUtils; + +public record SpaceCursorPageRequest( + Integer pageSize, + Sort sort, + Category filter +) { + + public static SpaceCursorPageRequest of( + Integer pageSize, + String sort, + Category filter + ) + { + return new SpaceCursorPageRequest( + pageSize, + StringUtils.hasText(sort) ? Sort.by(sort) : Sort.unsorted(), + filter); + } + +} diff --git a/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorSlice.java b/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorSlice.java new file mode 100644 index 00000000..70201255 --- /dev/null +++ b/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorSlice.java @@ -0,0 +1,49 @@ +package com.tenten.linkhub.domain.space.common; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Getter +public class SpaceCursorSlice { + private final Long lastFavoriteCount; + private final Long lastId; + private final Integer pageSize; + private final Boolean hasNext; + private final List content; + + public SpaceCursorSlice(Long lastFavoriteCount, Long lastId, Integer pageSize, Boolean hasNext, List content) { + this.lastFavoriteCount = lastFavoriteCount; + this.lastId = lastId; + this.pageSize = pageSize; + this.hasNext = hasNext; + this.content = new ArrayList<>(content); + } + + public static SpaceCursorSlice of(Long lastFavoriteCount, Long lastId, Integer pageSize, Boolean hasNext, List content) { + return new SpaceCursorSlice<>( + lastFavoriteCount, + lastId, + pageSize, + hasNext, + content + ); + } + + public SpaceCursorSlice map(Function converter) { + List newContent = content.stream() + .map(converter) + .collect(Collectors.toList()); + return new SpaceCursorSlice<>( + lastFavoriteCount, + lastId, + pageSize, + hasNext, + newContent + ); + } + +} diff --git a/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java b/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java index 31055279..24da671c 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java +++ b/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java @@ -2,6 +2,7 @@ import com.tenten.linkhub.domain.auth.MemberDetails; +import com.tenten.linkhub.domain.space.common.SpaceCursorPageRequest; import com.tenten.linkhub.domain.space.controller.dto.comment.CommentUpdateApiRequest; import com.tenten.linkhub.domain.space.controller.dto.comment.CommentUpdateApiResponse; import com.tenten.linkhub.domain.space.controller.dto.comment.RepliesFindApiRequest; @@ -48,10 +49,11 @@ import com.tenten.linkhub.domain.space.service.dto.comment.RootCommentCreateRequest; import com.tenten.linkhub.domain.space.service.dto.favorite.FavoriteSpacesFindResponses; import com.tenten.linkhub.domain.space.service.dto.favorite.SpaceRegisterInFavoriteResponse; -import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindByQueryRequest; +import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindWithFilterRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindByQueryResponses; import com.tenten.linkhub.domain.space.service.dto.space.SpaceTagGetResponses; +import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindWithCursorResponses; import com.tenten.linkhub.global.response.ErrorResponse; import com.tenten.linkhub.global.response.ErrorWithDetailCodeResponse; @@ -291,13 +293,13 @@ public ResponseEntity deleteSpace( public ResponseEntity findPublicSpacesWithFilter( @ModelAttribute PublicSpacesFindWithFilterApiRequest request ) { - PageRequest pageRequest = PageRequest.of( - request.pageNumber(), + SpaceCursorPageRequest pageRequest = SpaceCursorPageRequest.of( request.pageSize(), - StringUtils.hasText(request.sort()) ? Sort.by(request.sort()) : Sort.unsorted()); + request.sort(), + request.filter()); - PublicSpacesFindByQueryRequest serviceRequest = spaceMapper.toPublicSpacesFindByQueryRequest(request, pageRequest); - SpacesFindByQueryResponses responses = spaceService.findPublicSpacesByQuery(serviceRequest); + PublicSpacesFindWithFilterRequest serviceRequest = spaceMapper.toPublicSpacesFindWithFilterRequest(request, pageRequest); + SpacesFindWithCursorResponses responses = spaceService.findPublicSpacesWithFilter(serviceRequest); PublicSpaceFindWithFilterApiResponses apiResponses = PublicSpaceFindWithFilterApiResponses.from(responses); diff --git a/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpaceFindWithFilterApiResponses.java b/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpaceFindWithFilterApiResponses.java index b85b9445..4b210ebc 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpaceFindWithFilterApiResponses.java +++ b/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpaceFindWithFilterApiResponses.java @@ -1,17 +1,17 @@ package com.tenten.linkhub.domain.space.controller.dto.space; -import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindByQueryResponses; -import com.tenten.linkhub.global.util.PageMetaData; -import org.springframework.data.domain.Slice; +import com.tenten.linkhub.domain.space.common.CursorPageMataData; +import com.tenten.linkhub.domain.space.common.SpaceCursorSlice; +import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindWithCursorResponses; import java.util.List; public record PublicSpaceFindWithFilterApiResponses( List responses, - PageMetaData metaData + CursorPageMataData metaData ) { - public static PublicSpaceFindWithFilterApiResponses from(SpacesFindByQueryResponses responses) { - Slice mapResponses = responses.responses() + public static PublicSpaceFindWithFilterApiResponses from(SpacesFindWithCursorResponses responses) { + SpaceCursorSlice mapResponses = responses.responses() .map(r -> new PublicSpaceFindWithFilterApiResponse( r.spaceId(), r.spaceName(), @@ -23,13 +23,15 @@ public static PublicSpaceFindWithFilterApiResponses from(SpacesFindByQueryRespon r.spaceImagePath(), r.ownerNickName())); - PageMetaData pageMetaData = new PageMetaData( - mapResponses.hasNext(), - mapResponses.getSize(), - mapResponses.getNumber()); + CursorPageMataData cursorPageMataData = new CursorPageMataData( + mapResponses.getLastFavoriteCount(), + mapResponses.getLastId(), + mapResponses.getPageSize(), + mapResponses.getHasNext() + ); return new PublicSpaceFindWithFilterApiResponses( mapResponses.getContent(), - pageMetaData); + cursorPageMataData); } } diff --git a/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpacesFindWithFilterApiRequest.java b/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpacesFindWithFilterApiRequest.java index a295cd40..3b4faf8c 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpacesFindWithFilterApiRequest.java +++ b/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpacesFindWithFilterApiRequest.java @@ -4,8 +4,11 @@ import io.swagger.v3.oas.annotations.media.Schema; public record PublicSpacesFindWithFilterApiRequest( - @Schema(title = "페이지 번호", example = "0") - Integer pageNumber, + @Schema(title = "마지막 favoriteCount", example = "1") + Long lastFavoriteCount, + + @Schema(title = "마지막 spaceId", example = "1") + Long lastSpaceId, @Schema(title = "페이지 크기", example = "10") Integer pageSize, diff --git a/src/main/java/com/tenten/linkhub/domain/space/controller/mapper/SpaceApiMapper.java b/src/main/java/com/tenten/linkhub/domain/space/controller/mapper/SpaceApiMapper.java index 49832704..2525315c 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/controller/mapper/SpaceApiMapper.java +++ b/src/main/java/com/tenten/linkhub/domain/space/controller/mapper/SpaceApiMapper.java @@ -1,5 +1,6 @@ package com.tenten.linkhub.domain.space.controller.mapper; +import com.tenten.linkhub.domain.space.common.SpaceCursorPageRequest; import com.tenten.linkhub.domain.space.controller.dto.space.NewSpacesScrapApiRequest; import com.tenten.linkhub.domain.space.controller.dto.space.PublicSpacesFindWithFilterApiRequest; import com.tenten.linkhub.domain.space.controller.dto.space.SpaceCreateApiRequest; @@ -11,6 +12,7 @@ import com.tenten.linkhub.domain.space.facade.dto.SpaceCreateFacadeRequest; import com.tenten.linkhub.domain.space.facade.dto.SpaceDetailGetByIdFacadeRequest; import com.tenten.linkhub.domain.space.facade.dto.SpaceUpdateFacadeRequest; +import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindWithFilterRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpaceTagGetResponses; import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindByQueryRequest; import com.tenten.linkhub.domain.space.service.dto.spacemember.SpaceMemberRoleChangeRequest; @@ -28,7 +30,7 @@ public interface SpaceApiMapper { PublicSpacesFindByQueryRequest toPublicSpacesFindByQueryRequest(PublicSpacesFindByQueryApiRequest request, Pageable pageable); - PublicSpacesFindByQueryRequest toPublicSpacesFindByQueryRequest(PublicSpacesFindWithFilterApiRequest request, Pageable pageable); + PublicSpacesFindWithFilterRequest toPublicSpacesFindWithFilterRequest(PublicSpacesFindWithFilterApiRequest request, SpaceCursorPageRequest pageable); SpaceCreateFacadeRequest toSpaceCreateFacadeRequest(SpaceCreateApiRequest request, MultipartFile file, Long memberId); diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/DefaultSpaceRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/DefaultSpaceRepository.java index e59066ec..78297673 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/DefaultSpaceRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/DefaultSpaceRepository.java @@ -1,7 +1,9 @@ package com.tenten.linkhub.domain.space.repository.space; +import com.tenten.linkhub.domain.space.common.SpaceCursorSlice; import com.tenten.linkhub.domain.space.model.space.Space; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickName; +import com.tenten.linkhub.domain.space.repository.space.dto.CursorPageQueryCondition; import com.tenten.linkhub.domain.space.repository.space.dto.MemberSpacesQueryCondition; import com.tenten.linkhub.domain.space.repository.space.dto.QueryCondition; import com.tenten.linkhub.domain.space.repository.space.querydsl.SpaceQueryDslRepository; @@ -22,7 +24,7 @@ public DefaultSpaceRepository(SpaceJpaRepository spaceJpaRepository, SpaceQueryD } @Override - public Slice findPublicSpacesJoinSpaceImageByQuery(QueryCondition queryCondition) { + public SpaceCursorSlice findPublicSpacesJoinSpaceImageByQuery(CursorPageQueryCondition queryCondition) { return spaceQueryDslRepository.findPublicSpacesJoinSpaceImageByCondition(queryCondition); } diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/SpaceRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/SpaceRepository.java index e241b593..6fdcae94 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/SpaceRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/SpaceRepository.java @@ -1,14 +1,16 @@ package com.tenten.linkhub.domain.space.repository.space; +import com.tenten.linkhub.domain.space.common.SpaceCursorSlice; import com.tenten.linkhub.domain.space.model.space.Space; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickName; +import com.tenten.linkhub.domain.space.repository.space.dto.CursorPageQueryCondition; import com.tenten.linkhub.domain.space.repository.space.dto.MemberSpacesQueryCondition; import com.tenten.linkhub.domain.space.repository.space.dto.QueryCondition; import org.springframework.data.domain.Slice; public interface SpaceRepository { - Slice findPublicSpacesJoinSpaceImageByQuery(QueryCondition queryCondition); + SpaceCursorSlice findPublicSpacesJoinSpaceImageByQuery(CursorPageQueryCondition queryCondition); Slice searchPublicSpacesJoinSpaceImageByQuery(QueryCondition queryCondition); diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/dto/CursorPageQueryCondition.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/dto/CursorPageQueryCondition.java new file mode 100644 index 00000000..84882e5d --- /dev/null +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/dto/CursorPageQueryCondition.java @@ -0,0 +1,10 @@ +package com.tenten.linkhub.domain.space.repository.space.dto; + +import com.tenten.linkhub.domain.space.common.SpaceCursorPageRequest; + +public record CursorPageQueryCondition( + SpaceCursorPageRequest pageable, + Long lastFavoriteCount, + Long lastSpaceId +) { +} diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/DynamicQueryFactory.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/DynamicQueryFactory.java index 5fb4e76d..f3527e3e 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/DynamicQueryFactory.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/DynamicQueryFactory.java @@ -7,11 +7,12 @@ import com.tenten.linkhub.domain.space.model.category.Category; import com.tenten.linkhub.global.util.SearchKeywordParser; import lombok.NoArgsConstructor; -import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; +import java.util.Objects; + import static com.querydsl.core.types.dsl.Expressions.numberTemplate; import static com.tenten.linkhub.domain.space.model.space.QSpace.space; @@ -19,21 +20,44 @@ @Component public class DynamicQueryFactory { - public OrderSpecifier spaceSort(Pageable pageable) { - for (Sort.Order sort : pageable.getSort()) { - String property = sort.getProperty(); - - switch (property) { - case "favorite_count" -> { - return new OrderSpecifier(Order.DESC, space.favoriteCount); - } - case "created_at" -> { - return new OrderSpecifier(Order.DESC, space.createdAt); - } + public BooleanExpression ltLastFavoriteCountAndId(Long lastFavoriteCount, Long lastId, Sort sort) { + String requestSort = getRequestSort(sort); + + if (isCreatedAtRequest(requestSort, lastId)) { + return space.id.lt(lastId); + } + + if (isFavoriteCountRequest(requestSort, lastFavoriteCount, lastId)) { + return space.favoriteCount.lt(lastFavoriteCount) + .or(space.favoriteCount.eq(lastFavoriteCount).and(space.id.lt(lastId))); + } + + return null; + } + + public OrderSpecifier[] spaceSort(Sort sort) { + String property = getRequestSort(sort); + + if (Objects.isNull(property)) { + return null; + } + + switch (property) { + case "favorite_count" -> { + return new OrderSpecifier[]{ + new OrderSpecifier(Order.DESC, space.favoriteCount), + new OrderSpecifier(Order.DESC, space.id) + }; + } + + case "created_at" -> { + return new OrderSpecifier[]{ + new OrderSpecifier(Order.DESC, space.id) + }; } } - return new OrderSpecifier(Order.ASC, NullExpression.DEFAULT, OrderSpecifier.NullHandling.Default); + return null; } public BooleanExpression eqCategory(Category filter) { @@ -67,4 +91,22 @@ public BooleanExpression eqIsVisible(Boolean isSelfSpace) { return space.isVisible.eq(true); } + private String getRequestSort(Sort sort) { + return sort.stream() + .findFirst() + .map(Sort.Order::getProperty) + .orElse(null); + } + + private boolean isCreatedAtRequest(String requestSort, Long lastId) { + return Objects.equals(requestSort, "created_at") && + Objects.nonNull(lastId); + } + + private boolean isFavoriteCountRequest(String requestSort, Long lastFavoriteCount, Long lastId) { + return Objects.equals(requestSort, "favorite_count") && + Objects.nonNull(lastFavoriteCount) && + Objects.nonNull(lastId); + } + } diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java index 32f0a000..809adf3f 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java @@ -1,10 +1,14 @@ package com.tenten.linkhub.domain.space.repository.space.querydsl; import com.querydsl.jpa.impl.JPAQueryFactory; +import com.tenten.linkhub.domain.space.common.SpaceCursorPageRequest; +import com.tenten.linkhub.domain.space.common.SpaceCursorSlice; +import com.tenten.linkhub.domain.space.model.space.Space; import com.tenten.linkhub.domain.space.model.space.SpaceImage; import com.tenten.linkhub.domain.space.repository.common.dto.QSpaceAndOwnerNickName; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndOwnerNickName; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickName; +import com.tenten.linkhub.domain.space.repository.space.dto.CursorPageQueryCondition; import com.tenten.linkhub.domain.space.repository.space.dto.MemberSpacesQueryCondition; import com.tenten.linkhub.domain.space.repository.space.dto.QueryCondition; import com.tenten.linkhub.domain.space.repository.common.mapper.RepositoryDtoMapper; @@ -28,7 +32,9 @@ public class SpaceQueryDslRepository { private final DynamicQueryFactory dynamicQueryFactory; private final RepositoryDtoMapper mapper; - public Slice findPublicSpacesJoinSpaceImageByCondition(QueryCondition condition) { + public SpaceCursorSlice findPublicSpacesJoinSpaceImageByCondition(CursorPageQueryCondition condition) { + SpaceCursorPageRequest pageable = condition.pageable(); + List spaceAndOwnerNickNames = queryFactory .select(new QSpaceAndOwnerNickName( space, @@ -36,14 +42,13 @@ public Slice findPublicSpacesJoinSpaceImageByCo )) .from(space) .leftJoin(member).on(space.memberId.eq(member.id)) - .where(space.isDeleted.eq(false), + .where(dynamicQueryFactory.ltLastFavoriteCountAndId(condition.lastFavoriteCount(), condition.lastSpaceId(), pageable.sort()), + space.isDeleted.eq(false), space.isVisible.eq(true), - dynamicQueryFactory.eqSpaceName(condition.keyWord()), - dynamicQueryFactory.eqCategory(condition.filter()) + dynamicQueryFactory.eqCategory(pageable.filter()) ) - .orderBy(dynamicQueryFactory.spaceSort(condition.pageable())) - .offset(condition.pageable().getOffset()) - .limit(condition.pageable().getPageSize() + 1) + .orderBy(dynamicQueryFactory.spaceSort(pageable.sort())) + .limit(pageable.pageSize() + 1) .fetch(); List spaceIds = getSpaceIds(spaceAndOwnerNickNames); @@ -52,12 +57,20 @@ public Slice findPublicSpacesJoinSpaceImageByCo List contents = mapper.toSpaceAndSpaceImageOwnerNickNames(spaceAndOwnerNickNames, spaceImages); boolean hasNext = false; - if (contents.size() > condition.pageable().getPageSize()) { - contents.remove(condition.pageable().getPageSize()); + if (contents.size() > pageable.pageSize()) { + contents.remove(pageable.pageSize()); hasNext = true; } - return new SliceImpl<>(contents, condition.pageable(), hasNext); + Space lastSpace = contents.get(contents.size() - 1).space(); + + return SpaceCursorSlice.of( + lastSpace.getFavoriteCount(), + lastSpace.getId(), + pageable.pageSize(), + hasNext, + contents + ); } public Slice searchPublicSpacesJoinSpaceImageByCondition(QueryCondition condition) { @@ -73,7 +86,7 @@ public Slice searchPublicSpacesJoinSpaceImageBy dynamicQueryFactory.eqSpaceName(condition.keyWord()), dynamicQueryFactory.eqCategory(condition.filter()) ) - .orderBy(dynamicQueryFactory.spaceSort(condition.pageable())) + .orderBy(dynamicQueryFactory.spaceSort(condition.pageable().getSort())) .offset(condition.pageable().getOffset()) .limit(condition.pageable().getPageSize() + 1) .fetch(); @@ -115,7 +128,8 @@ public Slice findMemberSpacesJoinSpaceImageByCo List spaceIds = getSpaceIds(spaceAndOwnerNickNames); List spaceImages = findSpaceImagesBySpaceIds(spaceIds); - List contents = mapper.toSpaceAndSpaceImageOwnerNickNames(spaceAndOwnerNickNames, spaceImages);; + List contents = mapper.toSpaceAndSpaceImageOwnerNickNames(spaceAndOwnerNickNames, spaceImages); + ; boolean hasNext = false; if (contents.size() > condition.pageable().getPageSize()) { diff --git a/src/main/java/com/tenten/linkhub/domain/space/service/DefaultSpaceService.java b/src/main/java/com/tenten/linkhub/domain/space/service/DefaultSpaceService.java index c6bbac79..d2728781 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/service/DefaultSpaceService.java +++ b/src/main/java/com/tenten/linkhub/domain/space/service/DefaultSpaceService.java @@ -1,5 +1,6 @@ package com.tenten.linkhub.domain.space.service; +import com.tenten.linkhub.domain.space.common.SpaceCursorSlice; import com.tenten.linkhub.domain.space.model.space.Scrap; import com.tenten.linkhub.domain.space.model.space.Space; import com.tenten.linkhub.domain.space.model.space.SpaceImage; @@ -14,12 +15,14 @@ import com.tenten.linkhub.domain.space.service.dto.space.MemberSpacesFindRequest; import com.tenten.linkhub.domain.space.service.dto.space.NewSpacesScrapRequest; import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindByQueryRequest; +import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindWithFilterRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpaceCreateRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpaceTagGetResponse; import com.tenten.linkhub.domain.space.service.dto.space.SpaceTagGetResponses; import com.tenten.linkhub.domain.space.service.dto.space.SpaceUpdateRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpaceWithSpaceImageAndSpaceMemberInfo; import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindByQueryResponses; +import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindWithCursorResponses; import com.tenten.linkhub.domain.space.service.dto.spacemember.SpaceMemberRoleChangeRequest; import com.tenten.linkhub.domain.space.service.mapper.SpaceMapper; @@ -61,18 +64,17 @@ public class DefaultSpaceService implements SpaceService { @Override @Transactional(readOnly = true) - public SpacesFindByQueryResponses findPublicSpacesByQuery(PublicSpacesFindByQueryRequest request) { - validateSearchKeWord(request.keyWord()); - Slice spaceAndSpaceImageOwnerNickName = spaceRepository.findPublicSpacesJoinSpaceImageByQuery(mapper.toQueryCond(request)); + public SpacesFindWithCursorResponses findPublicSpacesWithFilter(PublicSpacesFindWithFilterRequest request) { + SpaceCursorSlice spaceAndSpaceImageOwnerNickName = spaceRepository.findPublicSpacesJoinSpaceImageByQuery(mapper.toCursorPageQueryCondition(request)); - return SpacesFindByQueryResponses.from(spaceAndSpaceImageOwnerNickName); + return SpacesFindWithCursorResponses.from(spaceAndSpaceImageOwnerNickName); } @Override @Transactional(readOnly = true) public SpacesFindByQueryResponses searchPublicSpacesByQuery(PublicSpacesFindByQueryRequest request) { validateSearchKeWord(request.keyWord()); - Slice spaceAndSpaceImageOwnerNickName = spaceRepository.findPublicSpacesJoinSpaceImageByQuery(mapper.toQueryCond(request)); + Slice spaceAndSpaceImageOwnerNickName = spaceRepository.searchPublicSpacesJoinSpaceImageByQuery(mapper.toQueryCond(request)); return SpacesFindByQueryResponses.from(spaceAndSpaceImageOwnerNickName); } diff --git a/src/main/java/com/tenten/linkhub/domain/space/service/SpaceService.java b/src/main/java/com/tenten/linkhub/domain/space/service/SpaceService.java index 17faf839..02632920 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/service/SpaceService.java +++ b/src/main/java/com/tenten/linkhub/domain/space/service/SpaceService.java @@ -4,16 +4,18 @@ import com.tenten.linkhub.domain.space.service.dto.space.MemberSpacesFindRequest; import com.tenten.linkhub.domain.space.service.dto.space.NewSpacesScrapRequest; import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindByQueryRequest; +import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindWithFilterRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindByQueryResponses; import com.tenten.linkhub.domain.space.service.dto.space.SpaceCreateRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpaceTagGetResponses; import com.tenten.linkhub.domain.space.service.dto.space.SpaceUpdateRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpaceWithSpaceImageAndSpaceMemberInfo; +import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindWithCursorResponses; import com.tenten.linkhub.domain.space.service.dto.spacemember.SpaceMemberRoleChangeRequest; public interface SpaceService { - SpacesFindByQueryResponses findPublicSpacesByQuery(PublicSpacesFindByQueryRequest request); + SpacesFindWithCursorResponses findPublicSpacesWithFilter(PublicSpacesFindWithFilterRequest request); SpacesFindByQueryResponses searchPublicSpacesByQuery(PublicSpacesFindByQueryRequest request); diff --git a/src/main/java/com/tenten/linkhub/domain/space/service/dto/space/PublicSpacesFindWithFilterRequest.java b/src/main/java/com/tenten/linkhub/domain/space/service/dto/space/PublicSpacesFindWithFilterRequest.java new file mode 100644 index 00000000..b1832dad --- /dev/null +++ b/src/main/java/com/tenten/linkhub/domain/space/service/dto/space/PublicSpacesFindWithFilterRequest.java @@ -0,0 +1,10 @@ +package com.tenten.linkhub.domain.space.service.dto.space; + +import com.tenten.linkhub.domain.space.common.SpaceCursorPageRequest; + +public record PublicSpacesFindWithFilterRequest( + SpaceCursorPageRequest pageable, + Long lastFavoriteCount, + Long lastSpaceId +) { +} diff --git a/src/main/java/com/tenten/linkhub/domain/space/service/dto/space/SpacesFindWithCursorResponses.java b/src/main/java/com/tenten/linkhub/domain/space/service/dto/space/SpacesFindWithCursorResponses.java new file mode 100644 index 00000000..c3c5d7f6 --- /dev/null +++ b/src/main/java/com/tenten/linkhub/domain/space/service/dto/space/SpacesFindWithCursorResponses.java @@ -0,0 +1,30 @@ +package com.tenten.linkhub.domain.space.service.dto.space; + +import com.tenten.linkhub.domain.space.common.SpaceCursorSlice; +import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickName; + +import java.util.Objects; + +public record SpacesFindWithCursorResponses(SpaceCursorSlice responses) { + + public static SpacesFindWithCursorResponses from(SpaceCursorSlice response){ + SpaceCursorSlice mapResponses = response.map(s -> new SpacesFindByQueryResponse( + s.space().getId(), + s.space().getSpaceName(), + Objects.isNull(s.space().getDescription()) ? "" : s.space().getDescription(), + s.space().getCategory(), + s.space().getIsVisible(), + s.space().getIsComment(), + s.space().getIsLinkSummarizable(), + s.space().getIsReadMarkEnabled(), + s.space().getViewCount(), + s.space().getScrapCount(), + s.space().getFavoriteCount(), + s.spaceImages().isEmpty() ? null : s.spaceImages().get(0).getPath(), + s.ownerNickName() + )); + + return new SpacesFindWithCursorResponses(mapResponses); + } + +} diff --git a/src/main/java/com/tenten/linkhub/domain/space/service/mapper/SpaceMapper.java b/src/main/java/com/tenten/linkhub/domain/space/service/mapper/SpaceMapper.java index 2fb2a57e..31bdd503 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/service/mapper/SpaceMapper.java +++ b/src/main/java/com/tenten/linkhub/domain/space/service/mapper/SpaceMapper.java @@ -5,10 +5,12 @@ import com.tenten.linkhub.domain.space.model.space.SpaceImage; import com.tenten.linkhub.domain.space.model.space.SpaceMember; import com.tenten.linkhub.domain.space.model.space.dto.SpaceUpdateDto; +import com.tenten.linkhub.domain.space.repository.space.dto.CursorPageQueryCondition; import com.tenten.linkhub.domain.space.repository.space.dto.MemberSpacesQueryCondition; import com.tenten.linkhub.domain.space.repository.space.dto.QueryCondition; import com.tenten.linkhub.domain.space.service.dto.space.MemberSpacesFindRequest; import com.tenten.linkhub.domain.space.service.dto.space.NewSpacesScrapRequest; +import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindWithFilterRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpaceCreateRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpaceUpdateRequest; import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindByQueryRequest; @@ -27,6 +29,14 @@ public QueryCondition toQueryCond(PublicSpacesFindByQueryRequest request) { request.filter()); } + public CursorPageQueryCondition toCursorPageQueryCondition(PublicSpacesFindWithFilterRequest request) { + return new CursorPageQueryCondition( + request.pageable(), + request.lastFavoriteCount(), + request.lastSpaceId() + ); + } + public Space toSpace(SpaceCreateRequest request, SpaceMember spaceMember, SpaceImage spaceImage) { return new Space( request.memberId(), diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d10603b7..4bec4cce 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,7 +16,7 @@ spring: url: ${LOKI_URL} datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://localhost:3306/link_hub_query_test?serverTimezone=Asia/Seoul&rewriteBatchedStatements=true + url: ${RDS_URL} username: ${RDS_USERNAME} password: ${RDS_PASSWORD} servlet: From 0b94ad97caa5b734cb96c447d14d07203b82b93d Mon Sep 17 00:00:00 2001 From: young970 Date: Tue, 30 Jan 2024 20:33:37 +0900 Subject: [PATCH 04/12] =?UTF-8?q?[Test]:=20=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=95=84=ED=84=B0=20=EC=A1=B0=ED=9A=8C,=20?= =?UTF-8?q?=EC=8A=A4=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/DefaultSpaceServiceTest.java | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/tenten/linkhub/domain/space/service/DefaultSpaceServiceTest.java b/src/test/java/com/tenten/linkhub/domain/space/service/DefaultSpaceServiceTest.java index 4725916c..79d6aa6c 100644 --- a/src/test/java/com/tenten/linkhub/domain/space/service/DefaultSpaceServiceTest.java +++ b/src/test/java/com/tenten/linkhub/domain/space/service/DefaultSpaceServiceTest.java @@ -9,6 +9,7 @@ import com.tenten.linkhub.domain.link.exception.LinkViewHistoryException; import com.tenten.linkhub.domain.link.facade.LinkFacade; import com.tenten.linkhub.domain.link.facade.dto.LinkCreateFacadeRequest; +import com.tenten.linkhub.domain.space.common.SpaceCursorPageRequest; import com.tenten.linkhub.domain.space.model.category.Category; import com.tenten.linkhub.domain.space.model.space.Role; import com.tenten.linkhub.domain.space.model.space.Space; @@ -17,9 +18,11 @@ import com.tenten.linkhub.domain.space.repository.space.SpaceJpaRepository; import com.tenten.linkhub.domain.space.service.dto.space.MemberSpacesFindRequest; import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindByQueryRequest; +import com.tenten.linkhub.domain.space.service.dto.space.PublicSpacesFindWithFilterRequest; import com.tenten.linkhub.domain.space.service.dto.space.SpaceTagGetResponses; import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindByQueryResponse; import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindByQueryResponses; +import com.tenten.linkhub.domain.space.service.dto.space.SpacesFindWithCursorResponses; import com.tenten.linkhub.domain.space.service.dto.spacemember.SpaceMemberRoleChangeRequest; import com.tenten.linkhub.global.exception.PolicyViolationException; import com.tenten.linkhub.global.exception.UnauthorizedAccessException; @@ -69,7 +72,7 @@ void tearDown() { @Test @DisplayName("유저는 올바른 키워드, 필터, 정렬 조건들을 통해 Space를 검색할 수 있다.") - void findSpacesByQuery() { + void searchPublicSpacesByQuery() { //given PageRequest pageRequest = PageRequest.of( 0, @@ -81,7 +84,7 @@ void findSpacesByQuery() { Category.KNOWLEDGE_ISSUE_CAREER); //when - SpacesFindByQueryResponses responses = spaceService.findPublicSpacesByQuery(request); + SpacesFindByQueryResponses responses = spaceService.searchPublicSpacesByQuery(request); //then List content = responses.responses().getContent(); @@ -94,6 +97,52 @@ void findSpacesByQuery() { assertThat(content.get(0).ownerNickName()).isEqualTo("잠자는 사자의 콧털"); } + @Test + @DisplayName("길이 2미만의 키워드로 검색 시 IllegalArgumentException가 발생한다.") + void searchPublicSpacesByQuery_sortKeyword_IllegalArgumentException() { + //given + PageRequest pageRequest = PageRequest.of( + 0, + 10, + Sort.by("created_at").descending()); + + PublicSpacesFindByQueryRequest request = new PublicSpacesFindByQueryRequest(pageRequest, + "첫", + Category.KNOWLEDGE_ISSUE_CAREER); + + //when //then + assertThatThrownBy(() -> spaceService.searchPublicSpacesByQuery(request)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("유저는 공개된 스페이스를 필터 조건과 함께 페이지네이션 조회할 수 있다.") + void findPublicSpacesWithFilter() { + //given + SpaceCursorPageRequest pageRequest = SpaceCursorPageRequest.of( + 10, + "created_at", + Category.KNOWLEDGE_ISSUE_CAREER); + + PublicSpacesFindWithFilterRequest request = new PublicSpacesFindWithFilterRequest(pageRequest, null, null); + + //when + SpacesFindWithCursorResponses response = spaceService.findPublicSpacesWithFilter(request); + + //then + List content = response.responses().getContent(); + + assertThat(content.size()).isEqualTo(2); + assertThat(content.get(0).spaceName()).isEqualTo("세번째 스페이스"); + assertThat(content.get(0).category()).isEqualTo(Category.KNOWLEDGE_ISSUE_CAREER); + assertThat(content.get(0).spaceImagePath()).isEqualTo("https://testimage3"); + assertThat(content.get(0).ownerNickName()).isEqualTo("백둥이"); + assertThat(content.get(1).spaceName()).isEqualTo("첫번째 스페이스"); + assertThat(content.get(1).category()).isEqualTo(Category.KNOWLEDGE_ISSUE_CAREER); + assertThat(content.get(1).spaceImagePath()).isEqualTo("https://testimage1"); + assertThat(content.get(1).ownerNickName()).isEqualTo("잠자는 사자의 콧털"); + } + @Test @DisplayName("유저는 키워드 필터 조건 없이 내가 아닌 특정 유저의 private을 제외한 public Space를 검색할 수 있다.") void findMemberSpacesByQuery_emptyKeyWord_emptyFilter() { From cc42e247e4847e1b4768882dcf54419beb620a77 Mon Sep 17 00:00:00 2001 From: young970 Date: Tue, 30 Jan 2024 20:35:17 +0900 Subject: [PATCH 05/12] =?UTF-8?q?[Docs]:=20=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=95=84=ED=84=B0=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../linkhub/domain/space/controller/SpaceController.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java b/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java index 24da671c..2b0e7b14 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java +++ b/src/main/java/com/tenten/linkhub/domain/space/controller/SpaceController.java @@ -283,11 +283,12 @@ public ResponseEntity deleteSpace( * 스페이스 필터 조회 API */ @Operation( - summary = "스페이스 필터 조회 API", description = "메인 페이지용 스페이스 필터 조회이며 pageNumber, pageSize, sort, filter를 받아 검색합니다. (sort, filter조건 없이 사용 가능합니다.)\n\n " + - "sort: {created_at, updated_at, favorite_count, view_count}\n\n " + + summary = "스페이스 필터 조회 API", description = "메인 페이지용 스페이스 필터 조회이며 lastSpaceId(정렬 조건 favorite_count인 경우 lastFavoriteCount 추가로 필요), pageSize, sort, filter를 받아 조회합니다. (sort, filter조건 없이 사용 가능합니다.)\n\n " + + "첫 페이지 조회의 경우 lastSpaceId 없이 요청하면 됩니다. (정렬 조건 favorite_count의 경우 lastSpaceId, lastFavoriteCount 둘다 없이 요청.)\n\n " + + "sort: {created_at, favorite_count}\n\n " + "filter: {ENTER_ART, LIFE_KNOWHOW_SHOPPING, HOBBY_LEISURE_TRAVEL, KNOWLEDGE_ISSUE_CAREER, ETC}", responses = { - @ApiResponse(responseCode = "200", description = "검색이 성공적으로 완료 되었습니다."), + @ApiResponse(responseCode = "200", description = "조회가 성공적으로 완료 되었습니다."), }) @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity findPublicSpacesWithFilter( From 5b1fbfac049d8574a44d79e281e73f4b2956bd4d Mon Sep 17 00:00:00 2001 From: young970 Date: Tue, 30 Jan 2024 20:53:43 +0900 Subject: [PATCH 06/12] =?UTF-8?q?[Chore]:=20spaces=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=20=EB=B3=80=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/migration/V3__update_index_to_space_table.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/resources/db/migration/V3__update_index_to_space_table.sql diff --git a/src/main/resources/db/migration/V3__update_index_to_space_table.sql b/src/main/resources/db/migration/V3__update_index_to_space_table.sql new file mode 100644 index 00000000..4100d1c4 --- /dev/null +++ b/src/main/resources/db/migration/V3__update_index_to_space_table.sql @@ -0,0 +1,4 @@ +DROP INDEX idx_spaces_created_at ON spaces; +DROP INDEX idx_spaces_favorite_count ON spaces; + +CREATE INDEX idx_spaces_favorite_count_id ON spaces (favorite_count desc, id desc); From 7ee082c7dc2d6f638f35ea13149372c974a064db Mon Sep 17 00:00:00 2001 From: young970 Date: Tue, 30 Jan 2024 20:54:32 +0900 Subject: [PATCH 07/12] =?UTF-8?q?[Refactor]:=20=ED=8A=B9=EC=A0=95=20?= =?UTF-8?q?=EB=A9=A4=EB=B2=84=EC=9D=98=20=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=EC=A0=95=EB=A0=AC=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/space/querydsl/SpaceQueryDslRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java index 809adf3f..b465cada 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java @@ -120,7 +120,7 @@ public Slice findMemberSpacesJoinSpaceImageByCo dynamicQueryFactory.eqSpaceName(condition.keyWord()), dynamicQueryFactory.eqCategory(condition.filter()) ) - .orderBy(space.createdAt.desc()) + .orderBy(space.id.desc()) .offset(condition.pageable().getOffset()) .limit(condition.pageable().getPageSize() + 1) .fetch(); From be17867ad7f0fff02c9f4997598431e87bc84611 Mon Sep 17 00:00:00 2001 From: young970 Date: Wed, 31 Jan 2024 19:05:15 +0900 Subject: [PATCH 08/12] =?UTF-8?q?[Fix]:=20=EC=A1=B4=EC=9E=AC=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=EC=8B=9C=20=EB=B0=9C=EC=83=9D=ED=95=98?= =?UTF-8?q?=EB=8A=94=20NPE=20=ED=95=B4=EA=B2=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (추가로 테스트 코드 수정) --- .../common/mapper/RepositoryDtoMapper.java | 16 ++++++++++++++++ .../space/querydsl/SpaceQueryDslRepository.java | 11 ++--------- .../space/service/DefaultSpaceServiceTest.java | 15 +++++++-------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/common/mapper/RepositoryDtoMapper.java b/src/main/java/com/tenten/linkhub/domain/space/repository/common/mapper/RepositoryDtoMapper.java index 6cacd2e1..becb0ba2 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/common/mapper/RepositoryDtoMapper.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/common/mapper/RepositoryDtoMapper.java @@ -1,5 +1,8 @@ package com.tenten.linkhub.domain.space.repository.common.mapper; +import com.tenten.linkhub.domain.space.common.SpaceCursorPageRequest; +import com.tenten.linkhub.domain.space.common.SpaceCursorSlice; +import com.tenten.linkhub.domain.space.model.space.Space; import com.tenten.linkhub.domain.space.model.space.SpaceImage; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndOwnerNickName; import com.tenten.linkhub.domain.space.repository.common.dto.SpaceAndSpaceImageOwnerNickName; @@ -7,6 +10,7 @@ import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; @Component @@ -29,4 +33,16 @@ public List toSpaceAndSpaceImageOwnerNickNames( .collect(Collectors.toList()); } + public SpaceCursorSlice toSpaceCursorSlice(List contents, SpaceCursorPageRequest pageable, boolean hasNext){ + Space lastSpace = contents.isEmpty() ? null : contents.get(contents.size() - 1).space(); + + return SpaceCursorSlice.of( + Objects.isNull(lastSpace) ? null : lastSpace.getFavoriteCount(), + Objects.isNull(lastSpace) ? null : lastSpace.getId(), + pageable.pageSize(), + hasNext, + contents + ); + } + } diff --git a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java index b465cada..4e2ab44d 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java +++ b/src/main/java/com/tenten/linkhub/domain/space/repository/space/querydsl/SpaceQueryDslRepository.java @@ -18,6 +18,7 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Objects; import static com.tenten.linkhub.domain.member.model.QMember.member; import static com.tenten.linkhub.domain.space.model.space.QSpace.space; @@ -62,15 +63,7 @@ public SpaceCursorSlice findPublicSpacesJoinSpa hasNext = true; } - Space lastSpace = contents.get(contents.size() - 1).space(); - - return SpaceCursorSlice.of( - lastSpace.getFavoriteCount(), - lastSpace.getId(), - pageable.pageSize(), - hasNext, - contents - ); + return mapper.toSpaceCursorSlice(contents, pageable, hasNext); } public Slice searchPublicSpacesJoinSpaceImageByCondition(QueryCondition condition) { diff --git a/src/test/java/com/tenten/linkhub/domain/space/service/DefaultSpaceServiceTest.java b/src/test/java/com/tenten/linkhub/domain/space/service/DefaultSpaceServiceTest.java index 79d6aa6c..eade612e 100644 --- a/src/test/java/com/tenten/linkhub/domain/space/service/DefaultSpaceServiceTest.java +++ b/src/test/java/com/tenten/linkhub/domain/space/service/DefaultSpaceServiceTest.java @@ -157,9 +157,9 @@ void findMemberSpacesByQuery_emptyKeyWord_emptyFilter() { List content = response.responses().getContent(); assertThat(content.size()).isEqualTo(2); - assertThat(content.get(1).spaceName()).isEqualTo("세번째 스페이스"); - assertThat(content.get(1).spaceImagePath()).isEqualTo("https://testimage3"); - assertThat(content.get(1).ownerNickName()).isEqualTo("백둥이"); + assertThat(content.get(0).spaceName()).isEqualTo("세번째 스페이스"); + assertThat(content.get(0).spaceImagePath()).isEqualTo("https://testimage3"); + assertThat(content.get(0).ownerNickName()).isEqualTo("백둥이"); } @Test @@ -176,12 +176,11 @@ void findMemberSpacesByQuery_emptyKeyWord_emptyFilter_findMySpaces() { List content = response.responses().getContent(); assertThat(content.size()).isEqualTo(2); - assertThat(content.get(0).spaceName()).isEqualTo("첫번째 스페이스"); - assertThat(content.get(0).description()).isEqualTo("첫번째 스페이스 소개글"); - assertThat(content.get(0).category()).isEqualTo(Category.KNOWLEDGE_ISSUE_CAREER); - assertThat(content.get(0).spaceImagePath()).isEqualTo("https://testimage1"); + assertThat(content.get(0).spaceName()).isEqualTo("두번째 스페이스"); + assertThat(content.get(0).spaceImagePath()).isEqualTo("https://testimage2"); assertThat(content.get(0).ownerNickName()).isEqualTo("잠자는 사자의 콧털"); - assertThat(content.get(1).spaceName()).isEqualTo("두번째 스페이스"); + assertThat(content.get(1).spaceName()).isEqualTo("첫번째 스페이스"); + assertThat(content.get(1).spaceImagePath()).isEqualTo("https://testimage1"); assertThat(content.get(1).ownerNickName()).isEqualTo("잠자는 사자의 콧털"); } From 5588b1fcc8e2bf6503e2d33e19c147fda91528b1 Mon Sep 17 00:00:00 2001 From: young970 Date: Wed, 31 Jan 2024 19:07:54 +0900 Subject: [PATCH 09/12] =?UTF-8?q?[Docs]:=20=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=95=84=ED=84=B0=20=EC=A1=B0=ED=9A=8CAPI=20?= =?UTF-8?q?=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/space/PublicSpacesFindWithFilterApiRequest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpacesFindWithFilterApiRequest.java b/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpacesFindWithFilterApiRequest.java index 3b4faf8c..e024cf30 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpacesFindWithFilterApiRequest.java +++ b/src/main/java/com/tenten/linkhub/domain/space/controller/dto/space/PublicSpacesFindWithFilterApiRequest.java @@ -4,10 +4,10 @@ import io.swagger.v3.oas.annotations.media.Schema; public record PublicSpacesFindWithFilterApiRequest( - @Schema(title = "마지막 favoriteCount", example = "1") + @Schema(title = "마지막 favoriteCount", example = "0") Long lastFavoriteCount, - @Schema(title = "마지막 spaceId", example = "1") + @Schema(title = "마지막 spaceId", example = "62") Long lastSpaceId, @Schema(title = "페이지 크기", example = "10") From c87e406d7c5e1f76e7f3c3687622a106cbf1b6f5 Mon Sep 17 00:00:00 2001 From: young970 Date: Thu, 1 Feb 2024 20:07:39 +0900 Subject: [PATCH 10/12] =?UTF-8?q?[Fix]:=20=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=95=84=ED=84=B0=20=EC=A1=B0=ED=9A=8CAPI=20pageSi?= =?UTF-8?q?ze=2010=EC=9C=BC=EB=A1=9C=20=EC=9A=94=EC=B2=AD=20=EC=8B=9C=2011?= =?UTF-8?q?=EA=B0=9C=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/space/common/SpaceCursorPageRequest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorPageRequest.java b/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorPageRequest.java index 20565b9c..32051515 100644 --- a/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorPageRequest.java +++ b/src/main/java/com/tenten/linkhub/domain/space/common/SpaceCursorPageRequest.java @@ -5,17 +5,16 @@ import org.springframework.util.StringUtils; public record SpaceCursorPageRequest( - Integer pageSize, + int pageSize, Sort sort, Category filter ) { public static SpaceCursorPageRequest of( - Integer pageSize, + int pageSize, String sort, Category filter - ) - { + ) { return new SpaceCursorPageRequest( pageSize, StringUtils.hasText(sort) ? Sort.by(sort) : Sort.unsorted(), From 40e4a7c2f78ce3b8e5cfaf26844eae1b19904ad7 Mon Sep 17 00:00:00 2001 From: young970 Date: Fri, 2 Feb 2024 23:56:35 +0900 Subject: [PATCH 11/12] =?UTF-8?q?[Docs]:=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 102 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 7e71d82c..889305ab 100644 --- a/README.md +++ b/README.md @@ -13,26 +13,68 @@ - 슬랙 혹은 단톡방에서 팀원들이 해당 링크를 봤는지 확인하고 싶다. - 슬랙 혹은 카카오 단톡방에 링크를 공유하면 해당 링크를 본 인원들을 파악하고 싶다. - 지역 맛집 혹은 쇼핑몰 등을 검색하면 양질의 정보가 아닌 많은 광고들로 인해 정보를 필터링하는 추가적인 비용이 소모된다. -## 🔗 핵심 기능 🔗 -- **링크 아카이빙**: -
링크의 메타태그를 통해 자동으로 제목을 추천해주며 태그를 통해 추후 필터링의 용이성을 올림 - 스크린샷 2024-01-23 오전 1 38 16 - - -- **공유 아카이빙 공간**: -
스페이스 초대 기능을 통해 언제든지 팀원들을 초대할 수 있으며 viewer 및 editor 등의 세부 권한 지정이 가능하다. 또한 설정 시 팀원들의 링크 조회 이력을 표시할 수 있다. - 스크린샷 2024-01-23 오전 1 38 33 - - -- **즐겨찾기**: -
공개된 스페이스(링크 저장소)는 즐겨찾기 기능을 통해 구독을 할 수 있다. 또한 많은 유저들에게 즐겨찾기된 스페이스는 메인화면 즐겨찾기 순 옵션에서 상위에 노출되게 된다. - 스크린샷 2024-01-23 오전 1 38 47 - - -- **가져오기**: -
공개된 스페이스를(링크 저장소) 복사하여 편집 가능한 나의 스페이스로 만들 수 있다. 즐겨찾기와 다르게 원본의 이후 변경사항은 반영되지 않는다. - 스크린샷 2024-01-23 오전 1 39 06 +## 🔗 기능 시연 🔗 +### 메인 화면 및 검색 + 좋아요를 많이 받은 상위 링크들과 최신순 혹은 즐겨찾기 순의 스페이스(링크 저장소)를 필터링하여 보여준다. 또한 검색 기능을 제공하여 편의성 확보 +
+
+ +

메인 페이지

+
+
+ +

스페이스(링크 저장소) 검색

+
+
+ +

유저 검색

+
+
+ +### 링크 아카이빙 + 링크의 메타태그를 통해 자동으로 제목을 추천해주며 태그를 통해 추후 필터링의 용이성을 올림 +
+
+ +

링크 생성

+
+
+ +

링크 필터링

+
+
+ +### 공유 아카이빙 공간 + 스페이스 초대 기능을 통해 언제든지 팀원들을 초대할 수 있으며 viewer 및 editor 등의 세부 권한 지정이 가능하다. 또한 설정 시 팀원들의 링크 조회 이력을 표시할 수 있다. +
+
+ +

초대 하기

+
+
+ +

초대 받기

+
+
+ +

링크 조회 이력 표시

+
+
+ +### 즐겨찾기 + 공개된 스페이스(링크 저장소)는 즐겨찾기 기능을 통해 구독을 할 수 있다. 또한 많은 유저들에게 즐겨찾기된 스페이스는 메인화면 즐겨찾기 순 옵션에서 상위에 노출되게 된다. +
+ +

즐겨찾기

+
+ +### 가져오기 + 공개된 스페이스를(링크 저장소) 복사하여 편집 가능한 나의 스페이스로 만들 수 있다. 즐겨찾기와 다르게 원본의 이후 변경사항은 반영되지 않는다. +
+ +

가져오기

+
## 👨‍👩‍👦 서버 팀원 소개 | Team Leader |Developer | Developer | @@ -44,17 +86,17 @@ ## 💻 기술스택 ### 개발 환경 - - - - - - + + + + + + - - - - + + + + ### 인프라 @@ -80,7 +122,7 @@ ![스크린샷 2023-12-03 오후 4 44 07](https://github.com/Team-TenTen/LinkHub-BE/assets/90172648/15b82f59-5f85-4567-996d-66652ac44aa0) - + ## 문서 [📁 LinkHub API 명세서 ](https://www.notion.so/prgrms/API-c9e7dd4d09b246999a0022273810e4f7?pvs=4)
From 0d2d4361633d5bae7a9616dd6df089cf0f13a321 Mon Sep 17 00:00:00 2001 From: young970 Date: Sat, 3 Feb 2024 00:53:57 +0900 Subject: [PATCH 12/12] =?UTF-8?q?[Docs]:=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 71 ++++++++++++++++--------------------------------------- 1 file changed, 20 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 889305ab..386da290 100644 --- a/README.md +++ b/README.md @@ -16,65 +16,34 @@ ## 🔗 기능 시연 🔗 ### 메인 화면 및 검색 - 좋아요를 많이 받은 상위 링크들과 최신순 혹은 즐겨찾기 순의 스페이스(링크 저장소)를 필터링하여 보여준다. 또한 검색 기능을 제공하여 편의성 확보 -
-
- -

메인 페이지

-
-
- -

스페이스(링크 저장소) 검색

-
-
- -

유저 검색

-
-
+좋아요를 많이 받은 상위 링크들과 최신순 혹은 즐겨찾기 순의 스페이스(링크 저장소)를 필터링하여 보여준다. 또한 검색 기능을 제공하여 편의성 확보 +| 메인 페이지 | 스페이스(링크 저장소) 검색 | 유저 검색 | +|:---:|:---:|:---:| +| ![메인 페이지](https://github.com/Team-TenTen/LinkHub-BE/assets/108216455/14368d47-d597-4bac-a375-4886b10f3eb4) | ![스페이스(링크 저장소) 검색](https://github.com/Team-TenTen/LinkHub-BE/assets/108216455/4ace19c3-0419-4955-a4de-1b2e355046de) | ![유저 검색](https://github.com/Team-TenTen/LinkHub-BE/assets/108216455/de209ba9-8871-4953-b86e-18360a794269) | ### 링크 아카이빙 - 링크의 메타태그를 통해 자동으로 제목을 추천해주며 태그를 통해 추후 필터링의 용이성을 올림 -
-
- -

링크 생성

-
-
- -

링크 필터링

-
-
+링크의 메타태그를 통해 자동으로 제목을 추천해주며 태그를 통해 추후 필터링의 용이성을 올림 +| 링크 생성 | 링크 필터링 | +|:---:|:---:| +| | | ### 공유 아카이빙 공간 - 스페이스 초대 기능을 통해 언제든지 팀원들을 초대할 수 있으며 viewer 및 editor 등의 세부 권한 지정이 가능하다. 또한 설정 시 팀원들의 링크 조회 이력을 표시할 수 있다. -
-
- -

초대 하기

-
-
- -

초대 받기

-
-
- -

링크 조회 이력 표시

-
-
+스페이스 초대 기능을 통해 언제든지 팀원들을 초대할 수 있으며 viewer 및 editor 등의 세부 권한 지정이 가능하다. 또한 설정 시 팀원들의 링크 조회 이력을 표시할 수 있다. +| 초대 하기 | 초대 받기 | 링크 조회 이력 표시 | +|:---:|:---:|:---:| +| ![초대 하기](https://github.com/Team-TenTen/LinkHub-BE/assets/108216455/cae806df-b646-491d-97ba-611179d92cc7) | ![초대 받기](https://github.com/Team-TenTen/LinkHub-BE/assets/108216455/9ed2649d-49ce-4209-9136-84b29e2a938e) | ![링크 조회 이력 표시](https://github.com/Team-TenTen/LinkHub-BE/assets/108216455/e4748848-8b7e-41af-9a40-cb0de6524f28) | ### 즐겨찾기 - 공개된 스페이스(링크 저장소)는 즐겨찾기 기능을 통해 구독을 할 수 있다. 또한 많은 유저들에게 즐겨찾기된 스페이스는 메인화면 즐겨찾기 순 옵션에서 상위에 노출되게 된다. -
- -

즐겨찾기

-
+공개된 스페이스(링크 저장소)는 즐겨찾기 기능을 통해 구독을 할 수 있다. 또한 많은 유저들에게 즐겨찾기된 스페이스는 메인화면 즐겨찾기 순 옵션에서 상위에 노출되게 된다. +| 즐겨찾기 | +|:---:| +| | ### 가져오기 - 공개된 스페이스를(링크 저장소) 복사하여 편집 가능한 나의 스페이스로 만들 수 있다. 즐겨찾기와 다르게 원본의 이후 변경사항은 반영되지 않는다. -
- -

가져오기

-
+공개된 스페이스를(링크 저장소) 복사하여 편집 가능한 나의 스페이스로 만들 수 있다. 즐겨찾기와 다르게 원본의 이후 변경사항은 반영되지 않는다. +| 가져오기 | +|:---:| +| | ## 👨‍👩‍👦 서버 팀원 소개 | Team Leader |Developer | Developer |