Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

페이지네이션 처리 시 최대 다음 페이지 수를 5개로 제한 #902

Merged
merged 15 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
d5158ce
refactor(pagination): 고정 페이지네이션에 대한 목록 조회 객체 생성
jminkkk Nov 8, 2024
8b23deb
refactor(response): 고정 페이지네이션에 대한 목록 조회 응답 형식 변경
jminkkk Nov 8, 2024
678d4a0
refactor(pagination): 고정 페이지네이션에 사용되는 페이지 카운팅 객체 구현
jminkkk Nov 8, 2024
5f4c09b
refactor(pagination): 템플릿 목록 조회 시 전체 element 요소 카운트 제거 및 FixedPage 적용
jminkkk Nov 8, 2024
26e2705
refactor: 좋아요한 템플릿 목록 조회에 고정 페이지네이션 적용 및 TemplateRepository로 위치 이
jminkkk Nov 8, 2024
34b7613
Merge branch 'dev/be' into refactor/899-remove-full-pagecount
jminkkk Nov 9, 2024
514f9f1
refactor(pagination): list null 체크 제거
jminkkk Nov 12, 2024
24ab43a
refactor(pagination): @DisplayName 오타 수정
jminkkk Nov 12, 2024
83a6d49
refactor(pagination): 다음페이지가 없는 경우 현재 페이지인 1페이지 반환
jminkkk Nov 12, 2024
01411ff
refactor(repository): 메서드 인자 순서 통일되게 변경
jminkkk Nov 12, 2024
219fa24
refactor(repository): max page를 count하는 메서드명을 명확하 변경
jminkkk Nov 13, 2024
c55cfd2
refactor(template): 목록 조회 응답 시 고정 최대 페이지 변수명 변
jminkkk Nov 13, 2024
6c61b52
refactor(template): 좋아요한 템플릿 조회 Service 메서드 위치 이동
jminkkk Nov 13, 2024
79d8ac1
refactor(repository): query dsl select 라인 줄바
jminkkk Nov 13, 2024
d8d3873
refactor(template): 페이지네이션 페이지 수 응답 변수 이름 변
jminkkk Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package codezap.global.pagination;

import java.util.List;

public record FixedPage<T> (List<T> contents, int nextPages) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package codezap.global.pagination;

import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.EntityPathBase;
import com.querydsl.jpa.impl.JPAQueryFactory;

