From fc276d209572d421ed3ff8fc49a1a883a0b1360d Mon Sep 17 00:00:00 2001 From: marianddi Date: Sat, 23 May 2026 15:08:53 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat=20:=20=EB=AA=85=EB=B6=80=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20service,=20dto,=20repository?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20=EC=99=84=EB=A3=8C=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ .../dto/request/MemberListRequestDto.java | 15 +++++++++ .../dto/response/MemberListResponseDto.java | 26 +++++++++++++++ .../domain/repository/MemberRepository.java | 7 ++++ .../one/domain/service/MemberListService.java | 33 +++++++++++++++++++ .../global/pagination/RequestPagingDto.java | 29 ++++++++++++++++ .../global/pagination/ResponsePagingDto.java | 30 +++++++++++++++++ 7 files changed, 142 insertions(+) create mode 100644 src/main/java/org/one/domain/dto/request/MemberListRequestDto.java create mode 100644 src/main/java/org/one/domain/dto/response/MemberListResponseDto.java create mode 100644 src/main/java/org/one/domain/service/MemberListService.java create mode 100644 src/main/java/org/one/global/pagination/RequestPagingDto.java create mode 100644 src/main/java/org/one/global/pagination/ResponsePagingDto.java diff --git a/build.gradle b/build.gradle index 19b75b7..6b4a5b2 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,8 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testRuntimeOnly 'com.h2database:h2' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' } tasks.named('test') { diff --git a/src/main/java/org/one/domain/dto/request/MemberListRequestDto.java b/src/main/java/org/one/domain/dto/request/MemberListRequestDto.java new file mode 100644 index 0000000..d932957 --- /dev/null +++ b/src/main/java/org/one/domain/dto/request/MemberListRequestDto.java @@ -0,0 +1,15 @@ +package org.one.domain.dto.request; + +import lombok.Getter; +import lombok.Setter; +import org.one.global.pagination.RequestPagingDto; + +@Getter +@Setter +public class MemberListRequestDto extends RequestPagingDto { + public MemberListRequestDto(){ + this.setSize(1000); + this.setSort("createdAt"); + this.setDirection("ASC"); + } +} diff --git a/src/main/java/org/one/domain/dto/response/MemberListResponseDto.java b/src/main/java/org/one/domain/dto/response/MemberListResponseDto.java new file mode 100644 index 0000000..7007bea --- /dev/null +++ b/src/main/java/org/one/domain/dto/response/MemberListResponseDto.java @@ -0,0 +1,26 @@ +package org.one.domain.dto.response; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import lombok.Getter; +import org.one.domain.entity.Member; +import org.one.domain.enums.MemberStatus; + +@Getter +public class MemberListResponseDto { + private String name; + private String studentId; + private Integer grade; + private Integer age; + private String phoneNumber; + private MemberStatus status; + + public MemberListResponseDto(Member member){ + this.name = member.getName(); + this.studentId = member.getStudentId(); + this.grade = member.getGrade(); + this.age = member.getAge(); + this.phoneNumber = member.getPhoneNumber(); + this.status = member.getStatus(); + } +} diff --git a/src/main/java/org/one/domain/repository/MemberRepository.java b/src/main/java/org/one/domain/repository/MemberRepository.java index 187eea5..91659dd 100644 --- a/src/main/java/org/one/domain/repository/MemberRepository.java +++ b/src/main/java/org/one/domain/repository/MemberRepository.java @@ -1,10 +1,13 @@ package org.one.domain.repository; import org.one.domain.entity.Member; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import java.util.List; + /** * 정규 부원 데이터 조회와 일괄 갱신을 담당하는 JPA Repository입니다. */ @@ -24,4 +27,8 @@ public interface MemberRepository extends JpaRepository { @Modifying @Query("UPDATE Member m SET m.grade = m.grade + 1, m.age = m.age + 1") void incrementGradeAndAge(); + + //동아리에 소속중인 부원들의 모든 정보를 가져와 리스트로 만듦. + @Query("SELECT m FROM Member m") + List findAllByAdmin(Pageable pageable); } diff --git a/src/main/java/org/one/domain/service/MemberListService.java b/src/main/java/org/one/domain/service/MemberListService.java new file mode 100644 index 0000000..0a5a011 --- /dev/null +++ b/src/main/java/org/one/domain/service/MemberListService.java @@ -0,0 +1,33 @@ +package org.one.domain.service; + + +import lombok.RequiredArgsConstructor; +import org.one.domain.dto.request.MemberListRequestDto; +import org.one.domain.dto.response.MemberListResponseDto; +import org.one.domain.entity.Member; +import org.one.domain.repository.MemberRepository; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MemberListService { + private final MemberRepository memberRepository; + + public List getMemberListByAdmin(MemberListRequestDto requestDto) { + //requestDto로 설정한 sort, size 등을 바탕으로 Pageable객체를 만듦. + Pageable pageable = requestDto.toPageable(); + + //memberRepository를 이용해 모든 부원 리스트를 가져옴. + List members = memberRepository.findAllByAdmin(pageable); + + //모든 부원 리스트를 Member(entity) -> MemberListResponseDto로 필요한 데이터만 빼서 리스트를 만듦. + return members.stream() + .map(MemberListResponseDto::new) //(member -> new MemberListResponseDto(member)) + .toList(); + } +} diff --git a/src/main/java/org/one/global/pagination/RequestPagingDto.java b/src/main/java/org/one/global/pagination/RequestPagingDto.java new file mode 100644 index 0000000..9d1a208 --- /dev/null +++ b/src/main/java/org/one/global/pagination/RequestPagingDto.java @@ -0,0 +1,29 @@ +package org.one.global.pagination; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class RequestPagingDto { + + private int page = 0; + private int size = 10; + private String sort = "id"; + private String direction = "DESC"; + + public Pageable toPageable() { + Sort.Direction dir = Sort.Direction.fromString(direction); + return PageRequest.of(page, size, Sort.by(dir, sort)); + } +} \ No newline at end of file diff --git a/src/main/java/org/one/global/pagination/ResponsePagingDto.java b/src/main/java/org/one/global/pagination/ResponsePagingDto.java new file mode 100644 index 0000000..27d1509 --- /dev/null +++ b/src/main/java/org/one/global/pagination/ResponsePagingDto.java @@ -0,0 +1,30 @@ +package org.one.global.pagination; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.domain.Page; + +import java.util.List; + +@Getter +@Builder +public class ResponsePagingDto { + + private List content; + private int page; + private int size; + private long totalElements; + private int totalPages; + private boolean last; + + public static ResponsePagingDto from(Page page) { + return ResponsePagingDto.builder() + .content(page.getContent()) + .page(page.getNumber()) + .size(page.getSize()) + .totalElements(page.getTotalElements()) + .totalPages(page.getTotalPages()) + .last(page.isLast()) + .build(); + } +} \ No newline at end of file From 1f926c2d629401f8383b874d7e9b2e43291ccf5b Mon Sep 17 00:00:00 2001 From: marianddi Date: Sun, 24 May 2026 05:37:21 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat=20:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A0=84=EC=9A=A9=20=EB=AA=85=EB=B6=80=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/controller/MemberController.java | 44 +++++++++++++++++++ .../one/domain/member/enums/MemberStatus.java | 31 +++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/main/java/org/one/domain/controller/MemberController.java create mode 100644 src/main/java/org/one/domain/member/enums/MemberStatus.java diff --git a/src/main/java/org/one/domain/controller/MemberController.java b/src/main/java/org/one/domain/controller/MemberController.java new file mode 100644 index 0000000..7d88f9e --- /dev/null +++ b/src/main/java/org/one/domain/controller/MemberController.java @@ -0,0 +1,44 @@ +package org.one.domain.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.one.domain.dto.request.MemberListRequestDto; +import org.one.domain.dto.response.MemberListResponseDto; +import org.one.domain.service.MemberListService; +import org.one.global.pagination.ResponsePagingDto; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Tag(name = "Member", description = "부원 명부 관리 (관리자 전용)") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/members") +public class MemberController { + private final MemberListService memberListService; + + /** + * 명부 전체 조회 API + * 요청 시, 선택적으로 page관련 설정(정렬 등) + * + * 예시 : GET /api/members?page=1&size=10&sort=... + * + * 응답 데이터 : 전체 member의 명부리스트 + */ + @Operation(summary = "명부 전체 조회", description = "관리자 권한(ADMIN)이 있는 계정만 전체 부원 명부를 조회할 수 있습니다.") + @PreAuthorize("hasRole('ADMIN')") + @GetMapping + public ResponseEntity> getMemberList(@ModelAttribute MemberListRequestDto requestDto){ + + // + List response = memberListService.getMemberListByAdmin(requestDto); + + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/org/one/domain/member/enums/MemberStatus.java b/src/main/java/org/one/domain/member/enums/MemberStatus.java new file mode 100644 index 0000000..f89c3bf --- /dev/null +++ b/src/main/java/org/one/domain/member/enums/MemberStatus.java @@ -0,0 +1,31 @@ +package org.one.domain.member.enums; + +/** + * 정규 부원의 재적 상태를 표현합니다. + */ +public enum MemberStatus { + /** + * 재학 중인 상태입니다. + */ + ACTIVE, + + /** + * 군 복무로 휴학 중인 상태입니다. + */ + MILITARY_LEAVE, + + /** + * 졸업한 상태입니다. + */ + GRADUATED, + + /** + * 탈퇴한 상태입니다. + */ + WITHDRAWN, + + /** + * 일반 휴학 중인 상태입니다. + */ + ON_LEAVE +} From f482458ef6996d617ad521626e9ea9ed9b5680ff Mon Sep 17 00:00:00 2001 From: marianddi Date: Fri, 29 May 2026 04:42:59 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor=20:=20MemberListResponseDto=20?= =?UTF-8?q?=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?memberId=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80,=20=EC=9D=B4=20?= =?UTF-8?q?=EC=99=B8=20=EC=A3=BC=EC=84=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/one/domain/controller/MemberController.java | 2 +- .../org/one/domain/dto/response/MemberListResponseDto.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/one/domain/controller/MemberController.java b/src/main/java/org/one/domain/controller/MemberController.java index 7d88f9e..49bbbdf 100644 --- a/src/main/java/org/one/domain/controller/MemberController.java +++ b/src/main/java/org/one/domain/controller/MemberController.java @@ -27,7 +27,7 @@ public class MemberController { * 명부 전체 조회 API * 요청 시, 선택적으로 page관련 설정(정렬 등) * - * 예시 : GET /api/members?page=1&size=10&sort=... + * 예시 : GET /api/v1/members?page=1&size=10&sort=... * * 응답 데이터 : 전체 member의 명부리스트 */ diff --git a/src/main/java/org/one/domain/dto/response/MemberListResponseDto.java b/src/main/java/org/one/domain/dto/response/MemberListResponseDto.java index 7007bea..f95b508 100644 --- a/src/main/java/org/one/domain/dto/response/MemberListResponseDto.java +++ b/src/main/java/org/one/domain/dto/response/MemberListResponseDto.java @@ -1,13 +1,18 @@ package org.one.domain.dto.response; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import lombok.Getter; import org.one.domain.entity.Member; import org.one.domain.enums.MemberStatus; +@JsonPropertyOrder({"memberId", "name", "age", "studentId", "grade", "phoneNumber", "status"}) +@Schema(description = "명부 리스트 조회 응답") @Getter public class MemberListResponseDto { + private Long memberId; private String name; private String studentId; private Integer grade; @@ -16,6 +21,7 @@ public class MemberListResponseDto { private MemberStatus status; public MemberListResponseDto(Member member){ + this.memberId = member.getMemberId(); this.name = member.getName(); this.studentId = member.getStudentId(); this.grade = member.getGrade(); From c8d46ded711d0cd91ec9b097bfc2088c9b954bdd Mon Sep 17 00:00:00 2001 From: marianddi Date: Sun, 31 May 2026 06:08:00 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor=20:=20=EB=AA=85=EB=B6=80=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20api=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20=EB=B0=8F=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/controller/MemberController.java | 44 --------------- .../one/domain/member/enums/MemberStatus.java | 31 ----------- .../one/domain/service/MemberListService.java | 33 ------------ .../member/controller/MemberController.java | 48 ++++++++++++++++- .../dto}/MemberListRequestDto.java | 8 +-- .../dto}/MemberListResponseDto.java | 22 +++++--- .../one/member/service/MemberListService.java | 54 +++++++++++++++++++ 7 files changed, 121 insertions(+), 119 deletions(-) delete mode 100644 src/main/java/org/one/domain/controller/MemberController.java delete mode 100644 src/main/java/org/one/domain/member/enums/MemberStatus.java delete mode 100644 src/main/java/org/one/domain/service/MemberListService.java rename src/main/java/org/one/{domain/dto/request => member/dto}/MemberListRequestDto.java (58%) rename src/main/java/org/one/{domain/dto/response => member/dto}/MemberListResponseDto.java (58%) create mode 100644 src/main/java/org/one/member/service/MemberListService.java diff --git a/src/main/java/org/one/domain/controller/MemberController.java b/src/main/java/org/one/domain/controller/MemberController.java deleted file mode 100644 index 49bbbdf..0000000 --- a/src/main/java/org/one/domain/controller/MemberController.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.one.domain.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import org.one.domain.dto.request.MemberListRequestDto; -import org.one.domain.dto.response.MemberListResponseDto; -import org.one.domain.service.MemberListService; -import org.one.global.pagination.ResponsePagingDto; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; - -@Tag(name = "Member", description = "부원 명부 관리 (관리자 전용)") -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/v1/members") -public class MemberController { - private final MemberListService memberListService; - - /** - * 명부 전체 조회 API - * 요청 시, 선택적으로 page관련 설정(정렬 등) - * - * 예시 : GET /api/v1/members?page=1&size=10&sort=... - * - * 응답 데이터 : 전체 member의 명부리스트 - */ - @Operation(summary = "명부 전체 조회", description = "관리자 권한(ADMIN)이 있는 계정만 전체 부원 명부를 조회할 수 있습니다.") - @PreAuthorize("hasRole('ADMIN')") - @GetMapping - public ResponseEntity> getMemberList(@ModelAttribute MemberListRequestDto requestDto){ - - // - List response = memberListService.getMemberListByAdmin(requestDto); - - return ResponseEntity.ok(response); - } -} diff --git a/src/main/java/org/one/domain/member/enums/MemberStatus.java b/src/main/java/org/one/domain/member/enums/MemberStatus.java deleted file mode 100644 index f89c3bf..0000000 --- a/src/main/java/org/one/domain/member/enums/MemberStatus.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.one.domain.member.enums; - -/** - * 정규 부원의 재적 상태를 표현합니다. - */ -public enum MemberStatus { - /** - * 재학 중인 상태입니다. - */ - ACTIVE, - - /** - * 군 복무로 휴학 중인 상태입니다. - */ - MILITARY_LEAVE, - - /** - * 졸업한 상태입니다. - */ - GRADUATED, - - /** - * 탈퇴한 상태입니다. - */ - WITHDRAWN, - - /** - * 일반 휴학 중인 상태입니다. - */ - ON_LEAVE -} diff --git a/src/main/java/org/one/domain/service/MemberListService.java b/src/main/java/org/one/domain/service/MemberListService.java deleted file mode 100644 index 0a5a011..0000000 --- a/src/main/java/org/one/domain/service/MemberListService.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.one.domain.service; - - -import lombok.RequiredArgsConstructor; -import org.one.domain.dto.request.MemberListRequestDto; -import org.one.domain.dto.response.MemberListResponseDto; -import org.one.domain.entity.Member; -import org.one.domain.repository.MemberRepository; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class MemberListService { - private final MemberRepository memberRepository; - - public List getMemberListByAdmin(MemberListRequestDto requestDto) { - //requestDto로 설정한 sort, size 등을 바탕으로 Pageable객체를 만듦. - Pageable pageable = requestDto.toPageable(); - - //memberRepository를 이용해 모든 부원 리스트를 가져옴. - List members = memberRepository.findAllByAdmin(pageable); - - //모든 부원 리스트를 Member(entity) -> MemberListResponseDto로 필요한 데이터만 빼서 리스트를 만듦. - return members.stream() - .map(MemberListResponseDto::new) //(member -> new MemberListResponseDto(member)) - .toList(); - } -} diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index caaa123..eea3243 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -1,4 +1,50 @@ -package org.one.member.controller; +package org.one.domain.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.one.global.annotation.ApiErrorExceptions; +import org.one.global.dto.ApiResponse; +import org.one.global.enums.ErrorCode; +import org.one.global.pagination.ResponsePagingDto; +import org.one.member.dto.MemberListRequestDto; +import org.one.member.dto.MemberListResponseDto; +import org.one.member.service.MemberListService; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Tag(name = "Member", description = "부원 명부 관리 (관리자 전용)") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/members") public class MemberController { + private final MemberListService memberListService; + + /** + * 명부 전체 조회 API + * 요청 시, 선택적으로 page관련 설정(정렬 등) + * + * 예시 : GET /api/v1/members?page=1&size=10&sort=... + * + * 응답 데이터 : 전체 member의 명부리스트 + */ + @ApiErrorExceptions({ErrorCode.INVALID_INPUT}) + @Operation(summary = "명부 전체 조회", description = "관리자 권한(ADMIN)이 있는 계정만 전체 부원 명부를 조회할 수 있습니다.") + @PreAuthorize("hasRole('ADMIN')") + @GetMapping + public ResponseEntity>> getMemberList( + @ModelAttribute MemberListRequestDto requestDto){ + + List response = memberListService.getMemberListByAdmin(requestDto); + + return ResponseEntity.ok(ApiResponse.success(response, "명부 리스트 조회 결과입니다.")); + } } diff --git a/src/main/java/org/one/domain/dto/request/MemberListRequestDto.java b/src/main/java/org/one/member/dto/MemberListRequestDto.java similarity index 58% rename from src/main/java/org/one/domain/dto/request/MemberListRequestDto.java rename to src/main/java/org/one/member/dto/MemberListRequestDto.java index d932957..6f51fe8 100644 --- a/src/main/java/org/one/domain/dto/request/MemberListRequestDto.java +++ b/src/main/java/org/one/member/dto/MemberListRequestDto.java @@ -1,14 +1,14 @@ -package org.one.domain.dto.request; +package org.one.member.dto; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; -import lombok.Setter; import org.one.global.pagination.RequestPagingDto; +@Schema(description = "명부 리스트 조회 요청(페이지 설정)") @Getter -@Setter public class MemberListRequestDto extends RequestPagingDto { public MemberListRequestDto(){ - this.setSize(1000); + this.setSize(15); this.setSort("createdAt"); this.setDirection("ASC"); } diff --git a/src/main/java/org/one/domain/dto/response/MemberListResponseDto.java b/src/main/java/org/one/member/dto/MemberListResponseDto.java similarity index 58% rename from src/main/java/org/one/domain/dto/response/MemberListResponseDto.java rename to src/main/java/org/one/member/dto/MemberListResponseDto.java index f95b508..4560708 100644 --- a/src/main/java/org/one/domain/dto/response/MemberListResponseDto.java +++ b/src/main/java/org/one/member/dto/MemberListResponseDto.java @@ -1,26 +1,32 @@ -package org.one.domain.dto.response; +package org.one.member.dto; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; import lombok.Getter; -import org.one.domain.entity.Member; -import org.one.domain.enums.MemberStatus; +import org.one.member.domain.Member; +import org.one.member.enums.MemberStatus; + @JsonPropertyOrder({"memberId", "name", "age", "studentId", "grade", "phoneNumber", "status"}) @Schema(description = "명부 리스트 조회 응답") @Getter public class MemberListResponseDto { + @Schema(description = "부원 id") private Long memberId; + @Schema(description = "부원 이름") private String name; + @Schema(description = "부원 학번") private String studentId; + @Schema(description = "부원 학년") private Integer grade; + @Schema(description = "부원 나이") private Integer age; + @Schema(description = "부원 전화번호") private String phoneNumber; + @Schema(description = "부원 활동 상태") private MemberStatus status; - public MemberListResponseDto(Member member){ + private MemberListResponseDto(Member member){ this.memberId = member.getMemberId(); this.name = member.getName(); this.studentId = member.getStudentId(); @@ -29,4 +35,8 @@ public MemberListResponseDto(Member member){ this.phoneNumber = member.getPhoneNumber(); this.status = member.getStatus(); } + + public static MemberListResponseDto from(Member member){ + return new MemberListResponseDto(member); + } } diff --git a/src/main/java/org/one/member/service/MemberListService.java b/src/main/java/org/one/member/service/MemberListService.java new file mode 100644 index 0000000..f22059f --- /dev/null +++ b/src/main/java/org/one/member/service/MemberListService.java @@ -0,0 +1,54 @@ +package org.one.member.service; + + +import lombok.RequiredArgsConstructor; +import org.one.auth.repository.AdminRepository; +import org.one.global.enums.ErrorCode; +import org.one.global.exception.BusinessException; +import org.one.member.domain.Member; +import org.one.member.dto.MemberListRequestDto; +import org.one.member.dto.MemberListResponseDto; +import org.one.member.repository.MemberRepository; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MemberListService { + private final MemberRepository memberRepository; + private final AdminRepository adminRepository; + + public List getMemberListByAdmin(MemberListRequestDto requestDto) { + //size가 비정상적일 경우 예외처리 + if(requestDto.getSize() <= 0 ||requestDto.getSize() > 100){ + throw new BusinessException(ErrorCode.INVALID_INPUT); + } + + //허용되지 않는 정렬 값을 넣을 경우 예외처리 + String sort = requestDto.getSort(); + if(!"createdAt".equals(sort) && !"grade".equals(sort)){ + throw new BusinessException(ErrorCode.INVALID_INPUT); + } + + //오름차순 내림차순 외 예외처리 + String direction = requestDto.getDirection(); + if(!"ASC".equalsIgnoreCase(direction) && !"DESC".equalsIgnoreCase(direction)){ + throw new BusinessException(ErrorCode.INVALID_INPUT); + } + + //requestDto로 설정한 sort, size 등을 바탕으로 Pageable객체를 만듦. + Pageable pageable = requestDto.toPageable(); + + //memberRepository를 이용해 모든 부원 리스트를 가져옴. + List members = memberRepository.findAllByAdmin(pageable); + + //모든 부원 리스트를 Member(entity) -> MemberListResponseDto로 필요한 데이터만 빼서 리스트를 만듦. + return members.stream() + .map(MemberListResponseDto::from) + .toList(); + } +} From aa918deea9f27fbabb50719f455b43208faf93e8 Mon Sep 17 00:00:00 2001 From: marianddi Date: Mon, 1 Jun 2026 04:11:02 +0900 Subject: [PATCH 5/5] =?UTF-8?q?refactor=20:=20pagination=20>=20dto=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/pagination/RequestPagingDto.java | 5 +-- .../global/pagination/ResponsePagingDto.java | 10 ++--- .../member/controller/MemberController.java | 16 ++++---- .../one/member/dto/MemberListRequestDto.java | 38 ++++++++++++++++++- .../one/member/dto/MemberListResponseDto.java | 14 +++---- ...berListService.java => MemberService.java} | 19 +--------- 6 files changed, 59 insertions(+), 43 deletions(-) rename src/main/java/org/one/member/service/{MemberListService.java => MemberService.java} (64%) diff --git a/src/main/java/org/one/global/pagination/RequestPagingDto.java b/src/main/java/org/one/global/pagination/RequestPagingDto.java index 9d1a208..f61ee28 100644 --- a/src/main/java/org/one/global/pagination/RequestPagingDto.java +++ b/src/main/java/org/one/global/pagination/RequestPagingDto.java @@ -16,9 +16,8 @@ @NoArgsConstructor @AllArgsConstructor public class RequestPagingDto { - - private int page = 0; - private int size = 10; + private Integer page = 0; + private Integer size = 10; private String sort = "id"; private String direction = "DESC"; diff --git a/src/main/java/org/one/global/pagination/ResponsePagingDto.java b/src/main/java/org/one/global/pagination/ResponsePagingDto.java index 27d1509..4550e0e 100644 --- a/src/main/java/org/one/global/pagination/ResponsePagingDto.java +++ b/src/main/java/org/one/global/pagination/ResponsePagingDto.java @@ -11,11 +11,11 @@ public class ResponsePagingDto { private List content; - private int page; - private int size; - private long totalElements; - private int totalPages; - private boolean last; + private Integer page; + private Integer size; + private Long totalElements; + private Integer totalPages; + private Boolean last; public static ResponsePagingDto from(Page page) { return ResponsePagingDto.builder() diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index eea3243..730750c 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -2,18 +2,16 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.one.global.annotation.ApiErrorExceptions; import org.one.global.dto.ApiResponse; import org.one.global.enums.ErrorCode; -import org.one.global.pagination.ResponsePagingDto; import org.one.member.dto.MemberListRequestDto; import org.one.member.dto.MemberListResponseDto; -import org.one.member.service.MemberListService; +import org.one.member.service.MemberService; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @@ -26,13 +24,13 @@ @RequiredArgsConstructor @RequestMapping("/api/v1/members") public class MemberController { - private final MemberListService memberListService; + private final MemberService memberService; /** * 명부 전체 조회 API * 요청 시, 선택적으로 page관련 설정(정렬 등) * - * 예시 : GET /api/v1/members?page=1&size=10&sort=... + * 예시 : GET /api/v1/members?page=1&sort=... * * 응답 데이터 : 전체 member의 명부리스트 */ @@ -41,10 +39,10 @@ public class MemberController { @PreAuthorize("hasRole('ADMIN')") @GetMapping public ResponseEntity>> getMemberList( - @ModelAttribute MemberListRequestDto requestDto){ + @ModelAttribute @Valid MemberListRequestDto requestDto){ - List response = memberListService.getMemberListByAdmin(requestDto); + List response = memberService.getMemberListByAdmin(requestDto); - return ResponseEntity.ok(ApiResponse.success(response, "명부 리스트 조회 결과입니다.")); + return ResponseEntity.ok(ApiResponse.success(response)); } } diff --git a/src/main/java/org/one/member/dto/MemberListRequestDto.java b/src/main/java/org/one/member/dto/MemberListRequestDto.java index 6f51fe8..73f590d 100644 --- a/src/main/java/org/one/member/dto/MemberListRequestDto.java +++ b/src/main/java/org/one/member/dto/MemberListRequestDto.java @@ -1,15 +1,51 @@ package org.one.member.dto; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Pattern; import lombok.Getter; import org.one.global.pagination.RequestPagingDto; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; @Schema(description = "명부 리스트 조회 요청(페이지 설정)") -@Getter public class MemberListRequestDto extends RequestPagingDto { public MemberListRequestDto(){ + //기본값 세팅 + this.setPage(0); this.setSize(15); this.setSort("createdAt"); this.setDirection("ASC"); } + + @Override + @Schema(description = "페이지 번호 (0부터 시작)", example = "0") + @Min(value = 0, message = "페이지 번호는 0 이상이어야 합니다.") + public Integer getPage() { + return super.getPage(); + } + + @Override + @Schema(description = "한 페이지에 보여줄 부원 수 [15 고정]", example = "15") + @Min(value = 15, message = "명부 조회의 페이지 크기는 15로 고정되어 있습니다.") + @Max(value = 15, message = "명부 조회의 페이지 크기는 15로 고정되어 있습니다.") + public Integer getSize() { + return super.getSize(); + } + + @Override + @Schema(description = "정렬 기준 필드", example = "createdAt") + @Pattern(regexp = "^(createdAt|grade)$", message = "정렬 기준은 createdAt 또는 grade만 가능합니다.") + public String getSort() { + return super.getSort(); + } + + @Override + @Schema(description = "정렬 방향", example = "ASC") + @Pattern(regexp = "^(ASC|DESC)$", message = "정렬 방향은 ASC 또는 DESC만 가능합니다.") + public String getDirection() { + return super.getDirection(); + } } diff --git a/src/main/java/org/one/member/dto/MemberListResponseDto.java b/src/main/java/org/one/member/dto/MemberListResponseDto.java index 4560708..39f6984 100644 --- a/src/main/java/org/one/member/dto/MemberListResponseDto.java +++ b/src/main/java/org/one/member/dto/MemberListResponseDto.java @@ -11,19 +11,19 @@ @Schema(description = "명부 리스트 조회 응답") @Getter public class MemberListResponseDto { - @Schema(description = "부원 id") + @Schema(description = "부원 id", example = "2") private Long memberId; - @Schema(description = "부원 이름") + @Schema(description = "부원 이름", example = "홍길동") private String name; - @Schema(description = "부원 학번") + @Schema(description = "부원 학번", example = "20991234") private String studentId; - @Schema(description = "부원 학년") + @Schema(description = "부원 학년", example = "4") private Integer grade; - @Schema(description = "부원 나이") + @Schema(description = "부원 나이", example = "22") private Integer age; - @Schema(description = "부원 전화번호") + @Schema(description = "부원 전화번호", example = "010-1234-5678") private String phoneNumber; - @Schema(description = "부원 활동 상태") + @Schema(description = "부원 활동 상태", example = "ACTIVE") private MemberStatus status; private MemberListResponseDto(Member member){ diff --git a/src/main/java/org/one/member/service/MemberListService.java b/src/main/java/org/one/member/service/MemberService.java similarity index 64% rename from src/main/java/org/one/member/service/MemberListService.java rename to src/main/java/org/one/member/service/MemberService.java index f22059f..320301c 100644 --- a/src/main/java/org/one/member/service/MemberListService.java +++ b/src/main/java/org/one/member/service/MemberService.java @@ -18,28 +18,11 @@ @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class MemberListService { +public class MemberService { private final MemberRepository memberRepository; private final AdminRepository adminRepository; public List getMemberListByAdmin(MemberListRequestDto requestDto) { - //size가 비정상적일 경우 예외처리 - if(requestDto.getSize() <= 0 ||requestDto.getSize() > 100){ - throw new BusinessException(ErrorCode.INVALID_INPUT); - } - - //허용되지 않는 정렬 값을 넣을 경우 예외처리 - String sort = requestDto.getSort(); - if(!"createdAt".equals(sort) && !"grade".equals(sort)){ - throw new BusinessException(ErrorCode.INVALID_INPUT); - } - - //오름차순 내림차순 외 예외처리 - String direction = requestDto.getDirection(); - if(!"ASC".equalsIgnoreCase(direction) && !"DESC".equalsIgnoreCase(direction)){ - throw new BusinessException(ErrorCode.INVALID_INPUT); - } - //requestDto로 설정한 sort, size 등을 바탕으로 Pageable객체를 만듦. Pageable pageable = requestDto.toPageable();