diff --git a/src/main/java/org/terning/terningserver/controller/HomeController.java b/src/main/java/org/terning/terningserver/controller/HomeController.java index 55482bb..8e52221 100644 --- a/src/main/java/org/terning/terningserver/controller/HomeController.java +++ b/src/main/java/org/terning/terningserver/controller/HomeController.java @@ -1,6 +1,8 @@ package org.terning.terningserver.controller; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; @@ -26,13 +28,13 @@ public class HomeController implements HomeSwagger { @GetMapping("/home") public ResponseEntity> getAnnouncements( @AuthenticationPrincipal Long userId, - @RequestParam(value = "sortBy", required = false, defaultValue = "deadlineSoon") String sortBy - ){ - HomeAnnouncementsResponseDto announcements = homeService.getAnnouncements(userId, sortBy); - + @RequestParam(value = "sortBy", required = false, defaultValue = "deadlineSoon") String sortBy, + @PageableDefault(size = 10) Pageable pageable) { + HomeAnnouncementsResponseDto announcements = homeService.getAnnouncements(userId, sortBy, pageable); return ResponseEntity.ok(SuccessResponse.of(SUCCESS_GET_ANNOUNCEMENTS, announcements)); } + @GetMapping("/home/upcoming") public ResponseEntity>> getUpcomingScraps( @AuthenticationPrincipal Long userId diff --git a/src/main/java/org/terning/terningserver/controller/swagger/HomeSwagger.java b/src/main/java/org/terning/terningserver/controller/swagger/HomeSwagger.java index 8b4cdb6..5e8a392 100644 --- a/src/main/java/org/terning/terningserver/controller/swagger/HomeSwagger.java +++ b/src/main/java/org/terning/terningserver/controller/swagger/HomeSwagger.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.terning.terningserver.dto.user.response.HomeAnnouncementsResponseDto; import org.terning.terningserver.dto.user.response.UpcomingScrapResponseDto; @@ -15,7 +16,8 @@ public interface HomeSwagger { @Operation(summary = "홈화면 > 나에게 딱맞는 인턴 공고 조회", description = "특정 사용자에 필터링 조건에 맞는 인턴 공고 정보를 조회하는 API") ResponseEntity> getAnnouncements( Long userId, - String sortBy + String sortBy, + Pageable pageable ); @Operation(summary = "홈화면 > 곧 마감인 스크랩 공고 조회", description = "곧 마감인 스크랩 공고를 조회하는 API") diff --git a/src/main/java/org/terning/terningserver/dto/user/response/HomeAnnouncementsResponseDto.java b/src/main/java/org/terning/terningserver/dto/user/response/HomeAnnouncementsResponseDto.java index 619b1c9..b32f2e7 100644 --- a/src/main/java/org/terning/terningserver/dto/user/response/HomeAnnouncementsResponseDto.java +++ b/src/main/java/org/terning/terningserver/dto/user/response/HomeAnnouncementsResponseDto.java @@ -6,12 +6,20 @@ @Builder public record HomeAnnouncementsResponseDto( - int totalCount, // 필터링 된 공고 총 개수 + int totalPages, + long totalCount, + boolean hasNext, List result ) { - public static HomeAnnouncementsResponseDto of(final int totalCount, final List announcements){ + public static HomeAnnouncementsResponseDto of( + final int totalPages, + final long totalCount, + final boolean hasNext, + final List announcements) { return HomeAnnouncementsResponseDto.builder() + .totalPages(totalPages) .totalCount(totalCount) + .hasNext(hasNext) .result(announcements) .build(); } diff --git a/src/main/java/org/terning/terningserver/repository/internship_announcement/InternshipRepositoryCustom.java b/src/main/java/org/terning/terningserver/repository/internship_announcement/InternshipRepositoryCustom.java index 0e3814b..c6eefcd 100644 --- a/src/main/java/org/terning/terningserver/repository/internship_announcement/InternshipRepositoryCustom.java +++ b/src/main/java/org/terning/terningserver/repository/internship_announcement/InternshipRepositoryCustom.java @@ -15,6 +15,5 @@ public interface InternshipRepositoryCustom { Page searchInternshipAnnouncement(String keyword, String sortBy, Pageable pageable); - List findFilteredInternshipsWithScrapInfo(User user, String sortBy); - + Page findFilteredInternshipsWithScrapInfo(User user, String sortBy, Pageable pageable); } diff --git a/src/main/java/org/terning/terningserver/repository/internship_announcement/InternshipRepositoryImpl.java b/src/main/java/org/terning/terningserver/repository/internship_announcement/InternshipRepositoryImpl.java index aeaf173..0b108de 100644 --- a/src/main/java/org/terning/terningserver/repository/internship_announcement/InternshipRepositoryImpl.java +++ b/src/main/java/org/terning/terningserver/repository/internship_announcement/InternshipRepositoryImpl.java @@ -110,9 +110,9 @@ private OrderSpecifier createOrderSpecifier(String sortBy) { } @Override - public List findFilteredInternshipsWithScrapInfo(User user, String sortBy){ - return jpaQueryFactory - .select(internshipAnnouncement, scrap.id, scrap.color) // tuple -> Scrap 정보 한번에 불러오기 + public Page findFilteredInternshipsWithScrapInfo(User user, String sortBy, Pageable pageable) { + List content = jpaQueryFactory + .select(internshipAnnouncement, scrap.id, scrap.color) .from(internshipAnnouncement) .leftJoin(internshipAnnouncement.scraps, scrap).on(scrap.user.eq(user)) .where( @@ -125,9 +125,23 @@ public List findFilteredInternshipsWithScrapInfo(User user, String sortBy sortAnnouncementsByDeadline().asc(), getSortOrder(sortBy) ) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) .fetch(); + + JPAQuery countQuery = jpaQueryFactory + .select(internshipAnnouncement.count()) + .from(internshipAnnouncement) + .where( + getJobTypeFilter(user), + getGraduatingFilter(user), + getWorkingPeriodFilter(user), + getStartDateFilter(user) + ); + + return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne); } - + private BooleanExpression getGraduatingFilter(User user){ if(user.getFilter().getGrade() != Grade.SENIOR){ return internshipAnnouncement.isGraduating.isFalse(); diff --git a/src/main/java/org/terning/terningserver/service/HomeService.java b/src/main/java/org/terning/terningserver/service/HomeService.java index c5a3345..65425c9 100644 --- a/src/main/java/org/terning/terningserver/service/HomeService.java +++ b/src/main/java/org/terning/terningserver/service/HomeService.java @@ -1,8 +1,9 @@ package org.terning.terningserver.service; +import org.springframework.data.domain.Pageable; import org.terning.terningserver.dto.user.response.HomeAnnouncementsResponseDto; public interface HomeService { - HomeAnnouncementsResponseDto getAnnouncements(Long userId, String sortBy); + HomeAnnouncementsResponseDto getAnnouncements(Long userId, String sortBy, Pageable pageable); } diff --git a/src/main/java/org/terning/terningserver/service/HomeServiceImpl.java b/src/main/java/org/terning/terningserver/service/HomeServiceImpl.java index 4990d2d..653de0d 100644 --- a/src/main/java/org/terning/terningserver/service/HomeServiceImpl.java +++ b/src/main/java/org/terning/terningserver/service/HomeServiceImpl.java @@ -2,6 +2,8 @@ import com.querydsl.core.Tuple; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.terning.terningserver.domain.InternshipAnnouncement; @@ -22,47 +24,71 @@ @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class HomeServiceImpl implements HomeService{ +public class HomeServiceImpl implements HomeService { private final InternshipRepository internshipRepository; private final UserRepository userRepository; @Override - public HomeAnnouncementsResponseDto getAnnouncements(Long userId, String sortBy){ - User user = userRepository.findById(userId).orElseThrow( - () -> new CustomException(ErrorMessage.NOT_FOUND_USER_EXCEPTION) - ); + public HomeAnnouncementsResponseDto getAnnouncements(Long userId, String sortBy, Pageable pageable) { + User user = getUserById(userId); - // 유저의 필터 정보가 없는 경우 - if(user.getFilter() == null){ - return HomeAnnouncementsResponseDto.of(0,List.of()); + if (user.getFilter() == null) { + return createEmptyResponse(); } - List announcements = internshipRepository.findFilteredInternshipsWithScrapInfo(user, sortBy); + Page pagedAnnouncements = internshipRepository.findFilteredInternshipsWithScrapInfo(user, sortBy, pageable); - // 해당하는 공고가 없는 경우 - if(announcements.isEmpty()){ - return HomeAnnouncementsResponseDto.of(0, List.of()); + if (pagedAnnouncements.isEmpty()) { + return createEmptyResponse(); } - List responseDtos = announcements.stream() + List responseDtos = mapToHomeResponseDtos(pagedAnnouncements); + + return createResponse(pagedAnnouncements, responseDtos); + } + + private User getUserById(Long userId) { + return userRepository.findById(userId) + .orElseThrow(() -> new CustomException(ErrorMessage.NOT_FOUND_USER_EXCEPTION)); + } + + private HomeAnnouncementsResponseDto createEmptyResponse() { + return HomeAnnouncementsResponseDto.of(0, 0, false, List.of()); + } + + private List mapToHomeResponseDtos(Page pagedAnnouncements) { + return pagedAnnouncements.getContent().stream() .map(tuple -> { InternshipAnnouncement announcement = tuple.get(internshipAnnouncement); Long scrapId = tuple.get(scrap.id); Color color = tuple.get(scrap.color); - boolean isScrapped = (scrapId != null); // 스크랩 여부 - // scrap 하지 않은 경우 color는 지정되지 않아야 한다. - String colorValue = (isScrapped && color != null) ? color.getColorValue() : null; + boolean isScrapped = isScrapped(scrapId); + String colorValue = determineColorValue(isScrapped, color); return HomeResponseDto.of(announcement, isScrapped, colorValue); }) .toList(); - - return HomeAnnouncementsResponseDto.of(responseDtos.size(), responseDtos); } private boolean isScrapped(Long scrapId) { return scrapId != null; } -} + + private String determineColorValue(boolean isScrapped, Color color) { + if (isScrapped && color != null) { + return color.getColorValue(); + } + return null; + } + + private HomeAnnouncementsResponseDto createResponse(Page pagedAnnouncements, List responseDtos) { + return HomeAnnouncementsResponseDto.of( + pagedAnnouncements.getTotalPages(), + pagedAnnouncements.getTotalElements(), + pagedAnnouncements.hasNext(), + responseDtos + ); + } +} \ No newline at end of file