diff --git a/src/main/java/com/potatocake/everymoment/controller/FriendController.java b/src/main/java/com/potatocake/everymoment/controller/FriendController.java index d108739..8fb3f75 100644 --- a/src/main/java/com/potatocake/everymoment/controller/FriendController.java +++ b/src/main/java/com/potatocake/everymoment/controller/FriendController.java @@ -45,10 +45,9 @@ public ResponseEntity> getOneFriendDia @GetMapping("/friends") public ResponseEntity> getFriendList( @RequestParam(required = false) String nickname, - @RequestParam(required = false) String email, @RequestParam(defaultValue = "0") int key, @RequestParam(defaultValue = "10") int size) { - FriendListResponse friendList = friendService.getFriendList(nickname, email, key, size); + FriendListResponse friendList = friendService.getFriendList(nickname, key, size); SuccessResponse response = SuccessResponse.builder() .code(HttpStatus.OK.value()) .message("success") diff --git a/src/main/java/com/potatocake/everymoment/controller/MemberController.java b/src/main/java/com/potatocake/everymoment/controller/MemberController.java index 3d255dc..e680a93 100644 --- a/src/main/java/com/potatocake/everymoment/controller/MemberController.java +++ b/src/main/java/com/potatocake/everymoment/controller/MemberController.java @@ -16,6 +16,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -32,7 +33,7 @@ public class MemberController { private final MemberService memberService; - @Operation(summary = "로그인", description = "이메일과 닉네임으로 로그인합니다.") + @Operation(summary = "로그인", description = "회원 번호와 닉네임으로 로그인합니다.") // @ApiResponses(value = { // @ApiResponse(responseCode = "200", description = "로그인 성공", // content = @Content(schema = @Schema(implementation = JwtResponse.class))), @@ -49,11 +50,10 @@ public ResponseEntity> login(@RequestBody MemberLog @GetMapping public ResponseEntity> searchMembers( @RequestParam(required = false) String nickname, - @RequestParam(required = false) String email, @RequestParam(required = false) Long key, @RequestParam(defaultValue = "10") int size) { - MemberSearchResponse response = memberService.searchMembers(nickname, email, key, size); + MemberSearchResponse response = memberService.searchMembers(nickname, key, size); return ResponseEntity.ok() .body(SuccessResponse.ok(response)); @@ -88,6 +88,14 @@ public ResponseEntity> updateMemberInfo(@AuthenticationPri .body(SuccessResponse.ok()); } + @DeleteMapping + public ResponseEntity> deleteMember(@AuthenticationPrincipal MemberDetails memberDetails) { + memberService.deleteMember(memberDetails.getId()); + + return ResponseEntity.ok() + .body(SuccessResponse.ok()); + } + private void validateProfileUpdate(MultipartFile profileImage, String nickname) { if (profileImage == null && !StringUtils.hasText(nickname)) { throw new GlobalException(ErrorCode.INFO_REQUIRED); diff --git a/src/main/java/com/potatocake/everymoment/dto/request/MemberLoginRequest.java b/src/main/java/com/potatocake/everymoment/dto/request/MemberLoginRequest.java index 005375b..f59d229 100644 --- a/src/main/java/com/potatocake/everymoment/dto/request/MemberLoginRequest.java +++ b/src/main/java/com/potatocake/everymoment/dto/request/MemberLoginRequest.java @@ -5,7 +5,7 @@ @Getter public class MemberLoginRequest { - private String email; + private Long number; private String nickname; } diff --git a/src/main/java/com/potatocake/everymoment/dto/response/MemberDetailResponse.java b/src/main/java/com/potatocake/everymoment/dto/response/MemberDetailResponse.java index 0180208..9abbac6 100644 --- a/src/main/java/com/potatocake/everymoment/dto/response/MemberDetailResponse.java +++ b/src/main/java/com/potatocake/everymoment/dto/response/MemberDetailResponse.java @@ -10,6 +10,5 @@ public class MemberDetailResponse { private Long id; private String profileImageUrl; private String nickname; - private String email; } diff --git a/src/main/java/com/potatocake/everymoment/entity/Member.java b/src/main/java/com/potatocake/everymoment/entity/Member.java index fa657f4..0142578 100644 --- a/src/main/java/com/potatocake/everymoment/entity/Member.java +++ b/src/main/java/com/potatocake/everymoment/entity/Member.java @@ -11,11 +11,15 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; @Getter @Builder @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) +@SQLDelete(sql = "UPDATE member SET deleted = true WHERE id = ?") +@SQLRestriction("deleted = false") @Entity public class Member extends BaseTimeEntity { @@ -23,8 +27,8 @@ public class Member extends BaseTimeEntity { @Id private Long id; - @Column(nullable = false, unique = true, length = 50) - private String email; + @Column(nullable = false, unique = true) + private Long number; @Column(nullable = false, length = 50) private String nickname; @@ -32,6 +36,9 @@ public class Member extends BaseTimeEntity { @Lob private String profileImageUrl; + @Column(nullable = false) + private boolean deleted = false; + public void update(String nickname, String profileImageUrl) { this.nickname = nickname; this.profileImageUrl = profileImageUrl; diff --git a/src/main/java/com/potatocake/everymoment/exception/ValidationErrorMessage.java b/src/main/java/com/potatocake/everymoment/exception/ValidationErrorMessage.java index 695c825..71dcdf7 100644 --- a/src/main/java/com/potatocake/everymoment/exception/ValidationErrorMessage.java +++ b/src/main/java/com/potatocake/everymoment/exception/ValidationErrorMessage.java @@ -4,13 +4,4 @@ public class ValidationErrorMessage { public static final String VALIDATION_ERROR = "입력 데이터의 유효성을 검사하던 중 문제가 발생했습니다."; - public static final String EMAIL_FORMAT_INVALID = "올바른 이메일 형식을 입력해 주세요."; - public static final String EMAIL_NOT_BLANK = "이메일은 필수 입력 값입니다."; - public static final String EMAIL_SIZE_INVALID = "이메일은 50자 이하여야 합니다."; - - public static final String NICKNAME_NOT_BLANK = "닉네임을 입력해 주세요."; - public static final String NICKNAME_SIZE_INVALID = "닉네임은 50자 이하여야 합니다."; - - public static final String CATEGORY_NAME_NOT_BLANK = "카테고리명을 입력해 주세요."; - } diff --git a/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java b/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java index 03b501c..135e588 100644 --- a/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java +++ b/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java @@ -9,11 +9,10 @@ public interface MemberRepository extends JpaRepository { - Optional findByEmail(String email); + Optional findByNumber(Long number); - boolean existsByEmail(String email); + boolean existsByNumber(Long number); - Window findByNicknameContainingAndEmailContaining(String nickname, String email, ScrollPosition position, - Pageable pageable); + Window findByNicknameContaining(String nickname, ScrollPosition position, Pageable pageable); } diff --git a/src/main/java/com/potatocake/everymoment/security/MemberAuthenticationService.java b/src/main/java/com/potatocake/everymoment/security/MemberAuthenticationService.java index c788d24..340e981 100644 --- a/src/main/java/com/potatocake/everymoment/security/MemberAuthenticationService.java +++ b/src/main/java/com/potatocake/everymoment/security/MemberAuthenticationService.java @@ -16,8 +16,8 @@ public class MemberAuthenticationService implements UserDetailsService { private final MemberRepository memberRepository; @Override - public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { - Member member = memberRepository.findByEmail(email) + public UserDetails loadUserByUsername(String number) throws UsernameNotFoundException { + Member member = memberRepository.findByNumber(Long.valueOf(number)) .orElseThrow(() -> new UsernameNotFoundException(ErrorCode.LOGIN_FAILED.getMessage())); return new MemberDetails(member); diff --git a/src/main/java/com/potatocake/everymoment/security/MemberDetails.java b/src/main/java/com/potatocake/everymoment/security/MemberDetails.java index c93d8e0..b4fedca 100644 --- a/src/main/java/com/potatocake/everymoment/security/MemberDetails.java +++ b/src/main/java/com/potatocake/everymoment/security/MemberDetails.java @@ -31,7 +31,7 @@ public String getPassword() { @Override public String getUsername() { - return member.getEmail(); + return String.valueOf(member.getNumber()); } public Long getId() { diff --git a/src/main/java/com/potatocake/everymoment/security/filter/LoginFilter.java b/src/main/java/com/potatocake/everymoment/security/filter/LoginFilter.java index 18760f8..53a2951 100644 --- a/src/main/java/com/potatocake/everymoment/security/filter/LoginFilter.java +++ b/src/main/java/com/potatocake/everymoment/security/filter/LoginFilter.java @@ -53,12 +53,14 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ MemberLoginRequest loginRequest = getLoginRequest(request); - if (loginRequest.getEmail() != null && loginRequest.getNickname() != null) { + if (loginRequest.getNumber() == null || loginRequest.getNickname() == null) { + throw new AuthenticationServiceException("Invalid login request"); + } else { registerIfNotExists(loginRequest); } UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated( - loginRequest.getEmail(), DEFAULT_PASSWORD); + String.valueOf(loginRequest.getNumber()), DEFAULT_PASSWORD); return authenticationManager.authenticate(authRequest); } @@ -117,14 +119,14 @@ private ErrorResponse getErrorResponse(ErrorCode loginFailed) { } private void registerIfNotExists(MemberLoginRequest loginRequest) { - if (!memberRepository.existsByEmail(loginRequest.getEmail())) { + if (!memberRepository.existsByNumber(loginRequest.getNumber())) { memberRepository.save(getMember(loginRequest)); } } private Member getMember(MemberLoginRequest loginRequest) { Member member = Member.builder() - .email(loginRequest.getEmail()) + .number(loginRequest.getNumber()) .nickname(loginRequest.getNickname()) .build(); diff --git a/src/main/java/com/potatocake/everymoment/service/FriendService.java b/src/main/java/com/potatocake/everymoment/service/FriendService.java index d252170..ba78771 100644 --- a/src/main/java/com/potatocake/everymoment/service/FriendService.java +++ b/src/main/java/com/potatocake/everymoment/service/FriendService.java @@ -51,8 +51,9 @@ public OneFriendDiariesResponse OneFriendDiariesResponse(Long id, LocalDate date Pageable pageable = PageRequest.of(key, size); - Page diaries = diaryRepository.findAll(DiarySpecification.filterDiaries(null, null, date, null, null, null) - .and((root, query, builder) -> builder.equal(root.get("memberId").get("id"), id)), pageable); + Page diaries = diaryRepository.findAll( + DiarySpecification.filterDiaries(null, null, date, null, null, null) + .and((root, query, builder) -> builder.equal(root.get("memberId").get("id"), id)), pageable); List diaryList = diaries.getContent().stream() .map(this::convertToFriendDiariesResponseDTO) @@ -68,7 +69,7 @@ public OneFriendDiariesResponse OneFriendDiariesResponse(Long id, LocalDate date //내 친구 목록 조회 @Transactional(readOnly = true) - public FriendListResponse getFriendList(String nickname, String email, int key, int size) { + public FriendListResponse getFriendList(String nickname, int key, int size) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); MemberDetails memberDetails = (MemberDetails) authentication.getPrincipal(); Member currentMember = memberDetails.getMember(); @@ -76,7 +77,7 @@ public FriendListResponse getFriendList(String nickname, String email, int key, Pageable pageable = PageRequest.of(key, size); - Specification spec = FriendSpecification.filterFriends(memberId, nickname, email) + Specification spec = FriendSpecification.filterFriends(memberId, nickname) .and((root, query, builder) -> builder.equal(root.get("memberId").get("id"), memberId)); Page friends = friendRepository.findAll(spec, pageable); @@ -147,7 +148,7 @@ private FriendDiarySimpleResponse convertToFriendDiariesResponseDTO(Diary savedD } //친구 프로필 DTO 변환 - private FriendProfileResponse convertToFriendProfileResponseDTO(Friend friend){ + private FriendProfileResponse convertToFriendProfileResponseDTO(Friend friend) { Member friendMember = friend.getFriendId(); return FriendProfileResponse.builder() .id(friendMember.getId()) diff --git a/src/main/java/com/potatocake/everymoment/service/FriendSpecification.java b/src/main/java/com/potatocake/everymoment/service/FriendSpecification.java index 6d193f1..71b9451 100644 --- a/src/main/java/com/potatocake/everymoment/service/FriendSpecification.java +++ b/src/main/java/com/potatocake/everymoment/service/FriendSpecification.java @@ -10,7 +10,7 @@ import org.springframework.data.jpa.domain.Specification; public class FriendSpecification { - public static Specification filterFriends(Long memberId, String nickname, String email) { + public static Specification filterFriends(Long memberId, String nickname) { return (Root root, CriteriaQuery query, CriteriaBuilder builder) -> { Predicate predicate = builder.conjunction(); @@ -21,11 +21,6 @@ public static Specification filterFriends(Long memberId, String nickname predicate = builder.and(predicate, builder.like(friendJoin.get("nickname"), "%" + nickname + "%")); } - if (email != null) { - Join friendJoin = root.join("friendId"); - predicate = builder.and(predicate, builder.like(friendJoin.get("email"), "%" + email + "%")); - } - return predicate; }; } diff --git a/src/main/java/com/potatocake/everymoment/service/MemberService.java b/src/main/java/com/potatocake/everymoment/service/MemberService.java index f93bfef..4ac4d15 100644 --- a/src/main/java/com/potatocake/everymoment/service/MemberService.java +++ b/src/main/java/com/potatocake/everymoment/service/MemberService.java @@ -31,8 +31,8 @@ public class MemberService { private final S3FileUploader s3FileUploader; @Transactional(readOnly = true) - public MemberSearchResponse searchMembers(String nickname, String email, Long key, int size) { - Window window = fetchMemberWindow(nickname, email, key, size); + public MemberSearchResponse searchMembers(String nickname, Long key, int size) { + Window window = fetchMemberWindow(nickname, key, size); List members = convertToMemberResponses(window.getContent()); Long nextKey = pagingUtil.getNextKey(window, Member::getId); @@ -51,7 +51,6 @@ public MemberDetailResponse getMyInfo(Long memberId) { .id(member.getId()) .profileImageUrl(member.getProfileImageUrl()) .nickname(member.getNickname()) - .email(member.getEmail()) .build(); } @@ -76,15 +75,20 @@ public void updateMemberInfo(Long id, MultipartFile profileImage, String nicknam member.update(nickname, profileImageUrl); } - private Window fetchMemberWindow(String nickname, String email, Long key, int size) { + public void deleteMember(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); + + memberRepository.delete(member); + } + + private Window fetchMemberWindow(String nickname, Long key, int size) { ScrollPosition scrollPosition = pagingUtil.createScrollPosition(key); Pageable pageable = pagingUtil.createPageable(size, ASC); String searchNickname = (nickname == null) ? "" : nickname; - String searchEmail = (email == null) ? "" : email; - return memberRepository.findByNicknameContainingAndEmailContaining(searchNickname, searchEmail, scrollPosition, - pageable); + return memberRepository.findByNicknameContaining(searchNickname, scrollPosition, pageable); } private List convertToMemberResponses(List members) { diff --git a/src/test/java/com/potatocake/everymoment/controller/MemberControllerTest.java b/src/test/java/com/potatocake/everymoment/controller/MemberControllerTest.java index 02ad1a3..c1a1ad1 100644 --- a/src/test/java/com/potatocake/everymoment/controller/MemberControllerTest.java +++ b/src/test/java/com/potatocake/everymoment/controller/MemberControllerTest.java @@ -47,17 +47,15 @@ class MemberControllerTest { void should_SearchMembers_When_ValidInput() throws Exception { // given String nickname = "testUser"; - String email = "test@test.com"; Long key = 1L; int size = 10; MemberSearchResponse response = MemberSearchResponse.builder().build(); - given(memberService.searchMembers(nickname, email, key, size)).willReturn(response); + given(memberService.searchMembers(nickname, key, size)).willReturn(response); // when ResultActions result = mockMvc.perform(get("/api/members") .param("nickname", nickname) - .param("email", email) .param("key", key.toString()) .param("size", String.valueOf(size))); @@ -67,7 +65,7 @@ void should_SearchMembers_When_ValidInput() throws Exception { .andExpect(jsonPath("$.code").value(200)) .andExpect(jsonPath("$.message").value("success")); - then(memberService).should().searchMembers(nickname, email, key, size); + then(memberService).should().searchMembers(nickname, key, size); } @Test @@ -75,7 +73,7 @@ void should_SearchMembers_When_ValidInput() throws Exception { void should_ReturnMyInfo_When_ValidMember() throws Exception { // given Long memberId = 1L; - MemberDetails memberDetails = createMemberDetails(memberId, "test@example.com", "test"); + MemberDetails memberDetails = createMemberDetails(memberId, 1234L, "test"); MemberDetailResponse response = MemberDetailResponse.builder().build(); given(memberService.getMyInfo(memberId)).willReturn(response); @@ -120,7 +118,7 @@ void should_ReturnMemberInfo_When_ValidMemberId() throws Exception { void should_UpdateMemberInfo_When_ValidInput() throws Exception { // given Long memberId = 1L; - MemberDetails memberDetails = createMemberDetails(memberId, "test@example.com", "test"); + MemberDetails memberDetails = createMemberDetails(memberId, 1234L, "test"); MockMultipartFile profileImage = new MockMultipartFile("profileImage", "image.png", "image/png", new byte[]{}); String nickname = "newNickname"; @@ -154,16 +152,16 @@ void should_ThrowException_When_ProfileImageAndNicknameAreMissing() throws Excep then(memberService).shouldHaveNoInteractions(); } - private Member createMember(Long memberId, String email, String nickname) { + private Member createMember(Long memberId, Long number, String nickname) { return Member.builder() .id(memberId) - .email(email) + .number(number) .nickname(nickname) .build(); } - private MemberDetails createMemberDetails(Long memberId, String email, String nickname) { - Member member = createMember(memberId, email, nickname); + private MemberDetails createMemberDetails(Long memberId, Long number, String nickname) { + Member member = createMember(memberId, number, nickname); return new MemberDetails(member); } diff --git a/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java b/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java index 4cb1bd8..2e72d9e 100644 --- a/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java +++ b/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java @@ -19,58 +19,57 @@ class MemberRepositoryTest { private MemberRepository memberRepository; @Test - @DisplayName("이메일로 회원을 성공적으로 조회한다.") - void should_FindMemberByEmail_When_EmailExists() { + @DisplayName("회원 번호로 회원을 성공적으로 조회한다.") + void should_FindMemberByNumber_When_NumberExists() { // given - String email = "test@test.com"; + Long number = 1234L; memberRepository.save(Member.builder() - .email(email) + .number(number) .nickname("testUser") .build()); // when - Optional foundMember = memberRepository.findByEmail(email); + Optional foundMember = memberRepository.findByNumber(number); // then assertThat(foundMember).isPresent(); - assertThat(foundMember.get().getEmail()).isEqualTo(email); + assertThat(foundMember.get().getNumber()).isEqualTo(number); } @Test - @DisplayName("이메일이 존재하는지 확인한다.") - void should_ReturnTrue_When_EmailExists() { + @DisplayName("회원 번호가 존재하는지 확인한다.") + void should_ReturnTrue_When_NumberExists() { // given - String email = "test@test.com"; + Long number = 1234L; memberRepository.save(Member.builder() - .email(email) + .number(number) .nickname("testUser") .build()); // when - boolean exists = memberRepository.existsByEmail(email); + boolean exists = memberRepository.existsByNumber(number); // then assertThat(exists).isTrue(); } @Test - @DisplayName("닉네임과 이메일을 포함하여 스크롤 방식으로 회원 목록을 조회한다.") - void should_FindByNicknameContainingAndEmailContaining_When_ValidScrollPosition() { + @DisplayName("닉네임을 포함하여 스크롤 방식으로 회원 목록을 조회한다.") + void should_FindByNicknameContaining_When_ValidScrollPosition() { // given String nickname = "test"; - String email = "test"; + Long number = 1234L; for (int i = 1; i <= 15; i++) { memberRepository.save(Member.builder() .nickname(nickname + i) - .email(email + i + "@test.com") + .number(number + i) .build()); } // when ScrollPosition scrollPosition = ScrollPosition.offset(); PageRequest pageRequest = PageRequest.of(0, 10); - Window window = memberRepository.findByNicknameContainingAndEmailContaining(nickname, email, - scrollPosition, pageRequest); + Window window = memberRepository.findByNicknameContaining(nickname, scrollPosition, pageRequest); // then assertThat(window).isNotNull(); @@ -79,15 +78,14 @@ void should_FindByNicknameContainingAndEmailContaining_When_ValidScrollPosition( } @Test - @DisplayName("닉네임과 이메일이 일치하지 않으면 빈 결과를 반환한다.") - void should_ReturnEmpty_When_NoMatchingNicknameAndEmail() { + @DisplayName("닉네임으로 검색 결과가 없다면 빈 결과를 반환한다.") + void should_ReturnEmpty_When_NoMatchingNickname() { // given ScrollPosition scrollPosition = ScrollPosition.offset(); PageRequest pageRequest = PageRequest.of(0, 10); // when - Window window = memberRepository.findByNicknameContainingAndEmailContaining("nonexistent", - "nonexistent@test.com", scrollPosition, pageRequest); + Window window = memberRepository.findByNicknameContaining("nonexistent", scrollPosition, pageRequest); // then assertThat(window.getContent()).isEmpty(); diff --git a/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java b/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java index 3c8e313..5106a5e 100644 --- a/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java +++ b/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java @@ -47,24 +47,23 @@ class MemberServiceTest { void should_ReturnMemberList_When_ValidSearchConditions() { // given String nickname = "testUser"; - String email = "test@test.com"; Long key = 1L; int size = 10; List members = List.of(Member.builder().build()); Window window = Window.from(members, ScrollPosition::offset, false); - given(memberRepository.findByNicknameContainingAndEmailContaining(anyString(), anyString(), any(), any())) + given(memberRepository.findByNicknameContaining(anyString(), any(), any())) .willReturn(window); // when - MemberSearchResponse result = memberService.searchMembers(nickname, email, key, size); + MemberSearchResponse result = memberService.searchMembers(nickname, key, size); // then assertThat(result).isNotNull(); assertThat(result.getMembers()).isNotEmpty(); then(memberRepository).should() - .findByNicknameContainingAndEmailContaining(anyString(), anyString(), any(), any()); + .findByNicknameContaining(anyString(), any(), any()); } @Test