@Component
public class FixedPageCounter {

private static final int MAXIMUM_PAGE = 5;
zangsu marked this conversation as resolved.
Show resolved Hide resolved

public int countNextFixedPage(
JPAQueryFactory queryFactory,
EntityPathBase<?> entityPath,
Pageable pageable,
BooleanExpression... conditions
) {
int maximumElementsCount = pageable.getPageSize() * MAXIMUM_PAGE;
long nextFixedElementCounts = queryFactory
.selectFrom(entityPath)
.where(conditions)
.offset(pageable.getOffset())
.limit(maximumElementsCount)
.fetch()
.size();
zangsu marked this conversation as resolved.
Show resolved Hide resolved

return (int) Math.ceil((double) nextFixedElementCounts / pageable.getPageSize());
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package codezap.likes.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import codezap.likes.domain.Likes;
import codezap.member.domain.Member;
Expand All @@ -17,11 +13,4 @@ public interface LikesJpaRepository extends JpaRepository<Likes, Long> {
boolean existsByMemberAndTemplate(Member member, Template template);

long countByTemplate(Template template);

@Query("""
SELECT l.template
FROM Likes l
WHERE l.member.id = :memberId AND (l.template.member.id = :memberId OR l.template.visibility = 'PUBLIC')
""")
Page<Template> findAllByMemberId(@Param(value = "memberId") Long memberId, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import codezap.likes.domain.Likes;
Expand Down Expand Up @@ -37,8 +35,4 @@ public void deleteByMemberAndTemplate(Member member, Template template) {
public void deleteAllByTemplateIds(List<Long> templateIds) {
likesQueryDslRepository.deleteAllByTemplateIds(templateIds);
}

public Page<Template> findAllByMemberId(Long memberId, Pageable pageable) {
return likesJpaRepository.findAllByMemberId(memberId, pageable);
}
}
6 changes: 3 additions & 3 deletions backend/src/main/java/codezap/likes/service/LikesService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import codezap.global.pagination.FixedPage;
import codezap.likes.domain.Likes;
import codezap.likes.repository.LikesRepository;
import codezap.member.domain.Member;
Expand Down Expand Up @@ -37,8 +37,8 @@ public boolean isLiked(Member member, Template template) {
return likesRepository.existsByMemberAndTemplate(member, template);
}

public Page<Template> findAllByMemberId(Long memberId, Pageable pageable) {
return likesRepository.findAllByMemberId(memberId, pageable);
public FixedPage<Template> findAllByMemberId(Long memberId, Pageable pageable) {
zangsu marked this conversation as resolved.
Show resolved Hide resolved
return templateRepository.findAllLikedByMemberId(memberId, pageable);
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
import io.swagger.v3.oas.annotations.media.Schema;

public record FindAllTemplatesResponse(
@Schema(description = "전체 페이지 개수", example = "1")
int totalPages,
@Schema(description = "총 템플릿 개수", example = "134")
long totalElements,
@Schema(description = "다음 페이지 개수, 최대 5개입니다.", example = "1")
int nextPages,
@Schema(description = "템플릿 목록")
List<FindAllTemplateItemResponse> templates
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package codezap.template.repository;

import static codezap.template.domain.QTemplate.template;

import java.util.List;
import java.util.Objects;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;

import codezap.global.pagination.FixedPage;
import codezap.global.pagination.FixedPageCounter;
import codezap.likes.domain.QLikes;
import codezap.template.domain.QTemplate;
import codezap.template.domain.Template;
import codezap.template.domain.Visibility;
import lombok.RequiredArgsConstructor;
Expand All @@ -23,8 +22,9 @@ public class TemplateQueryDSLRepository {

private final JPAQueryFactory queryFactory;
private final TemplateSearchExpressionProvider expressionProvider;
private final FixedPageCounter fixedPageCounter;

public Page<Template> findTemplates(
public FixedPage<Template> findTemplates(
Long memberId,
String keyword,
Long categoryId,
Expand All @@ -33,8 +33,8 @@ public Page<Template> findTemplates(
Pageable pageable
) {
List<Template> content = getTemplates(memberId, keyword, categoryId, tagIds, visibility, pageable);
long count = count(memberId, keyword, categoryId, tagIds, visibility);
return new PageImpl<>(content, pageable, count);
int nextFixedPage = countNextFixedPage(pageable, memberId, keyword, categoryId, tagIds, visibility);
return new FixedPage<>(content, nextFixedPage);
}

private List<Template> getTemplates(
Expand All @@ -46,28 +46,30 @@ private List<Template> getTemplates(
Pageable pageable
) {
return queryFactory
.selectFrom(template)
.leftJoin(template.category).fetchJoin()
.leftJoin(template.member).fetchJoin()
.selectFrom(QTemplate.template)
.leftJoin(QTemplate.template.category).fetchJoin()
.leftJoin(QTemplate.template.member).fetchJoin()
.where(matchesKeyword(memberId, keyword, categoryId, tagIds, visibility))
.orderBy(TemplateOrderSpecifierUtils.getOrderSpecifier(pageable.getSort()))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
}

private long count(
private int countNextFixedPage(
Pageable pageable,
Long memberId,
String keyword,
Long categoryId,
List<Long> tagIds,
Visibility visibility
) {
return Objects.requireNonNull(queryFactory
.select(template.count())
.from(template)
.where(matchesKeyword(memberId, keyword, categoryId, tagIds, visibility))
.fetchOne());
return fixedPageCounter.countNextFixedPage(
queryFactory,
QTemplate.template,
pageable,
matchesKeyword(memberId, keyword, categoryId, tagIds, visibility)
);
}

private BooleanExpression[] matchesKeyword(
Expand All @@ -85,5 +87,35 @@ private BooleanExpression[] matchesKeyword(
expressionProvider.matchesKeyword(keyword)
};
}

public FixedPage<Template> findAllLikedByMemberId(Long memberId, Pageable pageable) {
List<Template> content = queryFactory
.select(QLikes.likes.template)
.from(QLikes.likes)
.where(isLikedTemplateByMember(memberId))
.orderBy(TemplateOrderSpecifierUtils.getOrderSpecifier(pageable.getSort()))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();

return new FixedPage<>(content, countNextFixedPage(memberId, pageable));
}

private int countNextFixedPage(Long memberId, Pageable pageable) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오버로딩을 사용한 것 같은데 개인적으로는 이해하기 조금 힘들었던 것 같아요.
매개변수에 따라서 역할이 드러나는 것 같지도 않아서 오버로딩이 적절한지 조금 고민이네요..
조금만 더 명시적으로 바꿔주는 건 어떤가요??

하지만 조금은 사소하다고 생각해서 몰리가 편한대로 결정해주세요!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 완전하게 동일한 역할이라고 생각했어요!
조회 조건이 달라질 수 있지만, 조건에 맞는 로우의 갯수를 조회하는 것이여서요!
초롱이 명시적으로 어떤 부분을 나타내고 싶은지 의견을 말해주면 제가 좀 더 고민해볼 수 있을 것 같아요 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그 조회 조건이 파라미터를 통해 구분되지 않는다는 것이 위험하다고 생각해요.
저는 개인적으로 오버로딩을 사용할 때는 메소드명과 파라미터만 봐도 그 메소드의 역할이 무엇인지 알고, 명확하게 구분되어야 한다고 생각해요.
하지만 현재는 로직을 봤을 때는 두 메소드가 구분이 되기는 하지만 파라미터만으로는 전혀 구분이 되지 않는 것 같아요.

제가 만약 두 메소드 중 하나를 사용하게 된다면, 어떤 메소드를 사용해야 할지 확인하기 어려울 것 같아요.

고정된 페이지를 가져온다라는 동일한 동작을 하더라도 그 동작을 하는 조건이 다르고 그 조건을 파라미터를 통해 나타낼 수 없다면 두 메소드는 다른 역할을 하는 것이라고 생각됩니다.
몰리의 의견은 어떤가요??

Copy link
Contributor Author

@jminkkk jminkkk Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 이해했습니다~!
예시로 좋아요한 템플릿 목록이 아닌 스크랩한 템플릿 목록이 생기면 초롱 의견처럼 구분이 불가능할 것 같네요 좋은 의견 감사합니다 👍

zangsu marked this conversation as resolved.
Show resolved Hide resolved
return fixedPageCounter.countNextFixedPage(
queryFactory,
QLikes.likes,
pageable,
isLikedTemplateByMember(memberId)
);
}

private static BooleanExpression isLikedTemplateByMember(Long memberId) {
BooleanExpression isLikedByMemberId = QLikes.likes.member.id.eq(memberId);
BooleanExpression isLikedTemplateByMemberId = QLikes.likes.template.member.id.eq(memberId);
BooleanExpression isTemplatePublic = QLikes.likes.template.visibility.eq(Visibility.PUBLIC);

return isLikedByMemberId.and(isLikedTemplateByMemberId.or(isTemplatePublic));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import codezap.global.exception.CodeZapException;
import codezap.global.exception.ErrorCode;
import codezap.global.pagination.FixedPage;
import codezap.template.domain.Template;
import codezap.template.domain.Visibility;
import lombok.RequiredArgsConstructor;
Expand All @@ -28,12 +28,16 @@ public List<Template> findByMemberId(Long id) {
return templateJpaRepository.findByMemberId(id);
}

public Page<Template> findAll(
public FixedPage<Template> findAll(
Long memberId, String keyword, Long categoryId, List<Long> tagIds, Visibility visibility, Pageable pageable
) {
return templateQueryDSLRepository.findTemplates(memberId, keyword, categoryId, tagIds, visibility, pageable);
}

public FixedPage<Template> findAllLikedByMemberId(Long memberId, Pageable pageable) {
return templateQueryDSLRepository.findAllLikedByMemberId(memberId, pageable);
}

public boolean existsByCategoryId(Long categoryId) {
return templateJpaRepository.existsByCategoryId(categoryId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
import java.util.HashSet;
import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import codezap.category.domain.Category;
import codezap.global.exception.CodeZapException;
import codezap.global.exception.ErrorCode;
import codezap.global.pagination.FixedPage;
import codezap.member.domain.Member;
import codezap.template.domain.Template;
import codezap.template.domain.Visibility;
Expand Down Expand Up @@ -45,7 +45,7 @@ public List<Template> getByMemberId(Long memberId) {
return templateRepository.findByMemberId(memberId);
}

public Page<Template> findAllBy(
public FixedPage<Template> findAllBy(
Long memberId,
String keyword,
Long categoryId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import jakarta.annotation.Nullable;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -13,10 +12,10 @@
import codezap.category.service.CategoryService;
import codezap.global.exception.CodeZapException;
import codezap.global.exception.ErrorCode;
import codezap.global.pagination.FixedPage;
import codezap.likes.service.LikedChecker;
import codezap.likes.service.LikesService;
import codezap.member.domain.Member;
import codezap.member.service.MemberService;
import codezap.tag.domain.Tag;
import codezap.tag.service.TagService;
import codezap.template.domain.SourceCode;
Expand Down Expand Up @@ -45,7 +44,6 @@ public class TemplateApplicationService {
private final TagService tagService;
private final ThumbnailService thumbnailService;
private final LikesService likesService;
private final MemberService memberService;

@Transactional
public Long create(Member member, CreateTemplateRequest createTemplateRequest) {
Expand Down Expand Up @@ -94,7 +92,7 @@ public FindAllTemplatesResponse findAllBy(
List<Long> tagIds,
Pageable pageable
) {
Page<Template> templates = templateService.findAllBy(
FixedPage<Template> templates = templateService.findAllBy(
memberId, keyword, categoryId, tagIds, Visibility.PUBLIC, pageable
);
return makeAllTemplatesResponse(templates, (template) -> false);
Expand All @@ -108,7 +106,7 @@ public FindAllTemplatesResponse findAllBy(
Pageable pageable,
Member loginMember
) {
Page<Template> templates = templateService.findAllBy(
FixedPage<Template> templates = templateService.findAllBy(
memberId, keyword, categoryId, tagIds, getVisibilityLevel(memberId, loginMember), pageable
);
return makeAllTemplatesResponse(templates, (template -> likesService.isLiked(loginMember, template)));
Expand All @@ -123,18 +121,17 @@ private Visibility getVisibilityLevel(Long memberId, Member loginMember) {
}

public FindAllTemplatesResponse findAllByLiked(Long memberId, Pageable pageable) {
Page<Template> likeTemplate = likesService.findAllByMemberId(memberId, pageable);
FixedPage<Template> likeTemplate = likesService.findAllByMemberId(memberId, pageable);
return makeAllTemplatesResponse(likeTemplate, (template -> true));
}

private FindAllTemplatesResponse makeAllTemplatesResponse(Page<Template> page, LikedChecker likedChecker) {
List<Template> templates = page.getContent();
private FindAllTemplatesResponse makeAllTemplatesResponse(FixedPage<Template> page, LikedChecker likedChecker) {
List<Template> templates = page.contents();
List<FindAllTemplateItemResponse> findAllTemplateByResponse =
getFindAllTemplateItemResponses(templates, likedChecker);

return new FindAllTemplatesResponse(
page.getTotalPages(),
page.getTotalElements(),
page.nextPages(),
findAllTemplateByResponse);
}

Expand Down
Loading