diff --git a/README.md b/README.md index 4b29724..32416ad 100644 --- a/README.md +++ b/README.md @@ -353,7 +353,33 @@ public class Member extends BaseEntity { } ``` -### 11. 데이터베이스 스키마 +### 11. DTO 네이밍 규칙 + +#### 기본 원칙 + +- **API 계층**: `Dto` 접미사 유지 +- **Service 계층**: 역할에 따라 `Dto` 접미사 결정 + +#### 사용 예시 + +```java +// API 계층 (Dto 유지) +CreateMemberRequestDto, GetMemberResponseDto + +// Service 계층 - 비즈니스 핵심 (Dto 유지) +MemberDto + +// Service 계층 - 내부 전달 (Dto 제거) +MemberSearchCondition +``` + +#### 핵심 규칙 + +- **API 노출**: `Dto` 유지 +- **비즈니스 핵심**: `Dto` 유지 +- **내부 전달**: `Dto` 제거 + +### 12. 데이터베이스 스키마 **데이터베이스 테이블 구조는 `ddl/schema_entity.sql`에 정의되어 있습니다.** diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/controller/MemberController.java b/src/main/java/com/bio/bio_backend/domain/base/member/controller/MemberController.java index ab42e1a..89a8dce 100644 --- a/src/main/java/com/bio/bio_backend/domain/base/member/controller/MemberController.java +++ b/src/main/java/com/bio/bio_backend/domain/base/member/controller/MemberController.java @@ -11,6 +11,11 @@ import com.bio.bio_backend.domain.base.member.dto.MemberDto; import com.bio.bio_backend.domain.base.member.dto.GetMemberResponseDto; import com.bio.bio_backend.domain.base.member.dto.CreateMemberRequestDto; import com.bio.bio_backend.domain.base.member.dto.CreateMemberResponseDto; + +import com.bio.bio_backend.domain.base.member.dto.MemberSearchCondition; +import com.bio.bio_backend.domain.base.member.dto.GetMembersRequestDto; +import com.bio.bio_backend.domain.base.member.dto.GetMembersPagedRequestDto; +import com.bio.bio_backend.global.dto.PagedResult; import com.bio.bio_backend.domain.base.member.service.MemberService; import com.bio.bio_backend.domain.base.member.mapper.MemberMapper; import lombok.RequiredArgsConstructor; @@ -26,7 +31,7 @@ import com.bio.bio_backend.global.utils.SecurityUtils; import com.bio.bio_backend.global.utils.JwtUtils; import jakarta.servlet.http.HttpServletResponse; -import java.util.HashMap; + import java.util.List; @@ -60,21 +65,33 @@ public class MemberController { @LogExecution("회원 목록 조회") @Operation(summary = "회원 목록 조회", description = "활성화된 모든 회원의 목록을 조회합니다.") @ApiResponses({ - @ApiResponse(responseCode = "200", description = "회원 목록 조회 성공") + @ApiResponse(responseCode = "200", description = "회원 목록 조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청 파라미터", content = @Content(schema = @Schema(implementation = ApiResponseDto.class))) }) @GetMapping - public ResponseEntity>> getMembers() { - try { - List members = memberService.selectMemberListForDisplay(new HashMap<>()); - ApiResponseDto> apiResponse = ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS, members); - log.info("전체 회원 목록 조회 완료: {}명", members.size()); - - return ResponseEntity.ok(apiResponse); - } catch (Exception e) { - log.error("회원 목록 조회 중 오류 발생: {}", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(ApiResponseDto.fail(ApiResponseCode.COMMON_INTERNAL_SERVER_ERROR)); - } + public ResponseEntity>> getMembers(@ModelAttribute GetMembersRequestDto requestDto) { + MemberSearchCondition condition = memberMapper.toSearchCondition(requestDto); + List members = memberService.getMembers(condition); + ApiResponseDto> apiResponse = ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS, members); + log.info("회원 목록 조회 완료: {}명", members.size()); + + return ResponseEntity.ok(apiResponse); + } + + @LogExecution("회원 목록 조회 (페이지네이션)") + @Operation(summary = "회원 목록 조회 (페이지네이션)", description = "페이지네이션을 적용한 회원 목록을 조회합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "회원 목록 조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청 파라미터", content = @Content(schema = @Schema(implementation = ApiResponseDto.class))) + }) + @GetMapping("/paged") + public ResponseEntity>> getMembersPaged(@ModelAttribute GetMembersPagedRequestDto requestDto) { + MemberSearchCondition condition = memberMapper.toSearchCondition(requestDto); + PagedResult pagedMembers = memberService.getMembersPaged(condition, requestDto.getPage(), requestDto.getSize()); + ApiResponseDto> apiResponse = ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS, pagedMembers); + log.info("페이지네이션된 회원 목록 조회 완료: 페이지={}, 크기={}, 전체={}명", requestDto.getPage(), requestDto.getSize(), pagedMembers.getTotalElements()); + + return ResponseEntity.ok(apiResponse); } @LogExecution("로그아웃") @@ -84,18 +101,12 @@ public class MemberController { }) @PostMapping("/logout") public ResponseEntity> logout(HttpServletResponse response) { - try { - String userId = SecurityUtils.getCurrentUserId(); - memberService.deleteRefreshToken(userId); - // 모든 토큰 쿠키 삭제 - jwtUtils.deleteAllTokenCookies(response); - log.info("사용자 로그아웃 완료: {}", userId); - - return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS)); - } catch (Exception e) { - log.error("로그아웃 처리 중 오류 발생: {}", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(ApiResponseDto.fail(ApiResponseCode.COMMON_INTERNAL_SERVER_ERROR)); - } + String userId = SecurityUtils.getCurrentUserId(); + memberService.deleteRefreshToken(userId); + // 모든 토큰 쿠키 삭제 + jwtUtils.deleteAllTokenCookies(response); + log.info("사용자 로그아웃 완료: {}", userId); + + return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS)); } } diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/dto/GetMembersPagedRequestDto.java b/src/main/java/com/bio/bio_backend/domain/base/member/dto/GetMembersPagedRequestDto.java new file mode 100644 index 0000000..f302046 --- /dev/null +++ b/src/main/java/com/bio/bio_backend/domain/base/member/dto/GetMembersPagedRequestDto.java @@ -0,0 +1,25 @@ +package com.bio.bio_backend.domain.base.member.dto; + +import com.bio.bio_backend.global.dto.BasePagedRequestDto; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 회원 목록 조회 요청용 DTO (검색 조건 + 페이지네이션 포함) + * GET /members/paged + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class GetMembersPagedRequestDto extends BasePagedRequestDto { + + private String userId; + private String name; + private String email; + private String searchKeyword; + private String createdDateFrom; + private String createdDateTo; + private String lastLoginFrom; + private String lastLoginTo; +} diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/dto/GetMembersRequestDto.java b/src/main/java/com/bio/bio_backend/domain/base/member/dto/GetMembersRequestDto.java new file mode 100644 index 0000000..9089adf --- /dev/null +++ b/src/main/java/com/bio/bio_backend/domain/base/member/dto/GetMembersRequestDto.java @@ -0,0 +1,22 @@ +package com.bio.bio_backend.domain.base.member.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 회원 목록 조회 요청용 DTO (검색 조건만 포함) + * GET /members + */ +@Data +@NoArgsConstructor +public class GetMembersRequestDto { + + private String userId; + private String name; + private String email; + private String searchKeyword; + private String createdDateFrom; + private String createdDateTo; + private String lastLoginFrom; + private String lastLoginTo; +} diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/dto/MemberSearchCondition.java b/src/main/java/com/bio/bio_backend/domain/base/member/dto/MemberSearchCondition.java new file mode 100644 index 0000000..d70e5c0 --- /dev/null +++ b/src/main/java/com/bio/bio_backend/domain/base/member/dto/MemberSearchCondition.java @@ -0,0 +1,23 @@ +package com.bio.bio_backend.domain.base.member.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +/** + * 회원 검색 조건 + */ +@Builder +@Getter +@Setter +public class MemberSearchCondition { + + private String userId; + private String name; + private String email; + private String searchKeyword; + private String createdDateFrom; + private String createdDateTo; + private String lastLoginFrom; + private String lastLoginTo; +} diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/mapper/MemberMapper.java b/src/main/java/com/bio/bio_backend/domain/base/member/mapper/MemberMapper.java index e43f904..269dbbe 100644 --- a/src/main/java/com/bio/bio_backend/domain/base/member/mapper/MemberMapper.java +++ b/src/main/java/com/bio/bio_backend/domain/base/member/mapper/MemberMapper.java @@ -4,6 +4,9 @@ import com.bio.bio_backend.domain.base.member.dto.CreateMemberRequestDto; import com.bio.bio_backend.domain.base.member.dto.CreateMemberResponseDto; import com.bio.bio_backend.domain.base.member.dto.LoginResponseDto; import com.bio.bio_backend.domain.base.member.dto.MemberDto; +import com.bio.bio_backend.domain.base.member.dto.MemberSearchCondition; +import com.bio.bio_backend.domain.base.member.dto.GetMembersRequestDto; +import com.bio.bio_backend.domain.base.member.dto.GetMembersPagedRequestDto; import com.bio.bio_backend.domain.base.member.dto.GetMemberResponseDto; import com.bio.bio_backend.domain.base.member.entity.Member; import com.bio.bio_backend.global.annotation.IgnoreBaseEntityMapping; @@ -68,4 +71,14 @@ public interface MemberMapper { * Member 엔티티 리스트를 GetMemberResponseDto 리스트로 변환 (민감한 정보 제외) */ List toGetMemberResponseDtoList(List members); + + /** + * GetMembersRequestDto를 MemberSearchCondition으로 변환 + */ + MemberSearchCondition toSearchCondition(GetMembersRequestDto request); + + /** + * GetMembersPagedRequestDto를 MemberSearchCondition으로 변환 + */ + MemberSearchCondition toSearchCondition(GetMembersPagedRequestDto request); } diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepository.java b/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepository.java index 09be690..f1f18e3 100644 --- a/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepository.java +++ b/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepository.java @@ -1,5 +1,7 @@ package com.bio.bio_backend.domain.base.member.repository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -14,4 +16,7 @@ public interface MemberRepository extends JpaRepository, MemberRep // 활성화된 회원 목록 조회 List findByUseFlagTrue(); + + // 활성화된 회원 목록 조회 (페이지네이션) + Page findByUseFlagTrue(Pageable pageable); } \ No newline at end of file diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepositoryCustom.java b/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepositoryCustom.java index 52f257b..5c4ee9c 100644 --- a/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepositoryCustom.java +++ b/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepositoryCustom.java @@ -1,6 +1,10 @@ package com.bio.bio_backend.domain.base.member.repository; +import com.bio.bio_backend.domain.base.member.dto.MemberSearchCondition; import com.bio.bio_backend.domain.base.member.entity.Member; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import java.util.List; import java.util.Optional; public interface MemberRepositoryCustom { @@ -12,4 +16,21 @@ public interface MemberRepositoryCustom { * @return Optional 회원 정보 (없으면 empty) */ Optional findActiveMemberByUserId(String userId); + + /** + * QueryDSL을 사용한 회원 목록 조회 (검색 조건 + 페이징) + * + * @param condition 검색 조건 + * @param pageable 페이징 정보 + * @return Page 검색된 회원 목록 + */ + Page findMembers(MemberSearchCondition condition, Pageable pageable); + + /** + * QueryDSL을 사용한 회원 목록 조회 (검색 조건) + * + * @param condition 검색 조건 + * @return List 검색된 회원 목록 + */ + List findMembers(MemberSearchCondition condition); } diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepositoryImpl.java b/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepositoryImpl.java index 4ffe170..cc71ceb 100644 --- a/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepositoryImpl.java +++ b/src/main/java/com/bio/bio_backend/domain/base/member/repository/MemberRepositoryImpl.java @@ -1,11 +1,20 @@ package com.bio.bio_backend.domain.base.member.repository; +import com.bio.bio_backend.domain.base.member.dto.MemberSearchCondition; import com.bio.bio_backend.domain.base.member.entity.Member; import com.bio.bio_backend.domain.base.member.entity.QMember; +import com.querydsl.core.BooleanBuilder; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Repository; +import org.springframework.util.StringUtils; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Optional; /** @@ -30,4 +39,91 @@ public class MemberRepositoryImpl implements MemberRepositoryCustom { return Optional.ofNullable(foundMember); } + + @Override + public Page findMembers(MemberSearchCondition condition, Pageable pageable) { + BooleanBuilder builder = buildSearchConditions(condition); + + List members = queryFactory + .selectFrom(member) + .where(builder) + .orderBy(member.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + Long total = queryFactory + .select(member.count()) + .from(member) + .where(builder) + .fetchOne(); + + return new PageImpl<>(members, pageable, total); + } + + @Override + public List findMembers(MemberSearchCondition condition) { + return queryFactory + .selectFrom(member) + .where(buildSearchConditions(condition)) + .orderBy(member.createdAt.desc()) + .fetch(); + } + + /** + * 검색 조건들을 조합하여 BooleanBuilder 생성 + */ + private BooleanBuilder buildSearchConditions(MemberSearchCondition condition) { + BooleanBuilder builder = new BooleanBuilder(); + + // 기본 조건: useFlag = true (활성화된 회원만 조회) + builder.and(member.useFlag.eq(true)); + + // 사용자 ID (정확 일치) + if (StringUtils.hasText(condition.getUserId())) { + builder.and(member.userId.eq(condition.getUserId())); + } + + // 이름 (부분 일치) + if (StringUtils.hasText(condition.getName())) { + builder.and(member.name.containsIgnoreCase(condition.getName())); + } + + // 이메일 (부분 일치) + if (StringUtils.hasText(condition.getEmail())) { + builder.and(member.email.containsIgnoreCase(condition.getEmail())); + } + + // 통합 검색 키워드 (이름, 이메일, 사용자ID에서 검색) + if (StringUtils.hasText(condition.getSearchKeyword())) { + String keyword = condition.getSearchKeyword(); + builder.and(member.name.containsIgnoreCase(keyword) + .or(member.email.containsIgnoreCase(keyword)) + .or(member.userId.containsIgnoreCase(keyword))); + } + + + // 생성일 범위 + if (StringUtils.hasText(condition.getCreatedDateFrom())) { + LocalDate fromDate = LocalDate.parse(condition.getCreatedDateFrom(), DateTimeFormatter.ISO_LOCAL_DATE); + builder.and(member.createdAt.goe(fromDate.atStartOfDay())); + } + if (StringUtils.hasText(condition.getCreatedDateTo())) { + LocalDate toDate = LocalDate.parse(condition.getCreatedDateTo(), DateTimeFormatter.ISO_LOCAL_DATE); + builder.and(member.createdAt.loe(toDate.atTime(23, 59, 59))); + } + + // 마지막 로그인 범위 + if (StringUtils.hasText(condition.getLastLoginFrom())) { + LocalDate fromDate = LocalDate.parse(condition.getLastLoginFrom(), DateTimeFormatter.ISO_LOCAL_DATE); + builder.and(member.lastLoginAt.goe(fromDate.atStartOfDay())); + } + if (StringUtils.hasText(condition.getLastLoginTo())) { + LocalDate toDate = LocalDate.parse(condition.getLastLoginTo(), DateTimeFormatter.ISO_LOCAL_DATE); + builder.and(member.lastLoginAt.loe(toDate.atTime(23, 59, 59))); + } + + return builder; + } + } diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/service/MemberService.java b/src/main/java/com/bio/bio_backend/domain/base/member/service/MemberService.java index 3b4faef..e8054d8 100644 --- a/src/main/java/com/bio/bio_backend/domain/base/member/service/MemberService.java +++ b/src/main/java/com/bio/bio_backend/domain/base/member/service/MemberService.java @@ -5,9 +5,10 @@ import org.springframework.security.core.userdetails.UserDetailsService; import com.bio.bio_backend.domain.base.member.dto.MemberDto; import com.bio.bio_backend.domain.base.member.dto.GetMemberResponseDto; +import com.bio.bio_backend.domain.base.member.dto.MemberSearchCondition; +import com.bio.bio_backend.global.dto.PagedResult; import java.util.List; -import java.util.Map; public interface MemberService extends UserDetailsService { @@ -20,13 +21,20 @@ public interface MemberService extends UserDetailsService { void deleteRefreshToken(String id); void updateMember(MemberDto member); - - List selectMemberList(Map params); /** - * 회원 목록 조회 (민감한 정보 제외) - * @param params 검색 파라미터 + * 검색 조건에 따른 회원 목록 조회 + * @param condition 검색 조건 * @return GetMemberResponseDto 리스트 */ - List selectMemberListForDisplay(Map params); + List getMembers(MemberSearchCondition condition); + + /** + * 페이지네이션된 회원 목록 조회 + * @param condition 검색 조건 + * @param page 페이지 번호 + * @param size 페이지 크기 + * @return PagedResult 페이지네이션된 회원 목록 + */ + PagedResult getMembersPaged(MemberSearchCondition condition, int page, int size); } diff --git a/src/main/java/com/bio/bio_backend/domain/base/member/service/MemberServiceImpl.java b/src/main/java/com/bio/bio_backend/domain/base/member/service/MemberServiceImpl.java index 8d88672..47fabac 100644 --- a/src/main/java/com/bio/bio_backend/domain/base/member/service/MemberServiceImpl.java +++ b/src/main/java/com/bio/bio_backend/domain/base/member/service/MemberServiceImpl.java @@ -2,6 +2,8 @@ package com.bio.bio_backend.domain.base.member.service; import com.bio.bio_backend.domain.base.member.dto.MemberDto; import com.bio.bio_backend.domain.base.member.dto.GetMemberResponseDto; +import com.bio.bio_backend.domain.base.member.dto.MemberSearchCondition; +import com.bio.bio_backend.global.dto.PagedResult; import com.bio.bio_backend.domain.base.member.entity.Member; import com.bio.bio_backend.domain.base.member.mapper.MemberMapper; import com.bio.bio_backend.domain.base.member.repository.MemberRepository; @@ -13,12 +15,15 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Map; @Service @RequiredArgsConstructor @@ -89,18 +94,20 @@ public class MemberServiceImpl implements MemberService { member.setRefreshToken(null); memberRepository.save(member); } - - @Override - public List selectMemberList(Map params) { - List members = memberRepository.findByUseFlagTrue(); - - return memberMapper.toMemberDtoList(members); - } @Override - public List selectMemberListForDisplay(Map params) { - List members = memberRepository.findByUseFlagTrue(); + public List getMembers(MemberSearchCondition condition) { + List members = memberRepository.findMembers(condition); return memberMapper.toGetMemberResponseDtoList(members); } + + @Override + public PagedResult getMembersPaged(MemberSearchCondition condition, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + Page memberPage = memberRepository.findMembers(condition, pageable); + List members = memberMapper.toGetMemberResponseDtoList(memberPage.getContent()); + + return PagedResult.of(memberPage, members); + } } diff --git a/src/main/java/com/bio/bio_backend/global/dto/BasePagedRequestDto.java b/src/main/java/com/bio/bio_backend/global/dto/BasePagedRequestDto.java new file mode 100644 index 0000000..a8a3c34 --- /dev/null +++ b/src/main/java/com/bio/bio_backend/global/dto/BasePagedRequestDto.java @@ -0,0 +1,28 @@ +package com.bio.bio_backend.global.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Max; + +/** + * 기본 페이징 요청 DTO + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BasePagedRequestDto { + + @Min(value = 0, message = "페이지 번호는 0 이상이어야 합니다") + @Builder.Default + private int page = 0; + + @Min(value = 1, message = "페이지 크기는 1 이상이어야 합니다") + @Max(value = 100, message = "페이지 크기는 100 이하여야 합니다") + @Builder.Default + private int size = 10; +} diff --git a/src/main/java/com/bio/bio_backend/global/dto/BasePagedResponseDto.java b/src/main/java/com/bio/bio_backend/global/dto/BasePagedResponseDto.java new file mode 100644 index 0000000..9999938 --- /dev/null +++ b/src/main/java/com/bio/bio_backend/global/dto/BasePagedResponseDto.java @@ -0,0 +1,24 @@ +package com.bio.bio_backend.global.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * 기본 페이징 응답 DTO + * 모든 페이징 응답에서 공통으로 사용되는 필드들을 포함 + */ +@Data +@SuperBuilder +@NoArgsConstructor +public class BasePagedResponseDto { + + private int currentPage; // 현재 페이지 + private int totalPages; // 전체 페이지 수 + private long totalElements; // 전체 요소 수 + private int size; // 페이지 크기 + private boolean hasNext; // 다음 페이지 존재 여부 + private boolean hasPrevious; // 이전 페이지 존재 여부 + private boolean isFirst; // 첫 번째 페이지 여부 + private boolean isLast; // 마지막 페이지 여부 +} diff --git a/src/main/java/com/bio/bio_backend/global/dto/PagedResult.java b/src/main/java/com/bio/bio_backend/global/dto/PagedResult.java new file mode 100644 index 0000000..4c5f3a8 --- /dev/null +++ b/src/main/java/com/bio/bio_backend/global/dto/PagedResult.java @@ -0,0 +1,34 @@ +package com.bio.bio_backend.global.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.springframework.data.domain.Page; + +import java.util.List; + +/** + * 페이지네이션된 결과를 담는 제네릭 클래스 + * @param 페이지네이션할 데이터의 타입 + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class PagedResult extends BasePagedResponseDto { + + private List content; + + public static PagedResult of(Page page, List content) { + PagedResult result = new PagedResult<>(); + result.setContent(content); + result.setCurrentPage(page.getNumber()); + result.setTotalPages(page.getTotalPages()); + result.setTotalElements(page.getTotalElements()); + result.setSize(page.getSize()); + result.setHasNext(page.hasNext()); + result.setHasPrevious(page.hasPrevious()); + result.setFirst(page.isFirst()); + result.setLast(page.isLast()); + return result; + } +}