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 |
|---|