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

[스프링] 36. MemberController.edit을 조금 다듬어서 테스트해보자.

장아장 2023. 2. 20. 20:35

일단, 다듬는 이유부터 말하자면, 실제 웹사이트에서 회원 정보 수정을 할 때를 생각해보았을 때, 이게 이상하다고 느꼈기 때문이다. 

보통 회원정보를 수정하면, 한 페이지에서 이메일, 닉네임, 비밀번호 수정등을 모두 하는데, 

지금 만든 프로젝트에서는 각 정보마다 수정을 따로 해주어야 하기 때문이다. 

이걸 동적으로 하면 되겠다는 생각이 들었다. 

 

기존의 코드를 조금 인용해서, 

@Transactional
public void editMember(EditMemberRequestDto editMemberRequestDto, Member member){
    if(editMemberRequestDto.getNickname() == null && editMemberRequestDto.getEmail() == null
            && editMemberRequestDto.getPassword() == null && editMemberRequestDto.getPasswordCheck() == null)
        throw new NeedToAddEditConditionException();
    if((editMemberRequestDto.getPassword() != null && editMemberRequestDto.getPasswordCheck() == null) ||
            (editMemberRequestDto.getPassword() == null && editMemberRequestDto.getPasswordCheck() != null))
        throw new NeedToPutPasswordTwiceToEditException();
    if(editMemberRequestDto.getNickname() != null)
        changeMemberNickname(editMemberRequestDto.getNickname(), member);
    if(editMemberRequestDto.getEmail() != null)
        changeMemberEmail(editMemberRequestDto.getEmail(), member);
    if(editMemberRequestDto.getPassword() != null && editMemberRequestDto.getPasswordCheck() != null)
        changeMemberPassword(editMemberRequestDto.getPassword()
                , editMemberRequestDto.getPasswordCheck(), member);
}

이런 식으로 기능을 만들었다. 

전부 null이면 예외처리하고, 

비밀번호 2회 입력중에 하나만 있을 때 예외처리하고, 

null이 아닌 값들만 수정을 해주는 방식으로 만들었다.

기존의 change~~의 메서드를 그대로 이용했다. 물론, 입력 파라미터를 dto에서 문자열과 Member로 변경했다. 

 

테스트도 만들어서 검증까지는 끝마쳤다. 만약 궁금하면 깃허브로 가보면 된다

 

이걸 컨트롤러로 끌고와서 테스트를 했다. 

  • 400에러
    • 이미 사용중인 닉네임
    • 이미 사용중인 이메일
    • 비밀번호가 서로 다를 때
    • 비밀번호가 둘중 하나 null일 때
    • 비밀번호가 기존과 같을 때 
    • 이메일 형식이 올바르지 않을 때 
  • 401에러
    • 로그인해서 받는 토큰이 존재하지 않을 때
  • 200 성공

400과 200은 기존의 서비스 로직에서 발생하는 예외이며, 401에러는 Spring Security에서 처리된다. 

일단 테스트를 성공하는 경우만 만들어서 보여보자면, 

@Test
@DisplayName("닉네임 변경을 요청할 떄, 성공하면 200코드와 수정에 성공했음을 반환해준다. ")
public void editNickname_Success() throws Exception{
    //given
    authService.registerNewMember(RegisterRequestDto.builder()
            .username("test")
            .nickname("test")
            .email("test@test.com")
            .password("test")
            .passwordCheck("test").build());
    TokenResponseDto tokenResponseDto = authService.signIn(SignInRequestDto.builder()
            .username("test")
            .password("test").build());
    EditMemberRequestDto editMemberRequestDto = EditMemberRequestDto.builder()
            .nickname("newNick")
            .build();
    //expected
    mvc.perform(MockMvcRequestBuilders.patch("/api/members/edit")
            .header("Authorization", "Bearer ".concat(tokenResponseDto.getAccessToken()))
            .header("RefreshToken", "Bearer ".concat(tokenResponseDto.getRefreshToken()))
            .contentType(MediaType.APPLICATION_JSON)
            .content(makeJson(editMemberRequestDto)))
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andExpect(MockMvcResultMatchers.jsonPath("$.success").value(true))
            .andExpect(MockMvcResultMatchers.jsonPath("$.code").value(200))
            .andExpect(MockMvcResultMatchers.jsonPath("$.result.data").value("수정을 성공했습니다."))
            .andDo(MockMvcResultHandlers.print());
    Assertions.assertThat(memberRepository.findByUsername("test").get().getNickname()).isEqualTo("newNick");
}

이런식으로 로그인로직을 이용해 토큰을 넣어서 만들었다. 

그렇다면, 컨트롤러에서 서비스로직에 파라미터로 Member를 넣어야 한다는 뜻이다. 

이것도 구현을 컨트롤러에서 만들까 했는데, 그러진 않았다. 

Member -> MemberRepository -> MemberService -> MemberController로 레이어를 구분했는데, 컨트롤러에서 MemberRepository.findByUsername으로 해서 다시 찾아오게 하는 것을 진행하고싶진 않았다. 

그래서, MemberService에 로직을 하나 추가했다. 

public Member findMemberByUsername(String username){
    return memberRepository.findByUsername(username).orElseThrow(MemberNotFoundException::new);
}

이걸 이용해 컨트롤러에서 Member를 불러올 것이다. 

SecurityContextHolder.getContext().getAuthentication().getName()을 이용해 현재 이용하는 멤버의 username을 가져오고, 

이걸 위의 메서드에 넣었다. 

private Member getUsingMember(){
    String username = SecurityContextHolder.getContext().getAuthentication().getName();
    return memberService.findMemberByUsername(username);
}

그렇게 만들어진 컨트롤러 로직을 이용해

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/members")
public class MemberController {

    private final MemberService memberService;

    @PatchMapping("/edit")
    public Response edit(@RequestBody EditMemberRequestDto editMemberRequestDto){
        memberService.editMember(editMemberRequestDto, getUsingMember());
        return Response.success("수정을 성공했습니다.");
    }

이런식으로 기능을 만들었다. 

 

이제 테스트를 해보았다. 

나이쑤~