🔍 문제 상황
컨트롤러 테스트를 하던 중 RestControllerAdvice를 이용하여 커스텀 예외 처리가 되어야 하는 부분에서
UnverifiedException이 아닌 NestedServletException이 발생하는 문제가 발생하였다. 예외 처리를 하고 있는 Service 코드는 다음과 같다.
@Override
public User login(UserDTO.loginForm form) {
User user = userRepository.findByEmail(form.getEmail()).orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_USER));
if(!user.getPassword().equals(form.getPassword())) {
throw new InvalidPasswordException(ErrorCode.INVALID_PASSWORD);
}
if(!user.isVerified()) {
throw new UnverifiedException(ErrorCode.UNVERIFIED_USER);
}
return user;
}
예외 처리는 @RestControllerAdvice와 @ExceptionHandler를 이용해서 예외가 터지면 전역적으로 처리할 수 있도록 코드를 작성한 상태였다.
@Slf4j
@RestControllerAdvice
public class GeneralExceptionHandler { }
문제 원인
NestedServletException은 서블릿에서 모든 예외를 처리할 수 있도록 제공하고 있는 클래스이다. 커스텀 예외를 처리하기 위해 InvalidVerificationCodeException이라는 클래스를 만들었는데, 이를 위한 핸들러 추가를 하지 않아 발생한 문제였다.
문제 해결
다음과 같이 핸들러를 restControolerAdvice 클래스에 추가함으로써 해결할 수 있었다.
@Slf4j
@RestControllerAdvice
public class GeneralExceptionHandler {
/**
* 이메일 인증 코드가 부적절한 경우
*/
@ExceptionHandler(value = { InvalidVerificationCodeException.class })
protected ResponseEntity<ApiResponse> handleInvalidVerificationCodeException(InvalidVerificationCodeException e) {
log.error("InvalidPasswordException", e);
ApiResponse errorResponse = ApiResponse.of(HttpStatus.BAD_REQUEST.toString(), e.getMessage());
return ResponseEntity.status(e.getErrorCode().getHttpStatus())
.body(errorResponse);
}
}
NestedSservletException의 예외 해결 추가 방법
- try-catch를 이용한다.
try {
mockMvc.perform()
...
} catch (NestedServletException e) {
throw e.getCause();
}
- org.assertj.core.api.Assertions.assertThatThrownBy를 사용한다.
assertThatThrownBy(() -> mockMvc.perform(post("/login")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print()))
.hasCause(new UnverifiedException(ErrorCode.UNVERIFIED_USER));