Spring Projcect/[팀플] In & Out 가계부

회원 예외 처리 MemberErrorCode, ExceptionHandler

계란💕 2022. 10. 25. 23:21
  • MemberErrorCode
<hide/>
@Getter
@AllArgsConstructor
public enum MemberErrorCode {

    /**
     * 회원가입 - 이미 존재하는 이메일, 휴대폰, 닉네임 오류
     */
    EXIST_EMAIL("이미 가입된 이메일입니다."),
    EXIST_PHONE("동일한 휴대폰 번호가 존재합니다. 다른 번호를 입력하세요."),
    EXIST_NICKNAME("동일한 닉네임이 존재합니다. 다른 닉네임을 입력하세요"),

    /**
     * 회원가입 - 비밀번호 오류
     */
    PASSWORD_LENGTH_MORE_THAN_8("비밀번호는 8자리 이상이어야합니다.(영문자, 숫자, 특수문자를 각각 1글자 이상 포함)"),
    PASSWORD_NOT_CONTAIN_DIGIT("비밀번호는 숫자를 한 글자 이상 포함해야합니다."),
    PASSWORD_NOT_CONTAIN_SPECIAL("비밀번호는 특수 문자를 한 글자 이상 포함해야합니다."),
    PASSWORD_NOT_CONTAIN_CHARACTER("비밀번호는 영문자를 한 글자 이상 포함해야합니다."),
    PASSWORD_NOT_CONTAIN_DIGIT_AND_SPECIAL("비밀번호는 숫자, 특수 문자를 각각 한 글자 이상 포함해야합니다."),
    PASSWORD_NOT_CONTAIN_DIGIT_AND_CHARACTER("비밀번호는 숫자, 영문자 문자를 각각 한 글자 이상 포함해야합니다."),
    PASSWORD_NOT_CONTAIN_CHARACTER_AND_SPECIAL("비밀번호는 영문자, 특수 문자를 각각 한 글자 이상 포함해야합니다."),

    /**
     * 회원 조회 오류
     */
    NOT_EXIST_MEMBER("회원 정보가 존재하지 않습니다."),
    NOT_EXIST_EMAIL("이메일이 존재하지 않습니다."),
    NOT_EXIST_PHONE("연락처(번호)가 존재하지 않습니다."),

    /**
     * 회원 정보 수정 오류
     */
    CONTAINS_BLANK("회원 정보는 공백을 포함할 수 없습니다."),

    /**
     * 비밀번호 오류
     */
    NOT_MATCH_PASSWORD("회원 비밀번호를 잘못 입력했습니다.");


    private final String description;

}

 

  • MemberException
<hide/>
@Getter
@Setter
@AllArgsConstructor
public class MemberException extends RuntimeException {

    public MemberErrorCode errorCode;

}

 

  • MemberErrorResponse
<hide/>
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class MemberErrorResponse {

    private MemberErrorCode errorCode;
    private String message;

}

 

  • ExceptionHandler
