자바 같은경우 checkedException된 예외를 try & catch로 잡아서 특정한 결과값으로 만든 후 모든 exception이 없도록 코드를 많이 짰었다.
ExceptionHandler
@ResponseStatus(value = HttpStatus.CONFLICT)
@ExceptionHandler(DMakerException.class)
public DMakerErrorResponse handlerException(DMakerException e, HttpServletRequest request) {
log.error("errorCode: {}, url: {}, message: {}", e.getDMakerErrorCode(),request.getRequestURI(),e.getMessage());
return DMakerErrorResponse.builder()
.errorCode(e.getDMakerErrorCode())
.errorMessage(e.getDetailMessage())
.build();
}
DMakerException이 발생하는경우 이 메소드안으로 진입
로그를이용해 errorCode와 에러발생한 url,메세지를 찍는 핸들러를 만들어 줬다
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class DMakerErrorResponse {
private DMakerErrorCode errorCode;
private String errorMessage;
}
동일한 개발자를 추가했을때 설정한 로그와 DMakerErrorResponse가 제대로 세팅 되는지 테스트
라는 조건을 두번 생성하였다.
2022-02-11 17:19:52.370 ERROR 3860 --- [nio-8080-exec-3] c.d.d.D.controller.DMakerController : errorCode: DUPLICATED_MEMBER_ID, url: /create-developer, message: MemberId가 중복되는 개발자가 있습니다.
서버상의 로그도 보여주고 사용자에게도 정돈된 메세지를 보여줄수 있는것을 확인할 수 있다.
이런 식으로 요즘은 정확한 에러코드와 메세지를 내려주는 식으로 개발하는게 추세라고 하셨다.
내부적인 로직에서 문제가 발생했을 때 에러 결과를 넘기고 넘기는 식(계속보내는식)은 로직 복잡도를 높이고 유지보수성 낮고 재활용성이 떨어지는 코드를 만들기때문에 피하는게 좋다.
exception시 각 코드마다 Exception을 처리하기위해 try catch를 사용하는게 아닌 ExceptionHandler를 통해서 처리 할 수 있다 라는 것을 배울 수 있었다.
조금더 글로벌한(예를 들어 Controller를 하나더 추가한 상황)
또 ExceptionHandler가 필요하다. application이 더 커지고 그러면 Controller가 10~20이상 생길수 있는데 ExceptionHandler를 Controller마다 추가를 하면 반복적이고 불필요한 코드가 될 수 있다.
요즘에 많이 하는 방법으로 Exception을 각 컨트롤러에서 처리하는 게 아니라 GlobalException을 만드는 것이다.
@RestControllerAdvice :
각 컨트롤러에다가 조언을 해주는 애노테이션이다.
전역적인 ExceptionHandler를 만들 수 있다.
일일히 들어가있던 ExceptionHandler들을 전부 지금 생성한 GlobalExceptionHandler에 추가함
@Slf4j
@RestControllerAdvice
public class DMakerExceptionHandler {
@ResponseStatus(value = HttpStatus.CONFLICT)
@ExceptionHandler(DMakerException.class)
public DMakerErrorResponse handlerException(DMakerException e, HttpServletRequest request) {
log.error("errorCode: {}, url: {}, message: {}", e.getDMakerErrorCode(),request.getRequestURI(),e.getMessage());
return DMakerErrorResponse.builder()
.errorCode(e.getDMakerErrorCode())
.errorMessage(e.getDetailMessage())
.build();
}
@ExceptionHandler(value = {
HttpRequestMethodNotSupportedException.class,
MethodArgumentNotValidException.class
})
public DMakerErrorResponse handleBadRequest(
Exception e, HttpServletRequest request
){
log.error(" url: {}, message: {}", request.getRequestURI(),e.getMessage());
return DMakerErrorResponse.builder()
.errorCode(INVALID_REQUEST)
.errorMessage(INVALID_REQUEST.getMessage())
.build();
}
//- 이 Exception은 뜨면 안좋은거다.. 최후의 보류
@ExceptionHandler(value = {
Exception.class
})
public DMakerErrorResponse handleException(
Exception e, HttpServletRequest request
){
log.error(" url: {}, message: {}", request.getRequestURI(),e.getMessage());
return DMakerErrorResponse.builder()
.errorCode(INTERNAL_SERVER_ERROR)
.errorMessage(INTERNAL_SERVER_ERROR.getMessage())
.build();
}
}
2022-02-11 20:16:17.499 ERROR 9624 --- [nio-8080-exec-1] c.d.d.D.e.DMakerExceptionHandler : url: /create-developer, message: Request method 'PUT' not supported
결과:
서버에서의 결과
2022-02-11 20:18:18.989 ERROR 9624 --- [nio-8080-exec-6] c.d.d.D.e.DMakerExceptionHandler : url: /create-developer, message: Validation failed for argument [0] in public com.developer.dmaker.Dmaker.dto.CreateDeveloperRequest): [Field error in object 'request' on field 'developerSkillType': rejected value [null]; codes [NotNull.request.developerSkillType,NotNull.developerSkillType,NotNull.com.developer.dmaker.Dmaker.type.DeveloperSkillType,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [request.developerSkillType,developerSkillType]; arguments []; default message [developerSkillType]]; default message [널이어서는 안됩니다]]