리팩토링 하기 전에 테스트 코드를 먼저 작성하자.
- 그래야 코드를 변경한 이후에 불안하지 않다.
- 변경한 코드가 무언가를 깨트리지 않았다는 것을 확인할 수 있다.
테스트할 것
- 폼에 이상한 값이 들어간 경우에 다시 폼이 보이는가?
- 폼에 값이 정상적인 경우
- 가입한 회원 데이터가 존재하는가?
- 이메일이 보내지는가?
리팩토링
- 메소드가 너무 길지 않은가?
- 코드를 읽기 쉬운가?
- 내가 작성한 코드를 내가 읽기 어렵다면 남들에겐 훨씬 더 어렵다.
- 코드가 적절한 위치에 있는가?
- 객체들 사이의 의존관계
- 책임이 너무 많진 않은지
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 기반 웹 어플리케이션 개발 > 1부 (개발환경, 회원가입, 로그인, 계정설정)' 카테고리의 다른 글
08. 회원 가입: 인증 메일 확인 (0) | 2020.04.18 |
---|---|
07. 회원 가입: 패스워드 인코더 (0) | 2020.04.18 |
05. 회원 가입: 폼 서브밋 (0) | 2020.04.17 |
04. 회원가입 뷰 (0) | 2020.04.17 |
03. 회원 가입: 컨트롤러 (0) | 2020.04.17 |