
@ControllerAdvice / @RestControllerAdvice
:Spring MVC 컨트롤러에서 발생하는 예외를 전역적으로 처리하기 위한 클래스에 지정한다.
이 어노테이션이 지정된 클래스는 모든 컨트롤러에서 발생하는 예외를 잡아, 처리할 수 있다.
@ExceptionHandler
: 특정 예외 유형에 대한 처리를 지정된 메서드에 할당한다.
이 어노테이션이 지정된 메서드는 해당 예외가 발생했을 때 호출되어, 적절히 처리하고 클라이언트에 응답을 반환한다.
일반적으로 HTTP 응답 상태 코드만으로는 클라이언트가 오류의 세부 사항을 알기는 어렵다.
GlobalException 클래스는 예외 발생 시, 해당 예외에 대한 커스텀 오류 코드를 생성하고, 이를 HTTP 응답에 포함시킨다.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(SpecificException.class)
public ModelAndView handleSpecificException(SpecificException ex) {
ModelAndView mav = new ModelAndView();
mav.addObject("errorMessage", ex.getMessage());
mav.setViewName("errorPage");
return mav;
}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(SpecificException.class)
public ResponseEntity<ApiError> handleSpecificException(SpecificException ex) {
ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getMessage(), "에러 상세 설명");
return new ResponseEntity<>(apiError, apiError.getStatus());
}
// IllegalArgumentException 예외 처리
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST) // HTTP 400 상태 반환
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(ex.getMessage());
}
}
@ExceptionHandler(IllegalArgumentException.class)
: IllegalArgumentException이 발생했을 때 호출되는 메서드. 예외를 처리하고 지정된 응답을 반환한다.
@ResponseStatus(HttpStatus.BAD_REQUEST)
: 예외 처리 후 반환할 HTTP 상태 코드를 지정한다. 여기서는 400 Bad Request 반환
HTTP 응답 생성
: ResponseEntity 객체를 사용해, 예외 메시지를 포함한 HTTP 응답을 생성하고, 상태 코드를 400 Bad Request로 설정하여 반환한다.
: 여러 개의 커스텀 예외 클래스가 사용될 때,
이러한 예외들은 특정 상황이나 오류를 명확하게 표현하고 처리하기 위해 정의된다.
각 예외 클래스는 특정 조건에서 발생하며, 코드의 가독성과 유지보수성을 높이는 데 도움을 준다.
코드 가독성 향상
throw new BookNotFoundException("해당 책을 찾을 수 없습니다.); 와 같이 구체적인 예외를 던지면, 코드 리뷰나 유지보수 시에도 쉽게 이해할 수 있다.예외 처리의 일관성 유지
public class TransactionRollbackException extends RuntimeException {
public TransactionRollbackException(String message) {
super(message);
}
}
public class BookNotFoundException extends IllegalArgumentException {
public BookNotFoundException(String message) {
super(message);
}
}
public class BookDeletionException extends IllegalArgumentException {
public BookDeletionException(String message) {
super(message);
}
}
@ControllerAdvice
public class GlobalExceptionHandler {
// TransactionRollbackException 처리
@ExceptionHandler(TransactionRollbackException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // HTTP 500 상태를 반환
public ResponseEntity<String> handleTransactionRollbackException(TransactionRollbackException ex)
{
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ex.getMessage());
}
// BookDeletionException 처리
@ExceptionHandler(BookDeletionException.class)
@ResponseStatus(HttpStatus.FORBIDDEN) // HTTP 403 상태를 반환
public ResponseEntity<String> handleBookDeletionException(BookDeletionException ex)
{
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body(ex.getMessage());
}
// BookNotFoundException 처리
@ExceptionHandler(BookNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND) // HTTP 404 상태를 반환
public ResponseEntity<String> handleBookNotFoundException(BookNotFoundException ex)
{
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(ex.getMessage());
}
// IllegalArgumentException 예외 처리
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST) // HTTP 400 상태 반환
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex)
{
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(ex.getMessage());
}
}
@Service
@RequiredArgsConstructor
@Transactional
public class BookService {
...
public void updateBookPublisher(Book book, String publisher) {
book.setPublisher(publisher);
// 예외 발생
if(true) {
throw new TransactionRollbackException("트랜잭션이 롤백되지 않습니다.");
}
saveBook(book);
}
...
public void deleteBook(Long id) {
Book book = findVerifiedBook(id);
if(book.getStatus() == Book.Status.BORROWED) {
throw new BookDeletionException("대출 중인 책은 삭제할 수 없습니다.");
}
bookRepository.delete(book);
}
...
public Book findVerifiedBook(Long id) {
return bookRepository.findById(id)
.orElseThrow(() -> new BookNotFoundException("존재하지 않는 책입니다."));
}
}
RuntimeException("트랜잭션이 롤백되지 않습니다.") → TransactionRollbackException("트랜잭션이 롤백되지 않습니다.")
IllegalArgumentException(대출 중인 책은 삭제할 수 없습니다."") → BookDeletionException("대출 중인 책은 삭제할 수 없습니다.")
IllegalArgumentException("존재하지 않는 책입니다.") → BookNotFoundException("존재하지 않는 책입니다.")
💡참고할 만한 강의들