스프링 공부/게시판 프로젝트 만들기

[스프링] 24. Member 데이터 변경을 비즈니스 로직에 추가하기

장아장 2023. 2. 7. 13:38

일단 정보 수정을 수행하는 로직의 테스트부터 만들어볼 것이다. 

  • 닉네임 수정
    • 닉네임 중복으로 예외처리
    • 정상적으로 수정
  • 이메일 수정
    • 이메일 중복으로 예외처리
    • 정상적으로 수정
  • 비밀번호 수정
    • 기존 비밀번호와 동일해 예외처리
    • 입력한 비밀번호 2가지가 서로 불일치
    • 정상적으로 수정

이렇게 총 3가지의 수정 메서드와, 각 메서드당 2(비밀번호는 3)가지 경우가 생기게 된다. 

결국 7가지 테스트를 만들어두고, 이에 대해 기능을 수행하는 메서드를 만들어보려고 한다. 

 

private Member makeMember(String index){
    RegisterRequestDto registerRequestDto = RegisterRequestDto.builder()
            .username("test" + index)
            .nickname("test" + index)
            .email("test" + index + "@test.com")
            .password("password" + index)
            .passwordCheck("password" + index)
            .build();
    memberService.registerNewMember(registerRequestDto);
    return new Member(registerRequestDto);
}

멤버를 생성하고, 저장해주는 기능과, 

 

@Test
@DisplayName("")
public void 닉네임_변경_중복() throws Exception{
    //given
    makeMember("1");
    Member member2 = makeMember("2");
    //when
    ChangeNicknameRequestDto changeNicknameRequestDto = new ChangeNicknameRequestDto("test1");
    //then
    assertThatThrownBy(()-> memberService.changeMemberNickname(changeNicknameRequestDto, member2))
            .isInstanceOf(NicknameAlreadyInUseException.class);
}

@Test
@DisplayName("")
public void 닉네임_변경_성공() throws Exception{
    //given
    makeMember("1");
    Member member2 = makeMember("2");
    //when
    ChangeNicknameRequestDto changeNicknameRequestDto = new ChangeNicknameRequestDto("test3");
    //then
    memberService.changeMemberNickname(changeNicknameRequestDto, member2);
}

닉네임을 예시로 들자면, 변경시도중에 예외처됨으로 이를 테스트하는 방법과, 성공시엔 아무 예외없이 진행되는 성공부분으로 분리해두었다. 

 

이렇게 닉네임, 이메일, 비밀번호에 대해서 테스트를 만들어보았다. 

 

이제 이 테스트를 성공하게 코드를 만들면 된다. 

하지만 어렵진 않다. 

 

결국에 기능적으로 존재해야 할 것은, 

validate + member.change정도의 로직이다. 

그리고, 이번엔 해당 멤버에 대한 정보를 수정하는 것이기 때문에 파라미터로 member를 받는 서비스 로직이다. 

심지어 validate은 이전에 회원가입에서 썼던 validate을 그대로 이용한다.

 

@Transactional
public void changeMemberEmail(ChangeEmailRequestDto changeEmailRequestDto, Member member){
    validateEmail(changeEmailRequestDto.getNewEmail());
    member.changeEmail(changeEmailRequestDto);
}

@Transactional
public void changeMemberNickname(ChangeNicknameRequestDto changeNicknameRequestDto, Member member){
    validateNickname(changeNicknameRequestDto.getNewNickname());
    member.changeNickname(changeNicknameRequestDto);
}

@Transactional
public void changeMemberPassword(ChangePasswordRequestDto changePasswordRequestDto, Member member){
    validatePasswordCheck(changePasswordRequestDto.getNewPassword(), changePasswordRequestDto.getNewPasswordCheck());
    validateWithCurrentPassword(changePasswordRequestDto, member.getPassword());
    changePasswordRequestDto.setNewPasswordCheck(passwordEncoder.encode(changePasswordRequestDto.getNewPassword()));
    member.changePassword(changePasswordRequestDto);
}

요청을 위해 모든 경우에 dto를 추가했다. 

dto에는, 단순하게 String타입의 닉네임, 이메일, 혹은 비밀번호 2번 쓰기가 인스턴스로 등록되어있다. 

요런식으로

 

딱 하나 차이나는 놈이 존재한다.

changePassword에서 변경되는 부분이 있고, 내가 만들면서 잘못했던 부분도 고치면서 넘어가려고 한다. 

 

private void validateWithCurrentPassword(ChangePasswordRequestDto changePasswordRequestDto, String currentPassword){
    if(passwordEncoder.matches(changePasswordRequestDto.getNewPassword(), currentPassword))
        throw new PasswordNotChangedException();
}

이놈이 추가되었다. 기존에 가지고 있던 비밀번호와, 입력한 비밀번호가 같을 경우, 예외처리를 해주어야 한다. 

그러기 위해서, passwordEncoder.matches로 평문과 암호문을 집어넣어 둘이 같으면 예외처리하게 시켰다. 

 

바꾸려는 비밀번호를 암호화시켜서 넣으려고 해도, 암호화 시키는 결과는 시간에 따라 항상 다른 값을 가진다. 

그렇기 때문에, 바꾸려는 암호화 기존의 암호가 다 password여도, 두 경우 다 다른 결과를 보인다. 

그렇기에 이렇게 로직을 수정하고, Member 로직에 있던 문자열로 비밀번호를 수정하는 로직들을 전부 제거했다. 

 

이렇게 만들어두고, 테스트를 동작시켰다. 

정상적으로 동작이 된다. 사실 수정의 경우는 그렇게 어렵지 않았다. 

생각보다 골칫거리였던 부분은, 비밀번호 변경시에 기존과 같은가를 찾는 로직을 확인하면서, 

내가 기존에 Member로직을 만들 때 실수를 했단 것을 깨닳았다. 

 

BCryptPasswordEncoder를 쓸 때에는, 매 순간 같은 문자열도 암호화되면 다른 암호가 된다.

그렇기 때문에, 암호문을 평문과 비교할 때, 

암호문.equals(bCryptPasswordEncoder.encode(평문))을 하는 것이 아닌, 

bCryptPasswordEncoder.matches(평문, 암호문)의 로직으로 수행하는 것이 맞다는 것을 나도 다시 상기할 수 있었다.