이번 글에서는 @DateTimeFormat
애노테이션을 통해서 날짜 값 변환
하는 것과 @ControllerAdvice
, @ExceptionHandler
를 통해서 에러 핸들링을 하는 방법에 대해서 알아보겠습니다.
최근에 프로젝트를 하면서 날짜
관련 타입으로 클라이언트로부터 받을 상황이 있었는데요. LocalDate
타입으로 쓸꺼면, 두 번째 메소드 처럼 LocalDate
로만 받으면 되겠다! 라고 생각을 했습니다.
DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.time.LocalDate';
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type
java.time.LocalDate] for value '2021-10-13'; nested exception is java.lang.IllegalArgumentException:
그래서 요청을 보내보니, 위의 에러가 발생합니다. 요약하면 String -> LocalDate
타입 변환을 할 수 없어서 에러가 발생했다는 것입니다. 스프링은 Long이나 int와 같은 기본 데이터 타입으로의 변환은 기본적으로 처리해주지만 LocalDateTime or LocalDate와 같은 타입으로의 변환은 추가 설정이 필요합니다.
이러한 상황일 때 @DateTimeFormat
애노테이션을 사용하면 @DateTimeFormat
에서 지정한 형식을 이용해서 String -> LocalDate
타입으로 변환해줍니다. 아주 쉽쥬~?
@DateTimeFormat
애노테이션을 사용하면 지정한 형식의 문자열을 LocalDate or LocalDateTime
으로 변환해줄 수 있다고 위에서 정리했는데요. 그러면 누가 String -> LocalDate
타입으로 변환을 해주는 것일까요?
public class WebDataBinder extends DataBinder {}
바로 WebDataBinder
가 그 역할을 하고 있습니다.
Spring MVC 디버깅 을 하는 것처럼 내부 과정을 따라가다 보니 위와 같이 WebDataBinder
를 create
를 하는 구간이 존재하는 것을 볼 수 있습니다.
그리고 convertIfNeccesary
메소드는 이름에서 알 수 있듯이 필요하다면 convert
를 하겠다라는 메소드인데요. 보면 arg
에 제가 PathVariable
을 통해서 넘긴 2021-11-01
이 존재하는 것도 볼 수 있습니다.
이러한 과정 속에서 자세히 보면 WebDataBinder
는 직접 변환 하지 않고 ConversionService
에 그 역할을 위임합니다. (디버깅을 해보면 매우 복잡하다..)
@ControllerAdvice
를 사용하면 Spring 영역에서 발생하는 Exception들을 핸들링 할 수 있습니다.
제가 자주 보았던 Spring Guide - Exception 전략 에서도 어떻게 Exception 전략을 가져가면 좋은지에 대해서 설명하고 있습니다.
저 또한 RequestBody DTO 필드를 Valid 유효성 검사하기 에서 간단하게 @ControllerAdvice
와 ExceptionHandler
를 통해서 예외 처리를 하는 것에 대해서 정리한 적이 있습니다.
이번 글에서도 비슷하게 위에서 살펴본 @DateTimeFormat
애노테이션이 없을 때 일어나는 Exception을 핸들링 하는 법에 대해서 정리해보겠습니다.
위와 같이 @ControllerAdvice
애노테이션을 사용하면 스프링 영역에서 발생하는 에러를 잡아 핸들링 할 수 있게 해줍니다. 예를들어, @DateTimeFormat
이 없을 때 String -> LocalDate
로 변환이 안되어 ConverterError
가 발생하는데 이 때 MethodArgumentTypeMismatchException
이 발생합니다.
즉, @ExceptionHandler
를 통해서 MethodArgumentTypeMismatchException
를 위와 같이 매칭시켜서 핸들링 시켜주면, MethodArgumentTypeMismatchException
에러가 발생했을 때 @ExceptionHandler
에 정의한 함수가 호출되어 예외처리를 할 수 있습니다.
- 같은 컨트롤러에 위치한
@ExceptionHandler
메소드 중 해당 Exception을 처리할 수 있는 메소드를 찾습니다. - 같은 클래스에 위치한 메소드가 Exception을 처리할 수 없을 경우
@ControllerAdvice
클래스에 위치한@ExceptionHandler
메소드를 검색합니다.
@ControllerAdvice
애노테이션을 보면 위와 크게 3개의 속성이 존재하는데요. 정리하면 아래와 같습니다.
속성 | 타입 | 설명 |
---|---|---|
value basePackages |
String[] | 공통 설정을 적용할 컨트롤러가 속하는 기준 패키지 |
annotaions | Class<? extends Annotations>[] | 특정 애노테이션이 적용된 컨트롤러 대상 |
assignableTypes | Class<?>[] | 특정 타입 또는 그 하위 타입인 컨트롤러 대상 |