🌐 github 주소
https://github.com/JoeMinKyung/spring-advanced
if 문
을 passwordEncoder.encode()
호출보다 위로 이동하여, 불필요한 인코딩이 발생하지 않도록 리팩토링 했다. 이제 이메일 중복 검사를 먼저 수행한 후, 패스워드를 인코딩하도록 수정되었다.
if (userRepository.existsByEmail(signupRequest.getEmail())) {
throw new InvalidRequestException("이미 존재하는 이메일입니다.");
}
@Transactional
public SignupResponse signup(SignupRequest signupRequest) {
if (userRepository.existsByEmail(signupRequest.getEmail())) {
throw new InvalidRequestException("이미 존재하는 이메일입니다.");
}
String encodedPassword = passwordEncoder.encode(signupRequest.getPassword());
UserRole userRole = UserRole.of(signupRequest.getUserRole());
User newUser = new User(
signupRequest.getEmail(),
encodedPassword,
userRole
);
User savedUser = userRepository.save(newUser);
String bearerToken = jwtUtil.createToken(savedUser.getId(), savedUser.getEmail(), userRole);
return new SignupResponse(bearerToken);
}
불필요한 else
블록을 제거하고 if 문
을 사용하여 각 조건이 독립적으로 처리된다. 또한 불필요한 중첩이 사라져 코드가 더 깔끔해졌다.
WeatherDto[] weatherArray = responseEntity.getBody();
if (!HttpStatus.OK.equals(responseEntity.getStatusCode())) {
throw new ServerException("날씨 데이터를 가져오는데 실패했습니다. 상태 코드: " + responseEntity.getStatusCode());
} else {
if (weatherArray == null || weatherArray.length == 0) {
throw new ServerException("날씨 데이터가 없습니다.");
}
}
if (!HttpStatus.OK.equals(responseEntity.getStatusCode())) {
throw new ServerException("날씨 데이터를 가져오는데 실패했습니다. 상태 코드: " + responseEntity.getStatusCode());
}
if (weatherArray == null || weatherArray.length == 0) {
throw new ServerException("날씨 데이터가 없습니다.");
}
changePassword()
메서드에서 비밀번호 검증 로직을 제거하고 대신 UserChangePasswordRequestDto
에서 spring-boot-starter-validation
라이브러리를 활용하여 유효성 검사를 수행하도록 개선하였다.
if (userChangePasswordRequest.getNewPassword().length() < 8 ||
!userChangePasswordRequest.getNewPassword().matches(".*\\d.*") ||
!userChangePasswordRequest.getNewPassword().matches(".*[A-Z].*")) {
throw new InvalidRequestException("새 비밀번호는 8자 이상이어야 하고, 숫자와 대문자를 포함해야 합니다.");
}
@Getter
public class UserChangePasswordRequestDto {
@NotBlank(message = "기존 비밀번호를 입력해야 합니다.")
private String oldPassword;
@NotBlank(message = "새 비밀번호를 입력해야 합니다.")
@Size(min = 8, message = "새 비밀번호는 최소 8자 이상이어야 합니다.")
@Pattern(regexp = ".*\\d.*", message = "새 비밀번호에는 적어도 하나의 숫자가 포함되어야 합니다.")
@Pattern(regexp = ".*[A-Z].*", message = "새 비밀번호에는 적어도 하나의 대문자가 포함되어야 합니다.")
private String newPassword;
}
userChangePasswordRequestDto
에서 javax.validation
의 어노테이션을 활용하여 유효성 검사를 수행했다. 이 Dto에서 검증을 담당하므로 changePassword()
메서드에서 관련 검증 로직을 제거했다.
@PostMapping("/change-password")
public ResponseEntity<Void> changePassword(@RequestParam long userId,
@Valid @RequestBody UserChangePasswordRequest request) {
userService.changePassword(userId, request);
return ResponseEntity.ok().build();
}
UserChangePasswordRequest
의 유효성 검사의 작동을 위해 컨트롤러에서도 @Valid
를 적용했다.
@EntityGraph
를 사용하여 fetch join
과 동일한 효과를 내도록 TodoRepository
를 수정하였다.
@EntityGraph(attributePaths = "user")
TodoRepository
의 findAllByOrderByModifiedAtDesc
메서드에서 @EntityGraph
를 사용하여 User
엔티티를 함께 가져오도록 설정했다. 즉 Todo
엔티티 조회 시 user
정보를 함께 가져오며, 기존 fetch join
과 동일한 효과를 내며, N+1 문제를 방지할 수 있다.
테스트 코드의 경우 계속 실행이 안돼서 결과를 확인하지 못했다.. 오늘 해설 세션을 들으며 어디가 잘못됐는지 확인할 예정이다.
private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
테스트 코드에서 PasswordEncoder
가 올바르게 설정되지 않아 BCryptPasswordEncoder
를 직접 생성하여 PasswordEncoder
를 주입받지 않고도 테스트가 정상적으로 동작하도록 수정하였다.
@Test
public void manager_목록_조회_시_Todo가_없다면_InvalidRequestException을_던진다() {
// given
long todoId = 1L;
given(todoRepository.findById(todoId)).willReturn(Optional.empty());
// when & then
assertThrows(InvalidRequestException.class,
() -> managerService.getManagers(todoId),
"Manager not found"
);
}
테스트 메서드 명을 변경하였고, assertThrows
문을 수정하였다.
InvalidRequestException exception = assertThrows(
InvalidRequestException.class,
() -> commentService.saveComment(authUser, todoId, request)
);
ServerException
대신 InvalidRequestException
으로 수정하였다.
if (todo.getUser() == null) {
throw new InvalidRequestException("todo의 user가 null입니다.");
}
ManagerService
의 saveManager
에서 todo.getUser()
가 null
인지 검사하는 로직을 추가하였다. null
이라면 InvalidRequestException
을 던진다.