티스토리 뷰
JsonInclude
@JsonInclude(JsonInclude.Include.NON_NULL) 어노테이션을 붙여주면 null 인 결과는 response 에 나타나지 않는다.
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonProperty("errors")
private List<CustomFieldError> customFieldErrors;
Exception Handler 구현
현재 참여하고 있는 프로젝트에서 일관성 있는 오류를 응답으로 주고 try-catch 문을 간결하게 / 혹은 생략하기 위해서 Exception Handler 를 구현하게 됐다.
< 기존에 Errorcode 는 존재하고 있는 상태였다 >
* 진행 상황
- InvalidParameter Error (구현 중)
- DataNotFoundException (구현 완료)
- user 관련 오류 처리 (계획 중)
ExceptionHandler 에는
package com.onjung.onjung.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.xml.crypto.Data;
import java.util.NoSuchElementException;
@ControllerAdvice
public class ControllerExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
protected ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
logger.error("handleHttpRequestMethodNotSupportedException", e);
final ErrorResponse response
= ErrorResponse
.create()
.status(HttpStatus.METHOD_NOT_ALLOWED.value())
.message(e.getMessage());
return new ResponseEntity<>(response, HttpStatus.METHOD_NOT_ALLOWED);
}
@ExceptionHandler(DataNotFoundException.class)
protected ResponseEntity<ErrorResponse> handleDataNotFoundException(DataNotFoundException e) {
logger.error("handleDataNotFoundException", e);
final ErrorResponse response
= ErrorResponse
.create()
.status(HttpStatus.NOT_FOUND.value())
.message(e.getMessage());
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
// 파라미터 유효성 검사 통과 못한 경우 처리하기
@ExceptionHandler({InvalidParameterException.class, MethodArgumentNotValidException.class})
protected ResponseEntity<ErrorResponse> handleInvalidParameterException(InvalidParameterException e) {
logger.error("handleInvalidParameterException", e);
ErrorCode errorCode = e.getErrorCode();
ErrorResponse response
= ErrorResponse
.create()
.status(errorCode.getStatus())
.message(e.toString());
// .errors(e.getErrors());
return new ResponseEntity<>(response, HttpStatus.resolve(errorCode.getStatus()));
}
// 나머지 오류 전부
@ExceptionHandler(Exception.class)
protected ResponseEntity<ErrorResponse> handleException(Exception e) {
logger.error("handleException", e);
ErrorResponse response
= ErrorResponse
.create()
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
.message(e.toString());
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
위와 같이 해줬다.
e.getErrors 에 주석처리를 해둔 이유는 응답에서 Error 부분이 너무 길어서 오히려 가독성을 해치는 느낌이 있어서 뺄까 고려 중이다.
ErrorResponse
package com.onjung.onjung.exception;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Getter
public class ErrorResponse {
private LocalDateTime timestamp = LocalDateTime.now(); // 오류 발생 시점
private String message; // 예외 메시지
private int status; // 상태코드
// 어떤 필드가 valid 를 통과하지 못했는지
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonProperty("errors")
private List<CustomFieldError> customFieldErrors;
public ErrorResponse(){
}
static public ErrorResponse create(){
return new ErrorResponse();
}
public ErrorResponse message(String message){
this.message = message;
return this;
}
public ErrorResponse status(int status){
this.status = status;
return this;
}
public ErrorResponse errors(Errors errors) {
setCustomFieldErrors(errors.getFieldErrors());
return this;
}
public void setCustomFieldErrors(List<FieldError> fieldErrors){
customFieldErrors = new ArrayList<>();
fieldErrors.forEach(error -> {
customFieldErrors.add(new CustomFieldError(
error.getCodes()[0],
error.getRejectedValue(),
error.getDefaultMessage()
));
});
}
@Getter
public static class CustomFieldError {
private String field;
private Object value;
private String reason;
public CustomFieldError(String field, Object value, String reason) {
this.field = field;
this.value = value;
this.reason = reason;
}
}
}
이전과의 차이?
위를 기반으로 컨트롤러들을 수정해줬다
그 중 하나를 예로 들어보자면, 기존의 코드는 아래와 같았다.
@GetMapping("{itemId}")
public ResponseEntity readItem(@PathVariable("itemId") Long itemId){
try{
Optional<Item> item = itemService.readItem(itemId);
return ResponseEntity.status(HttpStatus.OK).body(item);
}catch (Exception e)
{
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Exception raised in ItemController/readItem");
}
}
이의 경우에는, DataNotFoundException 인 경우에만 해당되는 응답이라고 볼 수 있다.
그러나 404가 아닌 여러 오류가 발생할 수 있고 body 에는 오직 문자열로 어디에서 오류가 발생했는지 담기기 때문에 코드가 길고 비효율적이라고 생각했다.
@GetMapping("{itemId}")
public ResponseEntity readItem(@PathVariable("itemId") Long itemId) {
try {
Optional<Item> item = itemService.readItem(itemId);
return ResponseEntity.status(HttpStatus.OK).body(item);
} catch (InterruptedException e) {
return ResponseEntity.status(HttpStatus.GATEWAY_TIMEOUT).body(e.getMessage());
}
}
그러나 위와 같이 수정하게 되면, InterruptedException 이 아닌 오류들은 전부 Exception Handler 에서 처리하게 되고,
DataNotFoundException 인지, 다른 예외처리인지 확인할 수 있다.
그러나 DataNotFoundException 과 같은 명시해서 좋을 예외들은 catch 문으로 작성해주고, throw 하는 것도 좋을 것 같다는 피드백이 있어서 이에 따라 추후에 수정해보고자 한다.
오류 잡기
중간에 계속 오류가 났었는데, 그 오류는 다음과 같았다.
ExceptionHandlerExceptionResolver : Failure in @ExceptionHandler ..
이와 관련해서 구글링을 해보니, ErrorResponse 에 Getter 가 있어야 한다고 해서 이를 추가해줬더니 해결됐다.
Getter 를 붙여줘야 하는 이유는, Json 구조로 객체 형태를 반환하기 위해서 Jackson 라이브러리는 ObjectMapping API 를 사용하는데 이는 Getter / Setter 를 기준으로 작동하기 때문에 Getter 가 없는 ErrorResponse 를 Json 구조로 반환할 수 없어서 발생하는 오류였다.
[SpringBoot] @ExceptionHandler 적용 시 HttpMediaTypeNotAcceptableException 에러 해결
에러를 처리하는 곳에서 에러가 났다...😱 (이것은.. 무한 에러 루프..?) Spring Boot에서 공통 익셉션을 처리하기 위해 @RestControllerAdvice, @ExceptionHandler를 사용하여 개발하고 있었다. 익셉션처리가 잘
yuja-kong.tistory.com