체크 예외와 언체크 예외(feat. Spring Framework)
체크 예외와 언체크 예외의 차이를 아는 것은 매우 중요하다.
스프링이 제공하는 선언적 트랜잭션(@Transaction)안에서 에러 발생 시
- 체크예외 : 롤백 X
- 언체크 예외 : 롤백 O
관련 기능 종류
Spring은 에러 처리라는 공통 관심사(cross-cutting concerns)를 메인 로직으로부터 분리하는 다양한 예외 처리 방식을 고안하였고, 예외 처리 전략을 추상화한 HandlerExceptionResolver 인터페이스를 만들었다(전략 패턴). 대부분의 HandlerExceptionResolver는 발생한 Exception을 catch하고 HTTP 상태나 응답메세지 등을 설정한다. 그래서 WAS 입장에서는 해당 요청이 정상적인 응답인 것으로 인식되며, 복잡한 WAS의 에러 전달이 진행되지 않는다.
Spring은 아래와 같은 도구들로 ExceptionResolver를 동작시켜 에러를 처리 지원
- @ResponseStatus
- @ResponseStatusException
- @ExceptionHandler
- @ControllerAdvice, @RestControllerAdvice
@ResponseStatus
📝 에러 HTTP 상태를 변경하도록 도와주는 어노테이션
적용 대상
- Exception 클래스 자체
- 메소드에 @ExceptionHanlder와 함께
- 클래스에 @RestControllerAdvice와 함께
사용법
직접 만든 예외 클래스에 @ResponseStatus로 응답 상태를 지정해 줄 수 있다.
@ResponseStatusException
외부 라이브러리에서 정의한 코드는 우리가 수정할 수 없으므로 @ResponseStatus를 붙여줄 수 없다. Spring5에는 @ResponseStatus의 프로그래밍적 대안으로써 손쉽게 에러를 반환할 수 있는 ResponseStatusException가 추가되었다. ResponseStatusException는 HttpStatus와 함께 선택적으로 reason과 cause를 추가할 수 있고, 언체크 예외를 상속받고 있어 명시적으로 에러를 처리해주지 않아도 된다.
@ExceptionHandler
💡 매우 유연하게 에러를 처리할 수 있는 방법을 제공하는 어노테이션
이 어노테이션을 메서드에 선언하고 특정 예외 클래스를 지정해주면 해당 예외가 발생했을 때 메서드에 정의한 로직으로 처리할 수 있다.
@ExceptionHandler(PostNotExistException.class)
적용대상
- 컨트롤러의 메소드
- @ControllerAdvice 나 @RestControllerAdvice가 적용된 클래스의 메소드
- @RestControllerAdvice public class MyExceptionHandler { @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler(PostNotExistException.class) protected ErrorResponse handlePostNotExistException(PostNotExistException ex) { return new ErrorResponse(ex.getErrorCode()); } }
원리
@ExceptionHandler는 Exception클래스들을 속성으로 받아 처리할 예외를 지정할 수 있다. 만약 ExceptionHandler 어노테이션에 예외 클래스를 지정하지 않는다면, 파라미터에 설정된 에러 클래스를 처리하게 된다.
또한 @ResponseStatus와도 결합가능한데, 만약 ResponseEntity에서도 HTTP상태를 지정하고 @ResponseStatus도 있으면 ResponseEntity가 우선순위를 갖는다.
장점
@ResponseStatus와 달리 에러 응답(payload)을 자유롭게 다룰 수 있다는 점에서 유연하다. 예를 들어 응답을 다음과 같이 정의해서 내려준다면 좋을 것이다.
📎 에러응답(payload)
- code : 어떠한 종류의 에러가 발생하는지에 대한 에러 코드
- E001, E002 등과 같이 내부적으로 정의한 값을 사용하는 것보다 BAD_REQUEST와 같은 Http 표준 상태와 같이 가독성이 좋은 값을 사용하는 것이 클라이언트 입장에서도 대응하기 좋고, 유지보수하는 입장에서도 좋다.
- message : 왜 에러가 발생했는지에 대한 설명
- erros : 어느 값이 잘못되어 @Valid에 의한 검증이 실패한 것인지를 위한 에러 목록
주의점
@ExceptionHandler에 등록된 예외 클래스와 파라미터로 받는 예외 클래스가 동일해아함
한계점(?)
@ExceptionHandler는 컨트롤러에 구현하므로 특정 컨트롤러에서만 발생하는 예외만 처리된다. 하지만 컨트롤러에 에러 처리 코드가 섞이며, 에러 처리 코드가 중복될 가능성이 높다.
@ControllerAdvice와 @RestControllerAdvice
try-catch 를 이용해 예외처리를 하면코드 라인이 길어지고 가독성도 떨어짐
@ControllerAdvice와 @RestControllerAdvice 비교
💡 @ControllerAdvice
@ExceptionHandler, @ModelAttribute, @InitBinder가 적용된 메소드들에 AOP를 적용해 Controller 단에 적용하기 위해 고안된 어노테이션
@RestControllerAdvice
@ControllerAdvice + @ResponseBody(응답을 Json으로 내려준다는 점)
적용대상
- 클래스
기능
- 여러 컨트롤러에 대해 전역적으로 ExceptionHandler를 적용
- @Controller에 대한, 전역적으로 발생할 수 있는 예외를 잡아서 처리할 수 있음
ControllerAdvice 어노테이션에는 @Component 어노테이션이 있어서 ControllerAdvice가 선언된 클래스는 스프링 빈으로 등록된다. 그러므로 우리는 다음과 같이 전역적으로 에러를 핸들링하는 클래스를 만들어 어노테이션을 붙여줌으로써 에러 처리를 위임할 수 있다.
Spring은 스프링 예외를 미리 처리해둔 ResponseEntityExceptionHandler를 추상 클래스로 제공하고 있다. ResponseEntityExceptionHandler에는 스프링 예외에 대한 ExceptionHandler가 모두 구현되어 있으므로 ControllerAdvice 클래스가 이를 상속 받게 하면 된다.
장점
- 하나의 클래스로 모든 컨트롤러에 대해 전역적으로 예외 처리가 가능함
- 직접 정의한 에러 응답을 일관성있게 클라이언트에게 내려줄 수 있음
- 별도의 try-catch문이 없어 코드의 가독성이 높아짐
주의점
여러 ControllerAdvice가 있을 때 @Order 어노테이션으로 순서를 지정하지 않는다면 Spring은 ControllerAdvice를 임의의 순서로 처리할 수 있으므로 일관된 예외 응답을 위해서는 이러한 점을주의해아함
- 한 프로젝트 당 하나의 ControllerAdvice만 관리하는 것이 좋다.
- 만약 여러 ControllerAdvice가 필요하다면 basePackages나 annotations 등을 지정해야 한다.
- 직접 구현한 Exception 클래스들은 한 공간에서 관리한다.
@RestControllerAdvice를 이용한 Spring 예외 처리 방법
[에러 코드 정의하기]
- 클라이언트에게 보내줄 에러 코드를 정의
- 발생할 예외를 처리해줄 언체크 예외(런타임 예외)를 상속받는 예외 클래스(Exception Class)를 추가해주어야 한다.
[에러 응답 클래스 생성하기]
[@RestControllerAdvice 구현]
이제 전역적으로 에러를 처리해주는 @RestControllerAdvice 클래스를 추가해주어야 한다. Spring은 스프링 예외를 미리 처리해둔 ResponseEntityExceptionHandler를 추상 클래스로 제공하고 있다. ResponseEntityExceptionHandler에는 스프링 예외에 대한 ExceptionHandler가 모두 구현 되어 있으므로 ControllerAdvice 클래스가 이를 상속받게 하면 된다. 하지만 에러 메세지는 반환하지 않으므로 스프링 예외에 대한 에러 응답을 보내려면아래 메소드를 오버라이딩 해야 한다.
'Spring 단기심화 2기' 카테고리의 다른 글
TIL_JWT_241121 (1) | 2024.11.21 |
---|---|
TIL_람다와스트림_241119 (0) | 2024.11.19 |
TIL_MSA 들어가기_241114 (0) | 2024.11.14 |
TIL _ 브랜지 Merge 전략 _ 241112(화) (0) | 2024.11.12 |
TIL _ Entity의 연관관계 _ 24/11/11 (0) | 2024.11.11 |