spring 예외 처리 @RestControllerAdvice, @ExceptionHandler

2023. 1. 6. 14:52시행착오

개발을 할 때 예외 처리는 상당히 중요하다고 생각한다. 고객들이 해당 제품을 사용시 예외 처리가 제대로 안되서 사용하는데 불편함을 느낀다면 해당제품을 사용하지 않는 불상사가 발생할 수 있습니다.

예외 처리를 할 때 try catch문을 활용해 잡는 방법이 있지만 try catch를 사용함으로써 복잡하고 가독성이 떨어지게 되는 문제점이 있습니다.

spring에 다양한 예외 처리 방법이 있는데 그 중 @ControllerAdvice와 @ExceptionHandler를 사용하는 방법에 대해서 정리를 해보겠습니다.

 

@ControllerAdvice 와 @RestControllerAdvice의 차이

 

spring을 공부 해본 분이라면 @Controller와 @RestController에 대해 학습한적이 있으실 겁니다. 이와 비슷하게 @RestControllerAdvice = @ControllerAdvice + @ResponseBody로 응답을 json으로 내려준다는 특징이 있습니다.

 

@ControllerAdvice는 @ExceptionHandler 처럼 AOP를 적용하기 위해 만들었습니다. @Component를 포함하기 때문에 bean으로 관리가 됩니다. 그리고 컨트롤러 간의 공통된 로직 구현을 하기 위해 사용됩니다.

 

코드를 통해 확인해 보겠습니다

 

ItemsService

@Transactional(readOnly = true)
public ItemsReadUpdateResponseDto findById(Long id) {
    Items items = itemsRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("해당하는 id가 없습니다."));

    return new ItemsReadUpdateResponseDto(items);
}

db에 없는 id의 상품을 조회하려면 에러가 발생하게 됩니다.

 

 

@ControllerAdvice와 @ExceptionHandler를 활용해 예외 처리를 해보겠습니다.

 

ErrorCode라는 enum 클래스를 만들어 줍니다.

@Getter
@RequiredArgsConstructor
public enum ErrorCode {

    BAD_REQUEST_ITEMS_READ(400, "id 입력 오류", "해당상품을 조회할 수 없습니다."),
    BAD_REQUEST_ITEMS_UPDATE(400, "id 입력 오류", "해당상품을 수정할 수 없습니다."),
    BAD_REQUEST_ITEMS_DELETE(400, "id 입력 오류", "해당상품을 삭제할 수 없습니다."),

    ITEMS_NOT_FOUND(404, "NOT_FOUND" ,"해당 상품의 정보를 찾을 수 없습니다.");

    private final int status;
    private final String errorCode;
    private final String message;

}

@RequiredArgsConstructor

final이나 @NotNull이 붙은 생성자를 자동으로 생성해준다.

 

그리고 나서 예외 상위 클래스를 상속받습니다.

@Getter
public class ItemsFindIdException extends RuntimeException{

    private ErrorCode errorCode;

    public ItemsFindIdException() {
        this.errorCode = ErrorCode.BAD_REQUEST_ITEMS_READ;
    }
}
@Getter
public class ItemsUpdateIdException extends RuntimeException{

    private ErrorCode errorCode;

    public ItemsUpdateIdException() {
        this.errorCode = ErrorCode.BAD_REQUEST_ITEMS_UPDATE;
    }

}
@Getter
public class ItemsDeleteIdException extends RuntimeException{

    private ErrorCode errorCode;

    public ItemsDeleteIdException() {
        this.errorCode = ErrorCode.BAD_REQUEST_ITEMS_DELETE;
    }

}

custom exception 클래스의 이름은 예외가 무엇인지 바로 알 수 있도록 지어주는 것이 좋다. 또한 해당 클래스는 해당 예외에 대해 책임을 지게하도록 하는 것을 추천드립니다.

 

그리고 나서 예외를 응답해줄 ErrorResponse를 생성해줍니다.

 

@Getter
@Setter
public class ErrorResponse {

    private int status;
    private String message;
    private String code;

    public ErrorResponse(ErrorCode errorCode) {
        this.status = errorCode.getStatus();
        this.message = errorCode.getMessage();
        this.code = errorCode.getErrorCode();
    }

}

그리고 난 후 @ControllerAdvice와 @ExceptionHandler를 활용하기 위한 GlobalExceptionHanler를 만들어 줍니다.

 

@ExceptionHandler는 @ControllerAdvice를 사용한 클래스 안에서만 사용이 가능합니다.

 

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ItemsFindIdException.class)
    public ResponseEntity<ErrorResponse> handleItemsException(ItemsFindIdException ex) {
        ErrorResponse response = new ErrorResponse(ex.getErrorCode());
        return new ResponseEntity<>(response, HttpStatus.valueOf(ex.getErrorCode().getStatus()));
    }

    @ExceptionHandler(ItemsUpdateIdException.class)
    public ResponseEntity<ErrorResponse> handleItemsException(ItemsUpdateIdException ex) {
        ErrorResponse response = new ErrorResponse(ex.getErrorCode());
        return new ResponseEntity<>(response, HttpStatus.valueOf(ex.getErrorCode().getStatus()));
    }

    @ExceptionHandler(ItemsDeleteIdException.class)
    public ResponseEntity<ErrorResponse> handleItemsException(ItemsDeleteIdException ex) {
        ErrorResponse response = new ErrorResponse(ex.getErrorCode());
        return new ResponseEntity<>(response, HttpStatus.valueOf(ex.getErrorCode().getStatus()));
    }

}

 

json 형식으로 사용자에게 알아보기 쉽게 에러 메시지를 보여줍니다. 이를 통해 사용자가 올바르게 접근하도록 유도를 할 수 있습니다.

 

ResponseEntity

httpRequest에 대한 응답 데이터를 포함하는 클래스이다

 

 

'시행착오' 카테고리의 다른 글

aws springboot 빌드시 8080포트 오류  (0) 2023.05.16