<hide/>
@RestControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler(IncomeException.class)
    protected ResponseEntity<IncomeErrorResponse> incomeHandlerCustomException(IncomeException e) {
        IncomeErrorResponse errorResponse = IncomeErrorResponse.builder()
            .errorCode(e.getErrorCode())
            .message(e.getErrorCode().getDescription())
            .build();

        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(ExpenseException.class)
    protected ResponseEntity<ExpenseErrorResponse> incomeHandlerCustomException(
        ExpenseException e) {
        ExpenseErrorResponse errorResponse = ExpenseErrorResponse.builder()
            .errorCode(e.getErrorCode())
            .message(e.getErrorCode().getDescription())
            .build();

        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(MemberException.class)
    protected ResponseEntity<MemberErrorResponse> MemberHandlerCustomException(
        MemberException e) {
        MemberErrorResponse errorResponse = MemberErrorResponse.builder()
            .errorCode(e.getErrorCode())
            .message(e.getErrorCode().getDescription())
            .build();

        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }
}

 

  • ImplTest
    • 테스트 코드에 예외처리 적용하기
<hide/>
@Test
@DisplayName("회원 정보 수정(공백 포함) - 실패 (1)")
void updateInfo_fail_blank() {

    // given
    Member member = Member.builder().email("egg@naver.com").address("서울특별시")
        .phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
        .nickName("강동원").password("abc!@#12").build();
    given(memberRepository.findByEmail(anyString())).willReturn(Optional.of(member));
    UpdateMemberInput input = UpdateMemberInput.builder().address("강원도").nickName("치킨")
        .phone("010-11 11-2313").birth(LocalDate.now()).memberPhotoUrl("c:").gender("여")
        .address("강원도").build();

    // when
    MemberException exception = assertThrows(MemberException.class,
        () -> memberService.updateInfo(member.getEmail(), input));

    // then
    assertEquals(MemberErrorCode.CONTAINS_BLANK.getDescription(),
        exception.getErrorCode().getDescription());

}


@Test
@DisplayName("회원 정보 수정(같은 폰번호가 존재하는 경우) - 실패 (2)")
void updateInfo_fail_sameNickName() {

    // given
    Member memberA = Member.builder().email("egg@naver.com").address("서울특별시")
        .phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
        .nickName("강동원").password("abc!@#12").build();

    given(memberRepository.findByEmail(any())).willReturn(Optional.of(memberA));

    Member memberB = Member.builder().email("ogh@naver.com").address("인천광역시")
        .phone("010-9999-0000").birth(LocalDate.from(LocalDate.of(1998, 9, 30))).gender("남")
        .nickName("소지섭").password("abc!@#12").build();

    given(memberRepository.findByPhone(any())).willReturn(Optional.of(memberB));

    UpdateMemberInput input = UpdateMemberInput.builder().address("강원도").nickName("강동원")
        .phone("010-9999-0000").birth(LocalDate.now()).memberPhotoUrl("c:").gender("여")
        .address("강원도").build();

    // when
    MemberException exception = assertThrows(MemberException.class,
        () -> memberService.updateInfo(memberB.getEmail(), input));

    // then
    assertEquals(MemberErrorCode.EXIST_PHONE.getDescription(),
        exception.getErrorCode().getDescription());
}

@Test
@DisplayName("회원 정보 수정(같은 닉네임이 존재하는 경우) - 실패(3)")
void updateInfo_fail_samePhoneNumber() {

    // given
    Member memberA = Member.builder().email("egg@naver.com").address("서울특별시")
        .phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
        .nickName("강동원").password("abc!@#12").build();
    given(memberRepository.findByEmail(any())).willReturn(Optional.of(memberA));

    Member memberB = Member.builder().email("ogh@naver.com").address("인천광역시")
        .phone("010-9999-0000").birth(LocalDate.from(LocalDate.of(1998, 9, 30))).gender("남")
        .nickName("소지섭").password("abc!@#12").build();

    given(memberRepository.findByNickName(any())).willReturn(Optional.of(memberB));

    UpdateMemberInput input = UpdateMemberInput.builder().address("강원도").nickName("소지섭")
        .phone("010-2222-0000").birth(LocalDate.now()).memberPhotoUrl("c:").gender("여")
        .address("강원도").build();

    // when
    MemberException exception = assertThrows(MemberException.class,
        () -> memberService.updateInfo(memberA.getEmail(), input));

    // then
    assertEquals(MemberErrorCode.EXIST_NICKNAME.getDescription(),
        exception.getErrorCode().getDescription());


}


@Test
@DisplayName("비밀번호 변경 - 성공")
void updatePassword() {

    // given
    Member member = Member.builder().email("egg@naver.com").address("서울특별시")
        .phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
        .nickName("강동원").build();
    String encPassword = bCryptPasswordEncoder.encode("abc!@#12");
    member.setPassword(encPassword);
    given(memberRepository.findByEmail("egg@naver.com")).willReturn(Optional.of(member));

    // when
    String email = "egg@naver.com";
    ChangePasswordInput input = ChangePasswordInput.builder().password("abc!@#12")
        .newPassword("xyz@#123").build();

    // then
    memberService.changePassword(email, input);


}


@Test
@DisplayName("비밀번호 변경 - 실패")
void updatePassword_fail() {

    // given
    Member member = Member.builder().email("egg@naver.com").address("서울특별시")
        .phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
        .nickName("강동원").build();
    String encPassword = bCryptPasswordEncoder.encode("abc!@#12");
    member.setPassword(encPassword);
    given(memberRepository.findByEmail(anyString())).willReturn(Optional.of(member));

    // when

    String email = "egg@naver.com";
    ChangePasswordInput input = ChangePasswordInput.builder().password("abc!@#12")
        .newPassword("xyz").build();

    // then
    MemberException exception = assertThrows(MemberException.class,
        () -> memberService.changePassword(email, input));
    assertEquals(MemberErrorCode.PASSWORD_LENGTH_MORE_THAN_8.getDescription(),
        exception.getErrorCode().getDescription());

}

 

  • Ctrl test
    • 컨트롤러에서 세션에 로그인된 정보를 가져오기 위해 Principal 객체를 이용할 경우, 테스트 코드에서는 이를 어떻게 적용할까?
    • 아래와 같이 UserTestingAuthenticationToken을 이용해서 테스트 코드에 적용가능하다.
User user = new User(email, pwd, AuthorityUtils.NO_AUTHORITIES);
TestingAuthenticationToken testingAuthenticationToken = new TestingAuthenticationToken(user, null);
<hide/>
  @Test
    @DisplayName("회원 비밀번호 변경 - 성공")
    void updatePassword() throws Exception {

        // given
        ChangePasswordInput input = ChangePasswordInput.builder().password("abc123!@")
            .newPassword("xyz098?!").build();
        String inputToJson = mapper.writeValueAsString(input);

        String email = "egg@naver.com";
        String pwd = "123abc?!";

        User user = new User(email, pwd, AuthorityUtils.NO_AUTHORITIES);
        TestingAuthenticationToken testingAuthenticationToken = new TestingAuthenticationToken(user,
            null);

        //when
        mockMvc.perform(patch("/api/member/password").contentType(MediaType.APPLICATION_JSON)
                .principal(testingAuthenticationToken).content(inputToJson)).andExpect(status().isOk())
            .andDo(print());
        ArgumentCaptor<ChangePasswordInput> captor = ArgumentCaptor.forClass(
            ChangePasswordInput.class);

        // then
        Mockito.verify(memberServiceImpl, times(1)).changePassword(anyString(), captor.capture());
        assertEquals(captor.getValue().getNewPassword(), input.getNewPassword());

    }

 

'Spring Projcect > [팀플] In & Out 가계부' 카테고리의 다른 글

회원 가입 API  (0) 2022.10.27
회원 로그아웃 API  (0) 2022.10.26
회원 로그인 API  (0) 2022.10.25
회원 정보 조회 및 수정 API  (0) 2022.10.21
회원 아이디 & 비밀번호 찾기  (0) 2022.10.19