본문 바로가기
스프링과 JPA 기반 웹 어플리케이션 개발/1부 (개발환경, 회원가입, 로그인, 계정설정)

06. 회원 가입: 리팩토링 및 테스트

by Backchus 2020. 4. 17.

리팩토링 하기 전에 테스트 코드를 먼저 작성하자.

  • 그래야 코드를 변경한 이후에 불안하지 않다.
  • 변경한 코드가 무언가를 깨트리지 않았다는 것을 확인할 수 있다.

테스트할 것

  • 폼에 이상한 값이 들어간 경우에 다시 폼이 보이는가?
  • 폼에 값이 정상적인 경우
    • 가입한 회원 데이터가 존재하는가?
    • 이메일이 보내지는가?

리팩토링

  • 메소드가 너무 길지 않은가?
  • 코드를 읽기 쉬운가?
    • 내가 작성한 코드를 내가 읽기 어렵다면 남들에겐 훨씬 더 어렵다.
  • 코드가 적절한 위치에 있는가?
    • 객체들 사이의 의존관계
    • 책임이 너무 많진 않은지

 

AccountControllerTest에 회원가입 처리 테스트 작성(입력값 오류, 입력값 정상)

package me.weekbelt.studyolle.account;

@AutoConfigureMockMvc
@SpringBootTest
class AccountControllerTest {

   	// 기존 코드 ........
    @Autowired
    private AccountRepository accountRepository;

    @MockBean
    JavaMailSender javaMailSender;

   
    // 기존 코드 ............
    @DisplayName("회원 가입 처리 - 입력값 오류")
    @Test
    public void signUpSubmit_with_wrong_input() throws Exception {
        mockMvc.perform(post("/sign-up")
                .param("nickname", "joohyuk")
                .param("email", "email...")
                .param("password", "12345")
                .with(csrf()))      // POST 요청시 CSRF값을 비교하여 같으면 요청을 처리하고 다르면 403에러가 발생한다.
                .andExpect(status().isOk())
                .andExpect(view().name("account/sign-up"))
        ;
    }

    @DisplayName("회원 가입 처리 - 입력값 정상")
    @Test
    public void signUpSubmit_with_correct_input() throws Exception {
        // 회원 가입 요청이 제대로 작동하는지 화인
        mockMvc.perform(post("/sign-up")
                .param("nickname", "joohyuk")
                .param("email", "vfrvfr4207@hanmail.net")
                .param("password", "12345678")
                .with(csrf()))      // POST 요청시 CSRF값을 비교하여 같으면 요청을 처리하고 다르면 403에러가 발생한다.
                .andExpect(status().is3xxRedirection())
                .andExpect(view().name("redirect:/"))
        ;

        // 가입 된 회원이 존재하는지 확인
        assertThat(accountRepository.existsByEmail("vfrvfr4207@hanmail.net")).isTrue();
        // 회원가입시 JavaMailSender 통해 SimpleMailMessage 호출되는지
        then(javaMailSender).should().send(any(SimpleMailMessage.class));

    }
    
}

AccountController에서 signUbSubmit메소드의 리팩토링

package me.weekbelt.runningflex.account;

@RequiredArgsConstructor
@Controller
public class AccountController {

    private final SignUpFormValidator signUpFormValidator;
    private final AccountRepository accountRepository;
    private final JavaMailSender javaMailSender;

   
	// 기존 코드 .....
    @PostMapping("/sign-up")
    public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors) {
        // 입력값 검증
        if (errors.hasErrors()) {
            return "account/sign-up";
        }

        // 검증된 입력값을 통해 엔티티 생성
        Account account = Account.builder()
                .email(signUpForm.getEmail())
                .nickname(signUpForm.getNickname())
                .password(signUpForm.getPassword())  // TODO: 나중에 패스워드 인코딩 필요
                .emailVerified(false)
                .runningEnrollmentResultByEmail(true)
                .runningUpdatedByWeb(true)
                .build();

        // 생성한 엔티티를 DB에 저장
        Account newAccount = accountRepository.save(account);
        newAccount.generateEmailCheckToken();   // 이메일체크 토큰 생성

        // 메일 메시지 만들기
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(newAccount.getEmail());
        mailMessage.setSubject("RunningFlex, 회원 가입 인증");
        mailMessage.setText("/check-email-token?token=" + newAccount.getEmailCheckToken()
                + "&email=" + newAccount.getEmail());

        // 메일 메시지 보내기
        javaMailSender.send(mailMessage);

        return "redirect:/";
    }

}

 

signUpSubmit메소드의 코드를 보면 한 메소드에 너무 많은 기능이 포함되어 있다는 것을 알 수 있다.

입력값 검증 후 엔티티를 생성하여 계정을 저장하고 메일 메시지를 만들어서 보내는 로직을 컨트롤러에 모두 포함되어 있다 보니 코드가 길어지고 복잡해져 읽기가 불편하다.

따라서 계정을 생성하는 로직과 메일을 보내는 로직을 서비스 계층으로 빼서 리팩토링을 하는 것이 코드 가독성과 유지보수 측면에서 더 좋은 방법이다.

 

AccountService 생성

package me.weekbelt.studyolle.account;

@RequiredArgsConstructor
@Service
public class AccountService {

    private final AccountRepository accountRepository;
    private final JavaMailSender javaMailSender;

    @Transaction
    public void processNewAccount(SignUpForm signUpForm) {
        Account newAccount = saveNewAccount(signUpForm);
        newAccount.generateEmailCheckToken();
        sendSignUpConfirmEmail(newAccount);
    }

    private Account saveNewAccount(@Valid SignUpForm signUpForm) {
        Account account = Account.builder()
                .email(signUpForm.getEmail())
                .nickname(signUpForm.getNickname())
                .password(signUpForm.getPassword())     // TODO encoding 해야함
                .emailVerified(false)
                .studyEnrollmentResultByWeb(true)
                .studyUpdatedByWeb(true)
                .build();

        return accountRepository.save(account);
    }

    private void sendSignUpConfirmEmail(Account newAccount) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(newAccount.getEmail());
        mailMessage.setSubject("스터디올래, 회원 가입 인증");
        mailMessage.setText("/check-email-token?token=" + newAccount.getEmailCheckToken() + "&email=" + newAccount.getEmail());
        javaMailSender.send(mailMessage);
    }

}

 

AccountController의 signUpSubmit메소드 수정

package me.weekbelt.studyolle.account;

import javax.validation.Valid;

@RequiredArgsConstructor
@Controller
public class AccountController {

    private final SignUpFormValidator signUpFormValidator;
    // AccountService 메소드 추가
    private final AccountService accountService;

    // 기존 코드 ..........
    @PostMapping("/sign-up")
    public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors){
        if(errors.hasErrors()){
            return "account/sign-up";
        }

        accountService.processNewAccount(signUpForm);
        return "redirect:/";
    }

}

 

훨씬 깔끔한 코드가 되었다.

 

 

참고: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1#

 

스프링과 JPA 기반 웹 애플리케이션 개발 - 인프런

이 강좌에서 여러분은 실제로 운영 중인 서비스를 스프링, JPA 그리고 타임리프를 비롯한 여러 자바 기반의 여러 오픈 소스 기술을 사용하여 웹 애플리케이션을 개발하는 과정을 학습할 수 있습니다. 이 강좌를 충분히 학습한다면 여러분 만의 웹 서비스를 만들거나 취직에 도움이 될만한 포트폴리오를 만들 수 있을 겁니다. 활용 웹 개발 프레임워크 및 라이브러리 Java Spring Spring Boot Spring Data JPA Thymeleaf 온라인 강의 스

www.inflearn.com