From fc276d209572d421ed3ff8fc49a1a883a0b1360d Mon Sep 17 00:00:00 2001 From: marianddi Date: Sat, 23 May 2026 15:08:53 +0900 Subject: [PATCH 01/25] =?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 02/25] =?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 6c5ad0d377a4ff9d0c4f98ad66df3f549270215c Mon Sep 17 00:00:00 2001 From: marianddi Date: Sun, 24 May 2026 07:33:56 +0900 Subject: [PATCH 03/25] =?UTF-8?q?feat=20:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=B6=80=EC=9B=90=20=EB=93=B1=EB=A1=9D=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/controller/MemberController.java | 40 +++++++++++++++---- .../dto/request/MemberRegisterRequestDto.java | 35 ++++++++++++++++ .../java/org/one/domain/entity/Member.java | 5 +++ .../member/controller/MemberController.java | 4 -- .../one/domain/member/enums/MemberStatus.java | 31 -------------- .../domain/repository/MemberRepository.java | 1 + ...berListService.java => MemberService.java} | 32 ++++++++++++++- 7 files changed, 104 insertions(+), 44 deletions(-) create mode 100644 src/main/java/org/one/domain/dto/request/MemberRegisterRequestDto.java delete mode 100644 src/main/java/org/one/domain/member/controller/MemberController.java delete mode 100644 src/main/java/org/one/domain/member/enums/MemberStatus.java rename src/main/java/org/one/domain/service/{MemberListService.java => MemberService.java} (51%) diff --git a/src/main/java/org/one/domain/controller/MemberController.java b/src/main/java/org/one/domain/controller/MemberController.java index 7d88f9e..15c5393 100644 --- a/src/main/java/org/one/domain/controller/MemberController.java +++ b/src/main/java/org/one/domain/controller/MemberController.java @@ -2,17 +2,15 @@ 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.domain.dto.request.MemberListRequestDto; +import org.one.domain.dto.request.MemberRegisterRequestDto; import org.one.domain.dto.response.MemberListResponseDto; -import org.one.domain.service.MemberListService; -import org.one.global.pagination.ResponsePagingDto; +import org.one.domain.service.MemberService; 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 org.springframework.web.bind.annotation.*; import java.util.List; @@ -21,7 +19,7 @@ @RequiredArgsConstructor @RequestMapping("/api/v1/members") public class MemberController { - private final MemberListService memberListService; + private final MemberService memberService; /** * 명부 전체 조회 API @@ -37,8 +35,34 @@ public class MemberController { public ResponseEntity> getMemberList(@ModelAttribute MemberListRequestDto requestDto){ // - List response = memberListService.getMemberListByAdmin(requestDto); + List response = memberService.getMemberListByAdmin(requestDto); return ResponseEntity.ok(response); } + + /** + * 부원 등록 API + * 요청 시, requestBody를 이용 MemberRegisterRequestDto 필드 입력 + * + * api 요청 예시 : POST /api/members + * + * requestBody 예시 + * "name" : "aa", + * "grade" : 1, + * "studentId" : "20991111", + * "age" : 22, + * "phoneNum" : "010-1111-2222" + * + * 응답 데이터 : 성공 메세지 + */ + @Operation(summary = "부원 등록", description = "관리자 권한(ADMIN)이 있는 계정만 전체 부원 명부를 조회할 수 있습니다.") + @PreAuthorize("hasRole('ADMIN')") + @PostMapping + public ResponseEntity registerMember(@RequestBody @Valid MemberRegisterRequestDto requestDto){ + //등록 정보를 service로 넘겨 부원 등록 진행 + memberService.registerMember(requestDto); + + //오류없이 넘어왔을 경우 성공 처리 + return ResponseEntity.ok("부원 등록이 성공적으로 완료되었습니다."); + } } diff --git a/src/main/java/org/one/domain/dto/request/MemberRegisterRequestDto.java b/src/main/java/org/one/domain/dto/request/MemberRegisterRequestDto.java new file mode 100644 index 0000000..981c318 --- /dev/null +++ b/src/main/java/org/one/domain/dto/request/MemberRegisterRequestDto.java @@ -0,0 +1,35 @@ +package org.one.domain.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Schema(description = "새로운 부원 등록 요청") +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class MemberRegisterRequestDto { + + @Schema(description = "부원 이름", example = "홍길동") + @NotBlank(message = "이름을 입력해주세요.") + private String name; + + @Schema(description = "부원 학년", example = "1") + @NotNull(message = "학년을 선택해주세요.") + private Integer grade; + + @Schema(description = "부원 학번", example = "20991111") + @NotBlank(message = "학번을 입력해주세요.") + private String studentId; + + @Schema(description = "부원 나이", example = "22") + @NotNull(message = "나이를 입력해주세요.") + private Integer age; + + @Schema(description = "부원 전화번호", example = "010-1111-2222") + @NotBlank(message = "전화번호를 입력해주세요.") + private String phoneNum; +} diff --git a/src/main/java/org/one/domain/entity/Member.java b/src/main/java/org/one/domain/entity/Member.java index 4104036..7d3a6c7 100644 --- a/src/main/java/org/one/domain/entity/Member.java +++ b/src/main/java/org/one/domain/entity/Member.java @@ -10,6 +10,9 @@ import jakarta.persistence.Table; import java.time.LocalDate; import java.time.LocalDateTime; + +import lombok.AllArgsConstructor; +import lombok.Builder; import org.one.domain.enums.Gender; import org.one.domain.enums.MemberStatus; import org.one.global.entity.BaseEntity; @@ -19,6 +22,8 @@ */ @Entity @Table(name = "member") +@Builder +@AllArgsConstructor public class Member extends BaseEntity { @Id diff --git a/src/main/java/org/one/domain/member/controller/MemberController.java b/src/main/java/org/one/domain/member/controller/MemberController.java deleted file mode 100644 index b87dbb0..0000000 --- a/src/main/java/org/one/domain/member/controller/MemberController.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.one.domain.member.controller; - -public class MemberController { -} 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/repository/MemberRepository.java b/src/main/java/org/one/domain/repository/MemberRepository.java index 91659dd..6d90267 100644 --- a/src/main/java/org/one/domain/repository/MemberRepository.java +++ b/src/main/java/org/one/domain/repository/MemberRepository.java @@ -31,4 +31,5 @@ public interface MemberRepository extends JpaRepository { //동아리에 소속중인 부원들의 모든 정보를 가져와 리스트로 만듦. @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/MemberService.java similarity index 51% rename from src/main/java/org/one/domain/service/MemberListService.java rename to src/main/java/org/one/domain/service/MemberService.java index 0a5a011..20641ef 100644 --- a/src/main/java/org/one/domain/service/MemberListService.java +++ b/src/main/java/org/one/domain/service/MemberService.java @@ -3,19 +3,23 @@ import lombok.RequiredArgsConstructor; import org.one.domain.dto.request.MemberListRequestDto; +import org.one.domain.dto.request.MemberRegisterRequestDto; import org.one.domain.dto.response.MemberListResponseDto; import org.one.domain.entity.Member; +import org.one.domain.enums.Gender; +import org.one.domain.enums.MemberStatus; 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.time.LocalDate; import java.util.List; @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class MemberListService { +public class MemberService { private final MemberRepository memberRepository; public List getMemberListByAdmin(MemberListRequestDto requestDto) { @@ -30,4 +34,30 @@ public List getMemberListByAdmin(MemberListRequestDto req .map(MemberListResponseDto::new) //(member -> new MemberListResponseDto(member)) .toList(); } + + /** + * 새로운 부원을 추가함 + * Param : MemberRegisterRequestDto 부원 추가 dto + */ + @Transactional + public void registerMember(MemberRegisterRequestDto requestDto){ + //요청데이터인 dto를 사용하여 새로운 Member엔티티 설정 + Member member = Member.builder() + .name(requestDto.getName()) + .grade(requestDto.getGrade()) + .studentId(requestDto.getStudentId()) + .age(requestDto.getAge()) + .phoneNumber(requestDto.getPhoneNum()) + .status(MemberStatus.ACTIVE) //status는 기본 값 , 테이블 오류로 아래는 더미 데이터임 -> 해결예정 + .department("웹응용소프트웨어공학과") + .birthday(LocalDate.of(2000, 1, 1)) + .gender(Gender.FEMALE) + .build(); + + //새로 생성한 부원 객체를 save(insert)해줌. + memberRepository.save(member); + + } + + } From 1dbf2d8ce80823c015e5054d2f012f959effcba6 Mon Sep 17 00:00:00 2001 From: marianddi Date: Sun, 24 May 2026 08:51:57 +0900 Subject: [PATCH 04/25] =?UTF-8?q?feat=20:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=B6=80=EC=9B=90=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=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 | 17 ++++++++ .../dto/response/MemberDetailResponseDto.java | 40 +++++++++++++++++++ .../org/one/domain/service/MemberService.java | 14 +++++++ 3 files changed, 71 insertions(+) create mode 100644 src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java diff --git a/src/main/java/org/one/domain/controller/MemberController.java b/src/main/java/org/one/domain/controller/MemberController.java index 15c5393..24a4568 100644 --- a/src/main/java/org/one/domain/controller/MemberController.java +++ b/src/main/java/org/one/domain/controller/MemberController.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import org.one.domain.dto.request.MemberListRequestDto; import org.one.domain.dto.request.MemberRegisterRequestDto; +import org.one.domain.dto.response.MemberDetailResponseDto; import org.one.domain.dto.response.MemberListResponseDto; import org.one.domain.service.MemberService; import org.springframework.http.ResponseEntity; @@ -65,4 +66,20 @@ public ResponseEntity registerMember(@RequestBody @Valid MemberRegisterR //오류없이 넘어왔을 경우 성공 처리 return ResponseEntity.ok("부원 등록이 성공적으로 완료되었습니다."); } + + /** + * 부원 상세 정보 조회 API : 부원 수정 시 정보를 불러오기 위한 api + * 요청 시, memberId를 @PathVariable로 url을 통해 전달 + * + * api 요청 예시 : GET /api/members/{memberId} + * + * 응답 데이터 : 특정 부원에 대한 상세 정보 + */ + @Operation(summary = "부원 상세 정보 조회", description = "관리자 권한(ADMIN)이 있는 계정만 부원 상세 정보를 조회할 수 있습니다.") + @PreAuthorize("hasRole('ADMIN')") + @GetMapping("/{memberId}") + public ResponseEntity getMemberDetail(@PathVariable Long memberId){ + MemberDetailResponseDto responseDto= memberService.getMemberDetail(memberId); + return ResponseEntity.ok(responseDto); + } } diff --git a/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java b/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java new file mode 100644 index 0000000..ee7d788 --- /dev/null +++ b/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java @@ -0,0 +1,40 @@ +package org.one.domain.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import org.one.domain.entity.Member; + +/** + * 멤버 상세 조회 성공 시 응답하는 dto + */ +@Schema(description = "부원 상세 정보 응답") +@Getter +public class MemberDetailResponseDto { + @Schema(description = "부원 이름") + private String name; + + @Schema(description = "부원 학년") + private Integer grade; + + @Schema(description = "부원 학번") + private String studentId; + + @Schema(description = "부원 나이") + private Integer age; + + @Schema(description = "부원 전화번호") + private String phoneNum; + + //생성자를 통해 Member엔티티에서 필요한 정보만 dto 필드로 저장 + public MemberDetailResponseDto(Member member){ + this.name = member.getName(); + this.grade = member.getGrade(); + this.studentId = member.getStudentId(); + this.age = member.getAge(); + this.phoneNum = member.getPhoneNumber(); + } +} diff --git a/src/main/java/org/one/domain/service/MemberService.java b/src/main/java/org/one/domain/service/MemberService.java index 20641ef..7e33ab7 100644 --- a/src/main/java/org/one/domain/service/MemberService.java +++ b/src/main/java/org/one/domain/service/MemberService.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import org.one.domain.dto.request.MemberListRequestDto; import org.one.domain.dto.request.MemberRegisterRequestDto; +import org.one.domain.dto.response.MemberDetailResponseDto; import org.one.domain.dto.response.MemberListResponseDto; import org.one.domain.entity.Member; import org.one.domain.enums.Gender; @@ -56,8 +57,21 @@ public void registerMember(MemberRegisterRequestDto requestDto){ //새로 생성한 부원 객체를 save(insert)해줌. memberRepository.save(member); + } + /** + * 요청값(memberId)를 통해 해당 부원의 상세 정보를 불러옴. + * Param : memberId + * return : MemberDetailResponseDto + */ + public MemberDetailResponseDto getMemberDetail(Long memberId){ + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new IllegalArgumentException("부원을 찾을 수 없습니다.")); + return new MemberDetailResponseDto(member); } + + + } From 09eda7b93584ecde7b41e0be39baefa6eaed95ba Mon Sep 17 00:00:00 2001 From: marianddi Date: Mon, 25 May 2026 10:45:01 +0900 Subject: [PATCH 05/25] =?UTF-8?q?feat=20:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=B6=80=EC=9B=90=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=20=EA=B5=AC=ED=98=84=20=EC=A4=91=20(request=20dto=EC=B6=94?= =?UTF-8?q?=EA=B0=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/MemberUpdateRequestDto.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/java/org/one/domain/dto/request/MemberUpdateRequestDto.java diff --git a/src/main/java/org/one/domain/dto/request/MemberUpdateRequestDto.java b/src/main/java/org/one/domain/dto/request/MemberUpdateRequestDto.java new file mode 100644 index 0000000..1198296 --- /dev/null +++ b/src/main/java/org/one/domain/dto/request/MemberUpdateRequestDto.java @@ -0,0 +1,34 @@ +package org.one.domain.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Schema(description = "부원 정보 수정 요청") +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class MemberUpdateRequestDto { + @Schema(description = "부원 이름", example = "홍길동") + @NotBlank(message = "이름을 입력해주세요.") + private String name; + + @Schema(description = "부원 학년", example = "1") + @NotNull(message = "학년을 선택해주세요.") + private Integer grade; + + @Schema(description = "부원 학번", example = "20991111") + @NotBlank(message = "학번을 입력해주세요.") + private String studentId; + + @Schema(description = "부원 나이", example = "22") + @NotNull(message = "나이를 입력해주세요.") + private Integer age; + + @Schema(description = "부원 전화번호", example = "010-1111-2222") + @NotBlank(message = "전화번호를 입력해주세요.") + private String phoneNum; +} From fe0ad2a90d88c7d9afd173ba7f109a4ad30b691c Mon Sep 17 00:00:00 2001 From: marianddi Date: Mon, 25 May 2026 11:11:42 +0900 Subject: [PATCH 06/25] =?UTF-8?q?fix=20:=20Member=20entity=ED=98=95?= =?UTF-8?q?=ED=83=9C=20=EB=B0=98=EC=98=81=ED=95=98=EC=97=AC=20=EC=88=98?= =?UTF-8?q?=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 | 4 ++-- src/main/java/org/one/domain/service/MemberService.java | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/one/domain/controller/MemberController.java b/src/main/java/org/one/domain/controller/MemberController.java index 15c5393..50eed5d 100644 --- a/src/main/java/org/one/domain/controller/MemberController.java +++ b/src/main/java/org/one/domain/controller/MemberController.java @@ -25,7 +25,7 @@ public class MemberController { * 명부 전체 조회 API * 요청 시, 선택적으로 page관련 설정(정렬 등) * - * 예시 : GET /api/members?page=1&size=10&sort=... + * api 요청 예시 : GET /api/v1/members?page=1&size=10&sort=... * * 응답 데이터 : 전체 member의 명부리스트 */ @@ -44,7 +44,7 @@ public ResponseEntity> getMemberList(@ModelAttribute * 부원 등록 API * 요청 시, requestBody를 이용 MemberRegisterRequestDto 필드 입력 * - * api 요청 예시 : POST /api/members + * api 요청 예시 : POST /api/v1/members * * requestBody 예시 * "name" : "aa", diff --git a/src/main/java/org/one/domain/service/MemberService.java b/src/main/java/org/one/domain/service/MemberService.java index 20641ef..e2b05c4 100644 --- a/src/main/java/org/one/domain/service/MemberService.java +++ b/src/main/java/org/one/domain/service/MemberService.java @@ -48,10 +48,7 @@ public void registerMember(MemberRegisterRequestDto requestDto){ .studentId(requestDto.getStudentId()) .age(requestDto.getAge()) .phoneNumber(requestDto.getPhoneNum()) - .status(MemberStatus.ACTIVE) //status는 기본 값 , 테이블 오류로 아래는 더미 데이터임 -> 해결예정 - .department("웹응용소프트웨어공학과") - .birthday(LocalDate.of(2000, 1, 1)) - .gender(Gender.FEMALE) + .status(MemberStatus.ACTIVE) //status는 기본 값(활동중) .build(); //새로 생성한 부원 객체를 save(insert)해줌. From dcbfe30dad0913deb77956f67149ec8233d4ea17 Mon Sep 17 00:00:00 2001 From: marianddi Date: Tue, 26 May 2026 02:21:22 +0900 Subject: [PATCH 07/25] =?UTF-8?q?docs=20:=20api=EC=84=A4=EB=AA=85=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EB=93=B1=EC=9D=84=20=EC=A0=95=ED=99=95?= =?UTF-8?q?=ED=95=9C=20=EC=84=A4=EB=AA=85=EC=9C=BC=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=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 | 6 +++--- .../one/domain/dto/response/MemberDetailResponseDto.java | 2 +- src/main/java/org/one/domain/service/MemberService.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/one/domain/controller/MemberController.java b/src/main/java/org/one/domain/controller/MemberController.java index 2dad631..1d7daa1 100644 --- a/src/main/java/org/one/domain/controller/MemberController.java +++ b/src/main/java/org/one/domain/controller/MemberController.java @@ -68,14 +68,14 @@ public ResponseEntity registerMember(@RequestBody @Valid MemberRegisterR } /** - * 부원 상세 정보 조회 API : 부원 수정 시 정보를 불러오기 위한 api + * 특정 부원 정보 조회 API : 부원 수정 시 정보를 불러오기 위한 api * 요청 시, memberId를 @PathVariable로 url을 통해 전달 * * api 요청 예시 : GET /api/members/{memberId} * - * 응답 데이터 : 특정 부원에 대한 상세 정보 + * 응답 데이터 : 특정 부원에 대한 정보 */ - @Operation(summary = "부원 상세 정보 조회", description = "관리자 권한(ADMIN)이 있는 계정만 부원 상세 정보를 조회할 수 있습니다.") + @Operation(summary = "부원 정보 가져오기", description = "관리자 권한(ADMIN)이 있는 계정만 부원 상세 정보를 조회할 수 있습니다.") @PreAuthorize("hasRole('ADMIN')") @GetMapping("/{memberId}") public ResponseEntity getMemberDetail(@PathVariable Long memberId){ diff --git a/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java b/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java index ee7d788..bb7b34a 100644 --- a/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java +++ b/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java @@ -9,7 +9,7 @@ import org.one.domain.entity.Member; /** - * 멤버 상세 조회 성공 시 응답하는 dto + * 특정 부원 정보 조회 성공 시 응답하는 dto */ @Schema(description = "부원 상세 정보 응답") @Getter diff --git a/src/main/java/org/one/domain/service/MemberService.java b/src/main/java/org/one/domain/service/MemberService.java index 738fe23..1d25c40 100644 --- a/src/main/java/org/one/domain/service/MemberService.java +++ b/src/main/java/org/one/domain/service/MemberService.java @@ -57,7 +57,7 @@ public void registerMember(MemberRegisterRequestDto requestDto){ } /** - * 요청값(memberId)를 통해 해당 부원의 상세 정보를 불러옴. + * 요청값(memberId)를 통해 해당 부원의 정보를 불러옴. * Param : memberId * return : MemberDetailResponseDto */ From acd9b94b6e24fb6b047136c39bbb3b1694124184 Mon Sep 17 00:00:00 2001 From: marianddi Date: Tue, 26 May 2026 03:20:59 +0900 Subject: [PATCH 08/25] =?UTF-8?q?feat=20:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=B6=80=EC=9B=90=20=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?api=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 | 23 +++++++++++++++++++ .../java/org/one/domain/entity/Member.java | 4 +++- .../org/one/domain/service/MemberService.java | 9 ++++++++ 3 files changed, 35 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 2dad631..1775ca0 100644 --- a/src/main/java/org/one/domain/controller/MemberController.java +++ b/src/main/java/org/one/domain/controller/MemberController.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import org.one.domain.dto.request.MemberListRequestDto; import org.one.domain.dto.request.MemberRegisterRequestDto; +import org.one.domain.dto.request.MemberUpdateRequestDto; import org.one.domain.dto.response.MemberDetailResponseDto; import org.one.domain.dto.response.MemberListResponseDto; import org.one.domain.service.MemberService; @@ -82,4 +83,26 @@ public ResponseEntity getMemberDetail(@PathVariable Lon MemberDetailResponseDto responseDto= memberService.getMemberDetail(memberId); return ResponseEntity.ok(responseDto); } + + /** + * 부원 정보 수정 api + * 요청 시, + * @PathVariable와 @requestBody를 통해 memberId와 MemberUpdateReqeustDto전달 + * + * api 요청 예시 : PATCH /api/members/{memberId} + * + * 응답 데이터 : 수정 완료 메시지 + */ + @Operation(summary = "부원 수정", description = "관리자 권한(ADMIN)이 있는 계정만 특정 부원의 정보를 일부 수정할 수 있습니다.") + @PreAuthorize("hasRole('ADMIN')") + @PatchMapping("/{memberId}") + public ResponseEntity updateMember( + @PathVariable Long memberId, + @RequestBody MemberUpdateRequestDto requestDto) + { + memberService.updateMember(memberId, requestDto); + + return ResponseEntity.ok("부원 정보가 성공적으로 수정되었습니다."); + } + } diff --git a/src/main/java/org/one/domain/entity/Member.java b/src/main/java/org/one/domain/entity/Member.java index dfc4345..c107ede 100644 --- a/src/main/java/org/one/domain/entity/Member.java +++ b/src/main/java/org/one/domain/entity/Member.java @@ -150,12 +150,14 @@ public Member(MemberStatus status, String name, String studentId, * 부원 기본 정보를 수정합니다. * * @param name 이름 + * @param studentId 학번 * @param phoneNumber 연락처 * @param grade 학년 * @param age 나이 */ - public void updateInfo(String name, String phoneNumber, Integer grade, Integer age) { + public void updateInfo(String name,String studentId, String phoneNumber, Integer grade, Integer age) { this.name = name; + this.studentId = studentId; this.phoneNumber = phoneNumber; this.grade = grade; this.age = age; diff --git a/src/main/java/org/one/domain/service/MemberService.java b/src/main/java/org/one/domain/service/MemberService.java index 738fe23..7463935 100644 --- a/src/main/java/org/one/domain/service/MemberService.java +++ b/src/main/java/org/one/domain/service/MemberService.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import org.one.domain.dto.request.MemberListRequestDto; import org.one.domain.dto.request.MemberRegisterRequestDto; +import org.one.domain.dto.request.MemberUpdateRequestDto; import org.one.domain.dto.response.MemberDetailResponseDto; import org.one.domain.dto.response.MemberListResponseDto; import org.one.domain.entity.Member; @@ -67,6 +68,14 @@ public MemberDetailResponseDto getMemberDetail(Long memberId){ return new MemberDetailResponseDto(member); } + @Transactional + public void updateMember(Long memberId, MemberUpdateRequestDto requestDto){ + Member member = memberRepository.findById(memberId) + .orElseThrow(()->new IllegalArgumentException("존재하지 않는 부원입니다.")); + + member.updateInfo(requestDto.getName(), requestDto.getStudentId(), requestDto.getPhoneNum(), requestDto.getGrade(), requestDto.getAge()); + } + From 463e40f9a245691a2a7e454a296c59b6aac939f7 Mon Sep 17 00:00:00 2001 From: marianddi Date: Tue, 26 May 2026 18:09:35 +0900 Subject: [PATCH 09/25] =?UTF-8?q?feat=20:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=B6=80=EC=9B=90=20=EC=82=AD=EC=A0=9C=20api=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/controller/MemberController.java | 22 +++++++++++++++++++ .../request/MemberDeleteListRequestDto.java | 18 +++++++++++++++ .../org/one/domain/service/MemberService.java | 8 +++++++ 3 files changed, 48 insertions(+) create mode 100644 src/main/java/org/one/domain/dto/request/MemberDeleteListRequestDto.java diff --git a/src/main/java/org/one/domain/controller/MemberController.java b/src/main/java/org/one/domain/controller/MemberController.java index 1775ca0..14e3358 100644 --- a/src/main/java/org/one/domain/controller/MemberController.java +++ b/src/main/java/org/one/domain/controller/MemberController.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.one.domain.dto.request.MemberDeleteListRequestDto; import org.one.domain.dto.request.MemberListRequestDto; import org.one.domain.dto.request.MemberRegisterRequestDto; import org.one.domain.dto.request.MemberUpdateRequestDto; @@ -105,4 +106,25 @@ public ResponseEntity updateMember( return ResponseEntity.ok("부원 정보가 성공적으로 수정되었습니다."); } + /** + * 부원 삭제 api + * 요청 시, + * @requestBody를 통해 memberId리스트를 전달 + * + * api 요청 예시 : Delete /api/members + * + * requestBody 예시 + * "memberIds" : [2, 7] + * + * 응답 데이터 : 삭제 완료 메시지 + */ + @Operation(summary = "부원 삭제", description = "관리자 권한(ADMIN)이 있는 계정만 부원을 삭제할 수 있습니다.") + @PreAuthorize("hasRole('ADMIN')") + @DeleteMapping + public ResponseEntity deleteMembers(@RequestBody @Valid MemberDeleteListRequestDto requestDto){ + memberService.deleteMembers(requestDto.getMemberIds()); + + return ResponseEntity.ok("선택하신 부원들이 성공적으로 삭제되었습니다."); + } + } diff --git a/src/main/java/org/one/domain/dto/request/MemberDeleteListRequestDto.java b/src/main/java/org/one/domain/dto/request/MemberDeleteListRequestDto.java new file mode 100644 index 0000000..fb4dcf4 --- /dev/null +++ b/src/main/java/org/one/domain/dto/request/MemberDeleteListRequestDto.java @@ -0,0 +1,18 @@ +package org.one.domain.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "부원 삭제 요청") +@Getter +@Setter +public class MemberDeleteListRequestDto { + @Schema(description = "삭제할 부원(들)의 id리스트") + @NotEmpty(message = "id리스트를 입력해주세요.") + private List memberIds; +} diff --git a/src/main/java/org/one/domain/service/MemberService.java b/src/main/java/org/one/domain/service/MemberService.java index 7463935..11437af 100644 --- a/src/main/java/org/one/domain/service/MemberService.java +++ b/src/main/java/org/one/domain/service/MemberService.java @@ -76,6 +76,14 @@ public void updateMember(Long memberId, MemberUpdateRequestDto requestDto){ member.updateInfo(requestDto.getName(), requestDto.getStudentId(), requestDto.getPhoneNum(), requestDto.getGrade(), requestDto.getAge()); } + /** + * 요청 값(memberIds)에 들어있는 id를 가진 데이터들을 삭제 + * Param : memberIds + */ + @Transactional + public void deleteMembers(List memberIds){ + memberRepository.deleteAllById(memberIds); + } From 1bb5cf2aac925d2516dab4dc3edc5a34f1d99a67 Mon Sep 17 00:00:00 2001 From: marianddi Date: Wed, 27 May 2026 14:47:17 +0900 Subject: [PATCH 10/25] =?UTF-8?q?feat=20:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20=EB=B6=80=EC=9B=90=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ApplicantMemberController.java | 43 +++++++++++++++++++ .../ApplicantMemberListRequestDto.java | 19 ++++++++ .../ApplicantMemberListResponseDto.java | 30 +++++++++++++ .../repository/ApplicantMemberRepository.java | 10 +++++ .../service/ApplicantMemberService.java | 34 +++++++++++++++ .../global/pagination/RequestPagingDto.java | 29 +++++++++++++ .../global/pagination/ResponsePagingDto.java | 30 +++++++++++++ 7 files changed, 195 insertions(+) create mode 100644 src/main/java/org/one/domain/controller/ApplicantMemberController.java create mode 100644 src/main/java/org/one/domain/dto/request/ApplicantMemberListRequestDto.java create mode 100644 src/main/java/org/one/domain/dto/response/ApplicantMemberListResponseDto.java create mode 100644 src/main/java/org/one/domain/service/ApplicantMemberService.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/src/main/java/org/one/domain/controller/ApplicantMemberController.java b/src/main/java/org/one/domain/controller/ApplicantMemberController.java new file mode 100644 index 0000000..78c12e2 --- /dev/null +++ b/src/main/java/org/one/domain/controller/ApplicantMemberController.java @@ -0,0 +1,43 @@ +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.ApplicantMemberListRequestDto; +import org.one.domain.dto.response.ApplicantMemberListResponseDto; +import org.one.domain.service.ApplicantMemberService; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Tag(name = "ApplicantMember", description = "신청 부원 관리 (관리자 전용)") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/applicantmembers") +public class ApplicantMemberController { + private final ApplicantMemberService applicantMemberService; + + + /** + * 신청 부원 조회 api + * + * api 요청 예시 : GET /api/v1/applicantmembers + * + * 응답 데이터 : 신청 부원 리스트 + */ + @Operation(summary = "신청 부원 조회", description = "관리자 권한(ADMIN)이 있는 계정만 신청 부원을 조회할 수 있습니다.") + @PreAuthorize("hasRole('ADMIN')") + @GetMapping + public ResponseEntity> getApplicantMemberList(){ + + ApplicantMemberListRequestDto requestDto = new ApplicantMemberListRequestDto(); + + List response = applicantMemberService.getApplicantList(requestDto); + return ResponseEntity.ok(response); + } + +} diff --git a/src/main/java/org/one/domain/dto/request/ApplicantMemberListRequestDto.java b/src/main/java/org/one/domain/dto/request/ApplicantMemberListRequestDto.java new file mode 100644 index 0000000..47c95b6 --- /dev/null +++ b/src/main/java/org/one/domain/dto/request/ApplicantMemberListRequestDto.java @@ -0,0 +1,19 @@ +package org.one.domain.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import org.one.global.pagination.RequestPagingDto; + + +@Schema(description = "신청 부원 리스트 조회 요청 dto") +@Getter +@Setter +public class ApplicantMemberListRequestDto extends RequestPagingDto { + //생성자를 통해 기본값설정(한번에 가져올 개수, 오래된 순) + public ApplicantMemberListRequestDto(){ + this.setSize(100); + this.setSort("createdAt"); + this.setDirection("ASC"); + } +} diff --git a/src/main/java/org/one/domain/dto/response/ApplicantMemberListResponseDto.java b/src/main/java/org/one/domain/dto/response/ApplicantMemberListResponseDto.java new file mode 100644 index 0000000..906eb18 --- /dev/null +++ b/src/main/java/org/one/domain/dto/response/ApplicantMemberListResponseDto.java @@ -0,0 +1,30 @@ +package org.one.domain.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import org.one.domain.entity.ApplicantMember; + +import java.time.LocalDateTime; + +/** + * 신청 부원 리스트 조회 시 리스트의 요소가 될 데이터 구조 + */ +@Schema(description = "신청 부원 리스트 요소 데이터 구조") +@Getter +public class ApplicantMemberListResponseDto { + private Long applicantId; + private String name; + private String studentId; + private String phoneNum; + private LocalDateTime createdAt; + private Boolean isFirstView; + + public ApplicantMemberListResponseDto(ApplicantMember applicantMember){ + this.applicantId = applicantMember.getApplicantId(); + this.name = applicantMember.getName(); + this.studentId = applicantMember.getStudentId(); + this.phoneNum = applicantMember.getPhoneNumber(); + this.createdAt = applicantMember.getCreatedAt(); + this.isFirstView = applicantMember.getIsFirstView(); + } +} diff --git a/src/main/java/org/one/domain/repository/ApplicantMemberRepository.java b/src/main/java/org/one/domain/repository/ApplicantMemberRepository.java index 36b0c78..4cdd6a7 100644 --- a/src/main/java/org/one/domain/repository/ApplicantMemberRepository.java +++ b/src/main/java/org/one/domain/repository/ApplicantMemberRepository.java @@ -1,7 +1,10 @@ package org.one.domain.repository; import java.time.LocalDateTime; +import java.util.List; + import org.one.domain.entity.ApplicantMember; +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; @@ -20,4 +23,11 @@ public interface ApplicantMemberRepository extends JpaRepository findAllBy(Pageable pageable); } diff --git a/src/main/java/org/one/domain/service/ApplicantMemberService.java b/src/main/java/org/one/domain/service/ApplicantMemberService.java new file mode 100644 index 0000000..778ebfc --- /dev/null +++ b/src/main/java/org/one/domain/service/ApplicantMemberService.java @@ -0,0 +1,34 @@ +package org.one.domain.service; + +import lombok.RequiredArgsConstructor; +import org.one.domain.dto.request.ApplicantMemberListRequestDto; +import org.one.domain.dto.response.ApplicantMemberListResponseDto; +import org.one.domain.entity.ApplicantMember; +import org.one.domain.repository.ApplicantMemberRepository; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 신청부원 리스트를 모두 가져옴. + * @Param ApplicantMemberListRequestDto : 페이지 설정 정보(정렬, 개수 등) 전달 + */ +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ApplicantMemberService { + private final ApplicantMemberRepository applicantMemberRepository; + + public List getApplicantList(ApplicantMemberListRequestDto requestDto){ + Pageable pageable = requestDto.toPageable(); + + List applicantMemberList = applicantMemberRepository.findAllBy(pageable); + + //엔티티에서 dto형태로 구조를 변환하여 리스트를 만들어 반환 + return applicantMemberList.stream() + .map(ApplicantMemberListResponseDto::new) + .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 32ed7074bd7c59bf2e58a93289456b1bd9363d74 Mon Sep 17 00:00:00 2001 From: marianddi Date: Wed, 27 May 2026 14:59:28 +0900 Subject: [PATCH 11/25] =?UTF-8?q?refactor=20:=20api=20=EA=B2=BD=EB=A1=9C?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/one/domain/controller/ApplicantMemberController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/one/domain/controller/ApplicantMemberController.java b/src/main/java/org/one/domain/controller/ApplicantMemberController.java index 78c12e2..720f33d 100644 --- a/src/main/java/org/one/domain/controller/ApplicantMemberController.java +++ b/src/main/java/org/one/domain/controller/ApplicantMemberController.java @@ -17,7 +17,7 @@ @Tag(name = "ApplicantMember", description = "신청 부원 관리 (관리자 전용)") @RestController @RequiredArgsConstructor -@RequestMapping("/api/v1/applicantmembers") +@RequestMapping("/api/v1/applicantMembers") public class ApplicantMemberController { private final ApplicantMemberService applicantMemberService; @@ -25,7 +25,7 @@ public class ApplicantMemberController { /** * 신청 부원 조회 api * - * api 요청 예시 : GET /api/v1/applicantmembers + * api 요청 예시 : GET /api/v1/applicantMembers * * 응답 데이터 : 신청 부원 리스트 */ From a514c8affb094c285f5bc34ae76ee910d446158e Mon Sep 17 00:00:00 2001 From: marianddi Date: Fri, 29 May 2026 04:07:49 +0900 Subject: [PATCH 12/25] =?UTF-8?q?docs=20:=20=EC=A3=BC=EC=84=9D=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/one/domain/service/ApplicantMemberService.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/one/domain/service/ApplicantMemberService.java b/src/main/java/org/one/domain/service/ApplicantMemberService.java index 778ebfc..1b388e3 100644 --- a/src/main/java/org/one/domain/service/ApplicantMemberService.java +++ b/src/main/java/org/one/domain/service/ApplicantMemberService.java @@ -11,16 +11,17 @@ import java.util.List; -/** - * 신청부원 리스트를 모두 가져옴. - * @Param ApplicantMemberListRequestDto : 페이지 설정 정보(정렬, 개수 등) 전달 - */ + @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class ApplicantMemberService { private final ApplicantMemberRepository applicantMemberRepository; + /** + * 신청부원 리스트를 모두 가져옴. + * @Param ApplicantMemberListRequestDto : 페이지 설정 정보(정렬, 개수 등) 전달 + */ public List getApplicantList(ApplicantMemberListRequestDto requestDto){ Pageable pageable = requestDto.toPageable(); From f482458ef6996d617ad521626e9ea9ed9b5680ff Mon Sep 17 00:00:00 2001 From: marianddi Date: Fri, 29 May 2026 04:42:59 +0900 Subject: [PATCH 13/25] =?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 316cc80c05676a8a9aed5a789e9c2a2a26013cdd Mon Sep 17 00:00:00 2001 From: marianddi Date: Fri, 29 May 2026 04:49:57 +0900 Subject: [PATCH 14/25] =?UTF-8?q?refactor=20:=20MemberController=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=EB=90=9C=20Valid=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/one/domain/controller/MemberController.java | 2 +- 1 file changed, 1 insertion(+), 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 1775ca0..6fa74de 100644 --- a/src/main/java/org/one/domain/controller/MemberController.java +++ b/src/main/java/org/one/domain/controller/MemberController.java @@ -98,7 +98,7 @@ public ResponseEntity getMemberDetail(@PathVariable Lon @PatchMapping("/{memberId}") public ResponseEntity updateMember( @PathVariable Long memberId, - @RequestBody MemberUpdateRequestDto requestDto) + @Valid @RequestBody MemberUpdateRequestDto requestDto) { memberService.updateMember(memberId, requestDto); From c8d46ded711d0cd91ec9b097bfc2088c9b954bdd Mon Sep 17 00:00:00 2001 From: marianddi Date: Sun, 31 May 2026 06:08:00 +0900 Subject: [PATCH 15/25] =?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 da91a0af5e2da3d7f851305b5bdbde92774ae3ec Mon Sep 17 00:00:00 2001 From: marianddi Date: Sun, 31 May 2026 07:00:45 +0900 Subject: [PATCH 16/25] =?UTF-8?q?refactor=20:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20=EB=B0=8F=20=EA=B5=AC=EC=A1=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/one/domain/service/MemberService.java | 60 ------------------- .../java/org/one/global/enums/ErrorCode.java | 4 +- .../member/controller/MemberController.java | 15 +++-- .../dto}/MemberRegisterRequestDto.java | 8 ++- .../member/repository/MemberRepository.java | 8 +++ ...berListService.java => MemberService.java} | 35 ++++++++++- 6 files changed, 57 insertions(+), 73 deletions(-) delete mode 100644 src/main/java/org/one/domain/service/MemberService.java rename src/main/java/org/one/{domain/dto/request => member/dto}/MemberRegisterRequestDto.java (74%) rename src/main/java/org/one/member/service/{MemberListService.java => MemberService.java} (61%) diff --git a/src/main/java/org/one/domain/service/MemberService.java b/src/main/java/org/one/domain/service/MemberService.java deleted file mode 100644 index e2b05c4..0000000 --- a/src/main/java/org/one/domain/service/MemberService.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.one.domain.service; - - -import lombok.RequiredArgsConstructor; -import org.one.domain.dto.request.MemberListRequestDto; -import org.one.domain.dto.request.MemberRegisterRequestDto; -import org.one.domain.dto.response.MemberListResponseDto; -import org.one.domain.entity.Member; -import org.one.domain.enums.Gender; -import org.one.domain.enums.MemberStatus; -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.time.LocalDate; -import java.util.List; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class MemberService { - 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(); - } - - /** - * 새로운 부원을 추가함 - * Param : MemberRegisterRequestDto 부원 추가 dto - */ - @Transactional - public void registerMember(MemberRegisterRequestDto requestDto){ - //요청데이터인 dto를 사용하여 새로운 Member엔티티 설정 - Member member = Member.builder() - .name(requestDto.getName()) - .grade(requestDto.getGrade()) - .studentId(requestDto.getStudentId()) - .age(requestDto.getAge()) - .phoneNumber(requestDto.getPhoneNum()) - .status(MemberStatus.ACTIVE) //status는 기본 값(활동중) - .build(); - - //새로 생성한 부원 객체를 save(insert)해줌. - memberRepository.save(member); - - } - - -} diff --git a/src/main/java/org/one/global/enums/ErrorCode.java b/src/main/java/org/one/global/enums/ErrorCode.java index 3990368..6e445b9 100644 --- a/src/main/java/org/one/global/enums/ErrorCode.java +++ b/src/main/java/org/one/global/enums/ErrorCode.java @@ -17,7 +17,9 @@ public enum ErrorCode { INVALID_DATE_RANGE(HttpStatus.BAD_REQUEST, "INVALID_DATE_RANGE", "날짜 범위가 올바르지 않습니다."), PHOTO_LIMIT_EXCEEDED(HttpStatus.BAD_REQUEST, "PHOTO_LIMIT_EXCEEDED", "사진은 최대 3장까지 업로드할 수 있습니다."), INVALID_OBJECT_KEY(HttpStatus.BAD_REQUEST, "INVALID_OBJECT_KEY", "유효하지 않은 파일 경로입니다."), - INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "INTERNAL_SERVER_ERROR", "서버 오류가 발생했습니다."); + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "INTERNAL_SERVER_ERROR", "서버 오류가 발생했습니다."), + DUPLICATE_STUDENT_ID(HttpStatus.CONFLICT, "DUPLICATE_STUDENT_ID", "이미 존재하는 학번입니다."), + DUPLICATE_PHONE_NUMBER(HttpStatus.CONFLICT, "DUPLICATE_PHONE_NUMBER", "이미 존재하는 전화번호입니다."); private final HttpStatus status; private final String code; diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index 3e7f744..2f43a7a 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -1,4 +1,4 @@ -package org.one.domain.controller; +package org.one.member.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -7,14 +7,12 @@ 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.dto.MemberRegisterRequestDto; +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; @@ -45,7 +43,7 @@ public class MemberController { public ResponseEntity>> getMemberList( @ModelAttribute MemberListRequestDto requestDto){ - List response = memberListService.getMemberListByAdmin(requestDto); + List response = memberService.getMemberListByAdmin(requestDto); return ResponseEntity.ok(ApiResponse.success(response, "명부 리스트 조회 결과입니다.")); } @@ -65,14 +63,15 @@ public ResponseEntity>> getMemberList( * * 응답 데이터 : 성공 메세지 */ + @ApiErrorExceptions({ErrorCode.DUPLICATE_PHONE_NUMBER, ErrorCode.DUPLICATE_STUDENT_ID}) @Operation(summary = "부원 등록", description = "관리자 권한(ADMIN)이 있는 계정만 전체 부원 명부를 조회할 수 있습니다.") @PreAuthorize("hasRole('ADMIN')") @PostMapping - public ResponseEntity registerMember(@RequestBody @Valid MemberRegisterRequestDto requestDto){ + public ResponseEntity> registerMember(@RequestBody @Valid MemberRegisterRequestDto requestDto){ //등록 정보를 service로 넘겨 부원 등록 진행 memberService.registerMember(requestDto); //오류없이 넘어왔을 경우 성공 처리 - return ResponseEntity.ok("부원 등록이 성공적으로 완료되었습니다."); + return ResponseEntity.ok(ApiResponse.success(null, "부원 등록이 성공적으로 완료되었습니다.")); } } diff --git a/src/main/java/org/one/domain/dto/request/MemberRegisterRequestDto.java b/src/main/java/org/one/member/dto/MemberRegisterRequestDto.java similarity index 74% rename from src/main/java/org/one/domain/dto/request/MemberRegisterRequestDto.java rename to src/main/java/org/one/member/dto/MemberRegisterRequestDto.java index 981c318..b24dc53 100644 --- a/src/main/java/org/one/domain/dto/request/MemberRegisterRequestDto.java +++ b/src/main/java/org/one/member/dto/MemberRegisterRequestDto.java @@ -1,8 +1,7 @@ -package org.one.domain.dto.request; +package org.one.member.dto; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.*; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -19,6 +18,8 @@ public class MemberRegisterRequestDto { @Schema(description = "부원 학년", example = "1") @NotNull(message = "학년을 선택해주세요.") + @Min(value = 1, message = "학년은 1학년 이상이어야 합니다.") + @Max(value = 4, message = "학년은 4학년 이하이어야 합니다.") private Integer grade; @Schema(description = "부원 학번", example = "20991111") @@ -31,5 +32,6 @@ public class MemberRegisterRequestDto { @Schema(description = "부원 전화번호", example = "010-1111-2222") @NotBlank(message = "전화번호를 입력해주세요.") + @Pattern(regexp = "^010-\\d{3,4}-\\d{4}$", message = "전화번호 형식이 올바르지 않습니다. (예: 010-1234-5678)") private String phoneNum; } diff --git a/src/main/java/org/one/member/repository/MemberRepository.java b/src/main/java/org/one/member/repository/MemberRepository.java index 9d748ee..51263e4 100644 --- a/src/main/java/org/one/member/repository/MemberRepository.java +++ b/src/main/java/org/one/member/repository/MemberRepository.java @@ -32,4 +32,12 @@ public interface MemberRepository extends JpaRepository { @Query("SELECT m FROM Member m") List findAllByAdmin(Pageable pageable); + /** + * 특정 전화번호를 가진 부원이 이미 존재하는지 확인합니다. + * + * @param phoneNumber 전화번호 + * @return 존재하면 true + */ + boolean existsByPhoneNumber(String phoneNumber); + } diff --git a/src/main/java/org/one/member/service/MemberListService.java b/src/main/java/org/one/member/service/MemberService.java similarity index 61% rename from src/main/java/org/one/member/service/MemberListService.java rename to src/main/java/org/one/member/service/MemberService.java index f22059f..3e83cce 100644 --- a/src/main/java/org/one/member/service/MemberListService.java +++ b/src/main/java/org/one/member/service/MemberService.java @@ -8,6 +8,8 @@ import org.one.member.domain.Member; import org.one.member.dto.MemberListRequestDto; import org.one.member.dto.MemberListResponseDto; +import org.one.member.dto.MemberRegisterRequestDto; +import org.one.member.enums.MemberStatus; import org.one.member.repository.MemberRepository; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -18,7 +20,7 @@ @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class MemberListService { +public class MemberService { private final MemberRepository memberRepository; private final AdminRepository adminRepository; @@ -51,4 +53,35 @@ public List getMemberListByAdmin(MemberListRequestDto req .map(MemberListResponseDto::from) .toList(); } + + /** + * 새로운 부원을 추가함 + * Param : MemberRegisterRequestDto 부원 추가 dto + */ + @Transactional + public void registerMember(MemberRegisterRequestDto requestDto){ + //학번 중복 시 예외처리 + if(memberRepository.existsByStudentId(requestDto.getStudentId())) { + throw new BusinessException(ErrorCode.DUPLICATE_STUDENT_ID); + } + + //전화번호 중복 시 예외처리 + if(memberRepository.existsByPhoneNumber(requestDto.getPhoneNum())) { + throw new BusinessException(ErrorCode.DUPLICATE_PHONE_NUMBER); + } + + //요청데이터인 dto를 사용하여 새로운 Member엔티티 설정 + Member member = Member.builder() + .name(requestDto.getName()) + .grade(requestDto.getGrade()) + .studentId(requestDto.getStudentId()) + .age(requestDto.getAge()) + .phoneNumber(requestDto.getPhoneNum()) + .status(MemberStatus.ACTIVE) //status는 기본 값(활동중) + .build(); + + //새로 생성한 부원 객체를 save(insert)해줌. + memberRepository.save(member); + + } } From 2884093d99f07447e857533fed9429fbc6fe36cb Mon Sep 17 00:00:00 2001 From: marianddi Date: Sun, 31 May 2026 15:11:48 +0900 Subject: [PATCH 17/25] =?UTF-8?q?refactor=20:=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=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 --- .../java/org/one/member/controller/MemberController.java | 1 + .../response => member/dto}/MemberDetailResponseDto.java | 9 +++------ .../request => member/dto}/MemberUpdateRequestDto.java | 8 +++++--- 3 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/org/one/{domain/dto/response => member/dto}/MemberDetailResponseDto.java (80%) rename src/main/java/org/one/{domain/dto/request => member/dto}/MemberUpdateRequestDto.java (74%) diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index 0ab4d74..8102a60 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -7,6 +7,7 @@ import org.one.global.annotation.ApiErrorExceptions; import org.one.global.dto.ApiResponse; import org.one.global.enums.ErrorCode; +import org.one.member.dto.MemberDetailResponseDto; import org.one.member.dto.MemberListRequestDto; import org.one.member.dto.MemberListResponseDto; import org.one.member.dto.MemberRegisterRequestDto; diff --git a/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java b/src/main/java/org/one/member/dto/MemberDetailResponseDto.java similarity index 80% rename from src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java rename to src/main/java/org/one/member/dto/MemberDetailResponseDto.java index ee7d788..9904956 100644 --- a/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java +++ b/src/main/java/org/one/member/dto/MemberDetailResponseDto.java @@ -1,12 +1,9 @@ -package org.one.domain.dto.response; +package org.one.member.dto; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Getter; -import org.one.domain.entity.Member; +import org.one.member.domain.Member; + /** * 멤버 상세 조회 성공 시 응답하는 dto diff --git a/src/main/java/org/one/domain/dto/request/MemberUpdateRequestDto.java b/src/main/java/org/one/member/dto/MemberUpdateRequestDto.java similarity index 74% rename from src/main/java/org/one/domain/dto/request/MemberUpdateRequestDto.java rename to src/main/java/org/one/member/dto/MemberUpdateRequestDto.java index 1198296..86329af 100644 --- a/src/main/java/org/one/domain/dto/request/MemberUpdateRequestDto.java +++ b/src/main/java/org/one/member/dto/MemberUpdateRequestDto.java @@ -1,8 +1,7 @@ -package org.one.domain.dto.request; +package org.one.member.dto; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.*; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -18,6 +17,8 @@ public class MemberUpdateRequestDto { @Schema(description = "부원 학년", example = "1") @NotNull(message = "학년을 선택해주세요.") + @Min(value = 1, message = "학년은 1학년 이상이어야 합니다.") + @Max(value = 4, message = "학년은 4학년 이하이어야 합니다.") private Integer grade; @Schema(description = "부원 학번", example = "20991111") @@ -30,5 +31,6 @@ public class MemberUpdateRequestDto { @Schema(description = "부원 전화번호", example = "010-1111-2222") @NotBlank(message = "전화번호를 입력해주세요.") + @Pattern(regexp = "^010-\\d{3,4}-\\d{4}$", message = "전화번호 형식이 올바르지 않습니다. (예: 010-1234-5678)") private String phoneNum; } From 98877a8193a85e804a6c5ae7ddadb2aebc4191f0 Mon Sep 17 00:00:00 2001 From: marianddi Date: Sun, 31 May 2026 15:49:30 +0900 Subject: [PATCH 18/25] =?UTF-8?q?refactor=20:=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/one/global/enums/ErrorCode.java | 4 +++- .../one/member/controller/MemberController.java | 7 +++++-- .../dto}/MemberDetailResponseDto.java | 16 ++++++++-------- .../org/one/member/service/MemberService.java | 10 +++------- 4 files changed, 19 insertions(+), 18 deletions(-) rename src/main/java/org/one/{domain/dto/response => member/dto}/MemberDetailResponseDto.java (69%) diff --git a/src/main/java/org/one/global/enums/ErrorCode.java b/src/main/java/org/one/global/enums/ErrorCode.java index 6e445b9..b0120ff 100644 --- a/src/main/java/org/one/global/enums/ErrorCode.java +++ b/src/main/java/org/one/global/enums/ErrorCode.java @@ -19,7 +19,9 @@ public enum ErrorCode { INVALID_OBJECT_KEY(HttpStatus.BAD_REQUEST, "INVALID_OBJECT_KEY", "유효하지 않은 파일 경로입니다."), INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "INTERNAL_SERVER_ERROR", "서버 오류가 발생했습니다."), DUPLICATE_STUDENT_ID(HttpStatus.CONFLICT, "DUPLICATE_STUDENT_ID", "이미 존재하는 학번입니다."), - DUPLICATE_PHONE_NUMBER(HttpStatus.CONFLICT, "DUPLICATE_PHONE_NUMBER", "이미 존재하는 전화번호입니다."); + DUPLICATE_PHONE_NUMBER(HttpStatus.CONFLICT, "DUPLICATE_PHONE_NUMBER", "이미 존재하는 전화번호입니다."), + MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER_NOT_FOUND", "존재하지 않는 부원입니다."); + private final HttpStatus status; private final String code; diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index 76715b2..3215dd8 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -3,10 +3,12 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import jakarta.validation.constraints.Positive; import lombok.RequiredArgsConstructor; import org.one.global.annotation.ApiErrorExceptions; import org.one.global.dto.ApiResponse; import org.one.global.enums.ErrorCode; +import org.one.member.dto.MemberDetailResponseDto; import org.one.member.dto.MemberListRequestDto; import org.one.member.dto.MemberListResponseDto; import org.one.member.dto.MemberRegisterRequestDto; @@ -83,11 +85,12 @@ public ResponseEntity> registerMember(@RequestBody @Valid Memb * * 응답 데이터 : 특정 부원에 대한 정보 */ + @ApiErrorExceptions({ErrorCode.MEMBER_NOT_FOUND, ErrorCode.INVALID_INPUT}) @Operation(summary = "부원 정보 가져오기", description = "관리자 권한(ADMIN)이 있는 계정만 부원 상세 정보를 조회할 수 있습니다.") @PreAuthorize("hasRole('ADMIN')") @GetMapping("/{memberId}") - public ResponseEntity getMemberDetail(@PathVariable Long memberId){ + public ResponseEntity> getMemberDetail(@PathVariable @Positive(message = "올바르지 않은 부원 id입니다.") Long memberId){ MemberDetailResponseDto responseDto= memberService.getMemberDetail(memberId); - return ResponseEntity.ok(responseDto); + return ResponseEntity.ok(ApiResponse.success(responseDto, "부원 상세 정보 조회 결과입니다.")); } } diff --git a/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java b/src/main/java/org/one/member/dto/MemberDetailResponseDto.java similarity index 69% rename from src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java rename to src/main/java/org/one/member/dto/MemberDetailResponseDto.java index bb7b34a..a1131cc 100644 --- a/src/main/java/org/one/domain/dto/response/MemberDetailResponseDto.java +++ b/src/main/java/org/one/member/dto/MemberDetailResponseDto.java @@ -1,12 +1,9 @@ -package org.one.domain.dto.response; +package org.one.member.dto; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Getter; -import org.one.domain.entity.Member; +import org.one.member.domain.Member; + /** * 특정 부원 정보 조회 성공 시 응답하는 dto @@ -29,12 +26,15 @@ public class MemberDetailResponseDto { @Schema(description = "부원 전화번호") private String phoneNum; - //생성자를 통해 Member엔티티에서 필요한 정보만 dto 필드로 저장 - public MemberDetailResponseDto(Member member){ + private MemberDetailResponseDto(Member member){ this.name = member.getName(); this.grade = member.getGrade(); this.studentId = member.getStudentId(); this.age = member.getAge(); this.phoneNum = member.getPhoneNumber(); } + + public static MemberDetailResponseDto from(Member member){ + return new MemberDetailResponseDto(member); + } } diff --git a/src/main/java/org/one/member/service/MemberService.java b/src/main/java/org/one/member/service/MemberService.java index 1151f8f..ab27bae 100644 --- a/src/main/java/org/one/member/service/MemberService.java +++ b/src/main/java/org/one/member/service/MemberService.java @@ -6,6 +6,7 @@ import org.one.global.enums.ErrorCode; import org.one.global.exception.BusinessException; import org.one.member.domain.Member; +import org.one.member.dto.MemberDetailResponseDto; import org.one.member.dto.MemberListRequestDto; import org.one.member.dto.MemberListResponseDto; import org.one.member.dto.MemberRegisterRequestDto; @@ -91,12 +92,7 @@ public void registerMember(MemberRegisterRequestDto requestDto){ */ public MemberDetailResponseDto getMemberDetail(Long memberId){ Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new IllegalArgumentException("부원을 찾을 수 없습니다.")); - return new MemberDetailResponseDto(member); + .orElseThrow(()->new BusinessException(ErrorCode.MEMBER_NOT_FOUND)); + return MemberDetailResponseDto.from(member); } - - - - - } From d4dbb3910da4dbd5c170dbfbd41bf4858f1943db Mon Sep 17 00:00:00 2001 From: marianddi Date: Sun, 31 May 2026 16:13:03 +0900 Subject: [PATCH 19/25] =?UTF-8?q?docs=20:=20ApiErrorEceptions=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=EC=97=90=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/one/member/controller/MemberController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index 2f43a7a..b42f39e 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -63,7 +63,7 @@ public ResponseEntity>> getMemberList( * * 응답 데이터 : 성공 메세지 */ - @ApiErrorExceptions({ErrorCode.DUPLICATE_PHONE_NUMBER, ErrorCode.DUPLICATE_STUDENT_ID}) + @ApiErrorExceptions({ErrorCode.DUPLICATE_PHONE_NUMBER, ErrorCode.DUPLICATE_STUDENT_ID, ErrorCode.INVALID_INPUT}) @Operation(summary = "부원 등록", description = "관리자 권한(ADMIN)이 있는 계정만 전체 부원 명부를 조회할 수 있습니다.") @PreAuthorize("hasRole('ADMIN')") @PostMapping From aa918deea9f27fbabb50719f455b43208faf93e8 Mon Sep 17 00:00:00 2001 From: marianddi Date: Mon, 1 Jun 2026 04:11:02 +0900 Subject: [PATCH 20/25] =?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(); From da36d70398a824981fc6403c46c6369533bab196 Mon Sep 17 00:00:00 2001 From: marianddi Date: Mon, 1 Jun 2026 04:50:28 +0900 Subject: [PATCH 21/25] =?UTF-8?q?refactor=20:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=ED=95=98=EC=97=AC=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/member/controller/MemberController.java | 4 ++-- .../java/org/one/member/dto/MemberRegisterRequestDto.java | 6 +++++- src/main/java/org/one/member/service/MemberService.java | 1 - 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index 5ea05c0..ddf187e 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -61,7 +61,7 @@ public ResponseEntity>> getMemberList( * "age" : 22, * "phoneNum" : "010-1111-2222" * - * 응답 데이터 : 성공 메세지 + * 응답 데이터 : x */ @ApiErrorExceptions({ErrorCode.DUPLICATE_PHONE_NUMBER, ErrorCode.DUPLICATE_STUDENT_ID, ErrorCode.INVALID_INPUT}) @Operation(summary = "부원 등록", description = "관리자 권한(ADMIN)이 있는 계정만 전체 부원 명부를 조회할 수 있습니다.") @@ -72,6 +72,6 @@ public ResponseEntity> registerMember(@RequestBody @Valid Memb memberService.registerMember(requestDto); //오류없이 넘어왔을 경우 성공 처리 - return ResponseEntity.ok(ApiResponse.success(null, "부원 등록이 성공적으로 완료되었습니다.")); + return ResponseEntity.ok(ApiResponse.success(null)); } } diff --git a/src/main/java/org/one/member/dto/MemberRegisterRequestDto.java b/src/main/java/org/one/member/dto/MemberRegisterRequestDto.java index b24dc53..9ec2d0c 100644 --- a/src/main/java/org/one/member/dto/MemberRegisterRequestDto.java +++ b/src/main/java/org/one/member/dto/MemberRegisterRequestDto.java @@ -14,6 +14,7 @@ public class MemberRegisterRequestDto { @Schema(description = "부원 이름", example = "홍길동") @NotBlank(message = "이름을 입력해주세요.") + @Size(min = 2, max = 20, message = "이름은 2~20자 사이로 입력해주세요.") private String name; @Schema(description = "부원 학년", example = "1") @@ -24,14 +25,17 @@ public class MemberRegisterRequestDto { @Schema(description = "부원 학번", example = "20991111") @NotBlank(message = "학번을 입력해주세요.") + @Pattern(regexp = "^\\d{8}$", message = "학번은 8자리의 숫자만 입력 가능합니다.") private String studentId; @Schema(description = "부원 나이", example = "22") @NotNull(message = "나이를 입력해주세요.") + @Min(value = 18, message = "나이는 18세 이상이어야 합니다.") + @Max(value = 100, message = "나이가 올바르지 않습니다.") private Integer age; @Schema(description = "부원 전화번호", example = "010-1111-2222") @NotBlank(message = "전화번호를 입력해주세요.") @Pattern(regexp = "^010-\\d{3,4}-\\d{4}$", message = "전화번호 형식이 올바르지 않습니다. (예: 010-1234-5678)") private String phoneNum; -} +} \ No newline at end of file diff --git a/src/main/java/org/one/member/service/MemberService.java b/src/main/java/org/one/member/service/MemberService.java index ff000df..8d5701a 100644 --- a/src/main/java/org/one/member/service/MemberService.java +++ b/src/main/java/org/one/member/service/MemberService.java @@ -65,6 +65,5 @@ public void registerMember(MemberRegisterRequestDto requestDto){ //새로 생성한 부원 객체를 save(insert)해줌. memberRepository.save(member); - } } From 6e552bb99278b1fa0ea4dc7232f8e3c443029d92 Mon Sep 17 00:00:00 2001 From: marianddi Date: Mon, 1 Jun 2026 05:32:55 +0900 Subject: [PATCH 22/25] =?UTF-8?q?refactor=20:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20=ED=95=98=EC=97=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/one/member/controller/MemberController.java | 4 ++-- .../org/one/member/dto/MemberDetailResponseDto.java | 10 +++++----- .../java/org/one/member/service/MemberService.java | 4 ++++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index 719556c..8c4c16a 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -89,8 +89,8 @@ public ResponseEntity> registerMember(@RequestBody @Valid Memb @Operation(summary = "부원 정보 가져오기", description = "관리자 권한(ADMIN)이 있는 계정만 부원 상세 정보를 조회할 수 있습니다.") @PreAuthorize("hasRole('ADMIN')") @GetMapping("/{memberId}") - public ResponseEntity> getMemberDetail(@PathVariable @Positive(message = "올바르지 않은 부원 id입니다.") Long memberId){ + public ResponseEntity> getMemberDetail(@PathVariable Long memberId){ MemberDetailResponseDto responseDto= memberService.getMemberDetail(memberId); - return ResponseEntity.ok(ApiResponse.success(responseDto, "부원 상세 정보 조회 결과입니다.")); + return ResponseEntity.ok(ApiResponse.success(responseDto)); } } diff --git a/src/main/java/org/one/member/dto/MemberDetailResponseDto.java b/src/main/java/org/one/member/dto/MemberDetailResponseDto.java index a1131cc..ede5767 100644 --- a/src/main/java/org/one/member/dto/MemberDetailResponseDto.java +++ b/src/main/java/org/one/member/dto/MemberDetailResponseDto.java @@ -11,19 +11,19 @@ @Schema(description = "부원 상세 정보 응답") @Getter public class MemberDetailResponseDto { - @Schema(description = "부원 이름") + @Schema(description = "부원 이름", example = "홍길동") private String name; - @Schema(description = "부원 학년") + @Schema(description = "부원 학년", example = "2") private Integer grade; - @Schema(description = "부원 학번") + @Schema(description = "부원 학번", example = "20991234") private String studentId; - @Schema(description = "부원 나이") + @Schema(description = "부원 나이", example = "22") private Integer age; - @Schema(description = "부원 전화번호") + @Schema(description = "부원 전화번호", example = "010-1111-2222") private String phoneNum; private MemberDetailResponseDto(Member member){ diff --git a/src/main/java/org/one/member/service/MemberService.java b/src/main/java/org/one/member/service/MemberService.java index ff703e9..6a02e61 100644 --- a/src/main/java/org/one/member/service/MemberService.java +++ b/src/main/java/org/one/member/service/MemberService.java @@ -74,6 +74,10 @@ public void registerMember(MemberRegisterRequestDto requestDto){ * return : MemberDetailResponseDto */ public MemberDetailResponseDto getMemberDetail(Long memberId){ + if(memberId == null || memberId <= 0){ + throw new BusinessException(ErrorCode.INVALID_INPUT); + } + Member member = memberRepository.findById(memberId) .orElseThrow(()->new BusinessException(ErrorCode.MEMBER_NOT_FOUND)); return MemberDetailResponseDto.from(member); From f81bacf6095408b18a6fc2c2b5f8791dc7c29902 Mon Sep 17 00:00:00 2001 From: marianddi Date: Mon, 1 Jun 2026 06:04:04 +0900 Subject: [PATCH 23/25] =?UTF-8?q?refactor=20:=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 11 ++++----- .../member/dto/MemberUpdateRequestDto.java | 4 ++++ .../org/one/member/service/MemberService.java | 23 +++++++++++++++---- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index 811fadf..56a80e4 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -3,15 +3,11 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import jakarta.validation.constraints.Positive; import lombok.RequiredArgsConstructor; import org.one.global.annotation.ApiErrorExceptions; import org.one.global.dto.ApiResponse; import org.one.global.enums.ErrorCode; -import org.one.member.dto.MemberDetailResponseDto; -import org.one.member.dto.MemberListRequestDto; -import org.one.member.dto.MemberListResponseDto; -import org.one.member.dto.MemberRegisterRequestDto; +import org.one.member.dto.*; import org.one.member.service.MemberService; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -103,16 +99,17 @@ public ResponseEntity> getMemberDetail(@Pat * * 응답 데이터 : 수정 완료 메시지 */ + @ApiErrorExceptions({ErrorCode.MEMBER_NOT_FOUND, ErrorCode.INVALID_INPUT, ErrorCode.DUPLICATE_PHONE_NUMBER, ErrorCode.DUPLICATE_STUDENT_ID}) @Operation(summary = "부원 수정", description = "관리자 권한(ADMIN)이 있는 계정만 특정 부원의 정보를 일부 수정할 수 있습니다.") @PreAuthorize("hasRole('ADMIN')") @PatchMapping("/{memberId}") - public ResponseEntity updateMember( + public ResponseEntity> updateMember( @PathVariable Long memberId, @Valid @RequestBody MemberUpdateRequestDto requestDto) { memberService.updateMember(memberId, requestDto); - return ResponseEntity.ok("부원 정보가 성공적으로 수정되었습니다."); + return ResponseEntity.ok(null); } } diff --git a/src/main/java/org/one/member/dto/MemberUpdateRequestDto.java b/src/main/java/org/one/member/dto/MemberUpdateRequestDto.java index 86329af..7465470 100644 --- a/src/main/java/org/one/member/dto/MemberUpdateRequestDto.java +++ b/src/main/java/org/one/member/dto/MemberUpdateRequestDto.java @@ -13,6 +13,7 @@ public class MemberUpdateRequestDto { @Schema(description = "부원 이름", example = "홍길동") @NotBlank(message = "이름을 입력해주세요.") + @Size(min = 2, max = 20, message = "이름은 2~20자 사이로 입력해주세요.") private String name; @Schema(description = "부원 학년", example = "1") @@ -23,10 +24,13 @@ public class MemberUpdateRequestDto { @Schema(description = "부원 학번", example = "20991111") @NotBlank(message = "학번을 입력해주세요.") + @Pattern(regexp = "^\\d{8}$", message = "학번은 8자리의 숫자만 입력 가능합니다.") private String studentId; @Schema(description = "부원 나이", example = "22") @NotNull(message = "나이를 입력해주세요.") + @Min(value = 18, message = "나이는 18세 이상이어야 합니다.") + @Max(value = 100, message = "나이가 올바르지 않습니다.") private Integer age; @Schema(description = "부원 전화번호", example = "010-1111-2222") diff --git a/src/main/java/org/one/member/service/MemberService.java b/src/main/java/org/one/member/service/MemberService.java index 818294e..2d781f3 100644 --- a/src/main/java/org/one/member/service/MemberService.java +++ b/src/main/java/org/one/member/service/MemberService.java @@ -6,10 +6,7 @@ import org.one.global.enums.ErrorCode; import org.one.global.exception.BusinessException; import org.one.member.domain.Member; -import org.one.member.dto.MemberDetailResponseDto; -import org.one.member.dto.MemberListRequestDto; -import org.one.member.dto.MemberListResponseDto; -import org.one.member.dto.MemberRegisterRequestDto; +import org.one.member.dto.*; import org.one.member.enums.MemberStatus; import org.one.member.repository.MemberRepository; import org.springframework.data.domain.Pageable; @@ -86,8 +83,24 @@ public MemberDetailResponseDto getMemberDetail(Long memberId){ @Transactional public void updateMember(Long memberId, MemberUpdateRequestDto requestDto){ + if(memberId == null || memberId <= 0){ + throw new BusinessException(ErrorCode.INVALID_INPUT); + } + Member member = memberRepository.findById(memberId) - .orElseThrow(()->new IllegalArgumentException("존재하지 않는 부원입니다.")); + .orElseThrow(()->new BusinessException(ErrorCode.MEMBER_NOT_FOUND)); + + //중복 값 예외처리(학번, 전화번호) + if(!member.getStudentId().equals(requestDto.getStudentId())) { + if(memberRepository.existsByStudentId(requestDto.getStudentId())) { + throw new BusinessException(ErrorCode.DUPLICATE_STUDENT_ID); + } + } + if(!member.getPhoneNumber().equals(requestDto.getPhoneNum())) { + if(memberRepository.existsByPhoneNumber(requestDto.getPhoneNum())) { + throw new BusinessException(ErrorCode.DUPLICATE_PHONE_NUMBER); + } + } member.updateInfo(requestDto.getName(), requestDto.getStudentId(), requestDto.getPhoneNum(), requestDto.getGrade(), requestDto.getAge()); } From 0721d05c849abe6e8984787b2163d761451096ac Mon Sep 17 00:00:00 2001 From: marianddi Date: Mon, 1 Jun 2026 06:43:37 +0900 Subject: [PATCH 24/25] =?UTF-8?q?refactor=20:=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/MemberDeleteListRequestDto.java | 18 ------------------ .../member/controller/MemberController.java | 8 ++++---- .../member/dto/MemberDeleteListRequestDto.java | 18 ++++++++++++++++++ .../org/one/member/service/MemberService.java | 18 ++++++++++++++++-- 4 files changed, 38 insertions(+), 24 deletions(-) delete mode 100644 src/main/java/org/one/domain/dto/request/MemberDeleteListRequestDto.java create mode 100644 src/main/java/org/one/member/dto/MemberDeleteListRequestDto.java diff --git a/src/main/java/org/one/domain/dto/request/MemberDeleteListRequestDto.java b/src/main/java/org/one/domain/dto/request/MemberDeleteListRequestDto.java deleted file mode 100644 index fb4dcf4..0000000 --- a/src/main/java/org/one/domain/dto/request/MemberDeleteListRequestDto.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.one.domain.dto.request; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "부원 삭제 요청") -@Getter -@Setter -public class MemberDeleteListRequestDto { - @Schema(description = "삭제할 부원(들)의 id리스트") - @NotEmpty(message = "id리스트를 입력해주세요.") - private List memberIds; -} diff --git a/src/main/java/org/one/member/controller/MemberController.java b/src/main/java/org/one/member/controller/MemberController.java index bb9704f..54e9825 100644 --- a/src/main/java/org/one/member/controller/MemberController.java +++ b/src/main/java/org/one/member/controller/MemberController.java @@ -122,15 +122,15 @@ public ResponseEntity> updateMember( * requestBody 예시 * "memberIds" : [2, 7] * - * 응답 데이터 : 삭제 완료 메시지 + * 응답 데이터 : x */ + @ApiErrorExceptions({ErrorCode.MEMBER_NOT_FOUND, ErrorCode.INVALID_INPUT}) @Operation(summary = "부원 삭제", description = "관리자 권한(ADMIN)이 있는 계정만 부원을 삭제할 수 있습니다.") @PreAuthorize("hasRole('ADMIN')") @DeleteMapping - public ResponseEntity deleteMembers(@RequestBody @Valid MemberDeleteListRequestDto requestDto){ + public ResponseEntity> deleteMembers(@RequestBody @Valid MemberDeleteListRequestDto requestDto){ memberService.deleteMembers(requestDto.getMemberIds()); - - return ResponseEntity.ok("선택하신 부원들이 성공적으로 삭제되었습니다."); + return ResponseEntity.ok(ApiResponse.success(null)); } } diff --git a/src/main/java/org/one/member/dto/MemberDeleteListRequestDto.java b/src/main/java/org/one/member/dto/MemberDeleteListRequestDto.java new file mode 100644 index 0000000..2a03d20 --- /dev/null +++ b/src/main/java/org/one/member/dto/MemberDeleteListRequestDto.java @@ -0,0 +1,18 @@ +package org.one.member.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "부원 삭제 요청") +@Getter +public class MemberDeleteListRequestDto { + @Schema(description = "삭제할 부원(들)의 id리스트", example = "[1, 2, 7]") + @NotEmpty(message = "삭제할 부원(들)의 id리스트를 입력해주세요.") + private List<@NotNull(message = "id는 비어있을 수 없습니다.") @Positive(message="올바르지 않은 부원의 id가 포함되어있습니다.") Long> memberIds; +} diff --git a/src/main/java/org/one/member/service/MemberService.java b/src/main/java/org/one/member/service/MemberService.java index affe82d..14d7f21 100644 --- a/src/main/java/org/one/member/service/MemberService.java +++ b/src/main/java/org/one/member/service/MemberService.java @@ -14,7 +14,9 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; +import java.util.HashSet; import java.util.List; +import java.util.Set; @Service @RequiredArgsConstructor @@ -110,8 +112,20 @@ public void updateMember(Long memberId, MemberUpdateRequestDto requestDto){ * Param : memberIds */ @Transactional - public void deleteMembers(List memberIds){ - memberRepository.deleteAllById(memberIds); + public void deleteMembers(List memberIds) + { + //리스트 내 중복값 있을 경우 예외처리 + Set uniqueIds = new HashSet<>(memberIds); + if (memberIds.size() != uniqueIds.size()) { + throw new BusinessException(ErrorCode.INVALID_INPUT); + } + //리스트 내 id 중 db에 없는 값이 하나라도 있을 경우 예외처리 + List existingMembers = memberRepository.findAllById(uniqueIds); + if (existingMembers.size() != uniqueIds.size()) { + throw new BusinessException(ErrorCode.MEMBER_NOT_FOUND); + } + + memberRepository.deleteAllByIdInBatch(memberIds); } From 923302785b1ac714fc8f3143f1a9a183a1fa95f4 Mon Sep 17 00:00:00 2001 From: marianddi Date: Mon, 1 Jun 2026 07:47:16 +0900 Subject: [PATCH 25/25] =?UTF-8?q?refactor=20:=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApplicantMemberListRequestDto.java | 19 ------- .../controller/ApplicantMemberController.java | 21 ++++---- .../dto/ApplicantMemberListRequestDto.java | 51 +++++++++++++++++++ .../dto}/ApplicantMemberListResponseDto.java | 16 ++++-- .../service/ApplicantMemberService.java | 12 ++--- 5 files changed, 82 insertions(+), 37 deletions(-) delete mode 100644 src/main/java/org/one/domain/dto/request/ApplicantMemberListRequestDto.java rename src/main/java/org/one/{domain => member}/controller/ApplicantMemberController.java (63%) create mode 100644 src/main/java/org/one/member/dto/ApplicantMemberListRequestDto.java rename src/main/java/org/one/{domain/dto/response => member/dto}/ApplicantMemberListResponseDto.java (53%) rename src/main/java/org/one/{domain => member}/service/ApplicantMemberService.java (75%) diff --git a/src/main/java/org/one/domain/dto/request/ApplicantMemberListRequestDto.java b/src/main/java/org/one/domain/dto/request/ApplicantMemberListRequestDto.java deleted file mode 100644 index 47c95b6..0000000 --- a/src/main/java/org/one/domain/dto/request/ApplicantMemberListRequestDto.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.one.domain.dto.request; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; -import org.one.global.pagination.RequestPagingDto; - - -@Schema(description = "신청 부원 리스트 조회 요청 dto") -@Getter -@Setter -public class ApplicantMemberListRequestDto extends RequestPagingDto { - //생성자를 통해 기본값설정(한번에 가져올 개수, 오래된 순) - public ApplicantMemberListRequestDto(){ - this.setSize(100); - this.setSort("createdAt"); - this.setDirection("ASC"); - } -} diff --git a/src/main/java/org/one/domain/controller/ApplicantMemberController.java b/src/main/java/org/one/member/controller/ApplicantMemberController.java similarity index 63% rename from src/main/java/org/one/domain/controller/ApplicantMemberController.java rename to src/main/java/org/one/member/controller/ApplicantMemberController.java index 720f33d..5b4cc54 100644 --- a/src/main/java/org/one/domain/controller/ApplicantMemberController.java +++ b/src/main/java/org/one/member/controller/ApplicantMemberController.java @@ -1,14 +1,19 @@ -package org.one.domain.controller; +package org.one.member.controller; 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.domain.dto.request.ApplicantMemberListRequestDto; -import org.one.domain.dto.response.ApplicantMemberListResponseDto; -import org.one.domain.service.ApplicantMemberService; +import org.one.global.annotation.ApiErrorExceptions; +import org.one.global.dto.ApiResponse; +import org.one.global.enums.ErrorCode; +import org.one.member.dto.ApplicantMemberListRequestDto; +import org.one.member.dto.ApplicantMemberListResponseDto; +import org.one.member.service.ApplicantMemberService; 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; @@ -29,15 +34,13 @@ public class ApplicantMemberController { * * 응답 데이터 : 신청 부원 리스트 */ + @ApiErrorExceptions({ErrorCode.INVALID_INPUT}) @Operation(summary = "신청 부원 조회", description = "관리자 권한(ADMIN)이 있는 계정만 신청 부원을 조회할 수 있습니다.") @PreAuthorize("hasRole('ADMIN')") @GetMapping - public ResponseEntity> getApplicantMemberList(){ - - ApplicantMemberListRequestDto requestDto = new ApplicantMemberListRequestDto(); - + public ResponseEntity>> getApplicantMemberList(@ModelAttribute @Valid ApplicantMemberListRequestDto requestDto){ List response = applicantMemberService.getApplicantList(requestDto); - return ResponseEntity.ok(response); + return ResponseEntity.ok(ApiResponse.success(response)); } } diff --git a/src/main/java/org/one/member/dto/ApplicantMemberListRequestDto.java b/src/main/java/org/one/member/dto/ApplicantMemberListRequestDto.java new file mode 100644 index 0000000..e6a5884 --- /dev/null +++ b/src/main/java/org/one/member/dto/ApplicantMemberListRequestDto.java @@ -0,0 +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 lombok.Setter; +import org.one.global.pagination.RequestPagingDto; + + +@Schema(description = "신청 부원 리스트 조회 요청 dto") +@Getter +public class ApplicantMemberListRequestDto extends RequestPagingDto { + //생성자를 통해 기본값설정(페이지 번호, 한번에 가져올 개수, 오래된 순) + public ApplicantMemberListRequestDto(){ + 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 = "정렬 기준 필드 [createdAt 고정]", example = "createdAt") + @Pattern(regexp = "^(createdAt)$", message = "정렬 기준은 createdAt만 가능합니다.") + public String getSort() { + return super.getSort(); + } + + @Override + @Schema(description = "정렬 방향 [ASC 고정]", example = "ASC") + @Pattern(regexp = "^(ASC)$", message = "정렬 방향은 ASC만 가능합니다.") + public String getDirection() { + return super.getDirection(); + } +} diff --git a/src/main/java/org/one/domain/dto/response/ApplicantMemberListResponseDto.java b/src/main/java/org/one/member/dto/ApplicantMemberListResponseDto.java similarity index 53% rename from src/main/java/org/one/domain/dto/response/ApplicantMemberListResponseDto.java rename to src/main/java/org/one/member/dto/ApplicantMemberListResponseDto.java index 906eb18..1cb90c2 100644 --- a/src/main/java/org/one/domain/dto/response/ApplicantMemberListResponseDto.java +++ b/src/main/java/org/one/member/dto/ApplicantMemberListResponseDto.java @@ -1,8 +1,8 @@ -package org.one.domain.dto.response; +package org.one.member.dto; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; -import org.one.domain.entity.ApplicantMember; +import org.one.member.domain.ApplicantMember; import java.time.LocalDateTime; @@ -12,14 +12,20 @@ @Schema(description = "신청 부원 리스트 요소 데이터 구조") @Getter public class ApplicantMemberListResponseDto { + @Schema(description = "신청 부원 id", example = "3") private Long applicantId; + @Schema(description = "신청 부원 이름", example = "홍길동") private String name; + @Schema(description = "신청 부원 학번", example = "20991234") private String studentId; + @Schema(description = "신청 부원 전화번호", example = "3") private String phoneNum; + @Schema(description = "신청 부원 신청 날짜", example = "2026-08-08") private LocalDateTime createdAt; + @Schema(description = "신청 부원 정보 조회 여부", example = "true") private Boolean isFirstView; - public ApplicantMemberListResponseDto(ApplicantMember applicantMember){ + private ApplicantMemberListResponseDto(ApplicantMember applicantMember){ this.applicantId = applicantMember.getApplicantId(); this.name = applicantMember.getName(); this.studentId = applicantMember.getStudentId(); @@ -27,4 +33,8 @@ public ApplicantMemberListResponseDto(ApplicantMember applicantMember){ this.createdAt = applicantMember.getCreatedAt(); this.isFirstView = applicantMember.getIsFirstView(); } + + public static ApplicantMemberListResponseDto from(ApplicantMember applicantMember){ + return new ApplicantMemberListResponseDto(applicantMember); + } } diff --git a/src/main/java/org/one/domain/service/ApplicantMemberService.java b/src/main/java/org/one/member/service/ApplicantMemberService.java similarity index 75% rename from src/main/java/org/one/domain/service/ApplicantMemberService.java rename to src/main/java/org/one/member/service/ApplicantMemberService.java index 1b388e3..24f290c 100644 --- a/src/main/java/org/one/domain/service/ApplicantMemberService.java +++ b/src/main/java/org/one/member/service/ApplicantMemberService.java @@ -1,10 +1,10 @@ -package org.one.domain.service; +package org.one.member.service; import lombok.RequiredArgsConstructor; -import org.one.domain.dto.request.ApplicantMemberListRequestDto; -import org.one.domain.dto.response.ApplicantMemberListResponseDto; -import org.one.domain.entity.ApplicantMember; -import org.one.domain.repository.ApplicantMemberRepository; +import org.one.member.domain.ApplicantMember; +import org.one.member.dto.ApplicantMemberListRequestDto; +import org.one.member.dto.ApplicantMemberListResponseDto; +import org.one.member.repository.ApplicantMemberRepository; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -29,7 +29,7 @@ public List getApplicantList(ApplicantMemberList //엔티티에서 dto형태로 구조를 변환하여 리스트를 만들어 반환 return applicantMemberList.stream() - .map(ApplicantMemberListResponseDto::new) + .map(ApplicantMemberListResponseDto::from) .toList(); } }