🔍예외의 종류
예외처리를 알아보기 전에 예외의 종류부터 살펴보자.
Unchecked Exception (RunTimeException)
- Exception Class의 서브 클래스
- RuntimeException Class를 상속
- Transaction으로 Rollback이 진행 됨
반드시 예외를 체크하지 않아도 되는 경우 사용합니다.
이처럼 명시적인 예외처리를 강제하지 않기 때문에 언체크 예외라고 부릅니다.
언체크 예외는 런타임 예외라고도 부릅니다.
체크 예외와는 달리 따로 처리하지 않아도 컴파일 에러가 발생하지 않습니다.
예외를 피할 수 있지만 개발자가 부주의해서 발생할 수 있는 경우를 위해 만든 예외입니다.
즉, 주로 프로그램 자체의 오류가 있을 때 발생하도록 의도됩니다.
대표적으로 NullPointerException, IllegalArgumentException 등이 포함됩니다.
CheckedException
- Exception Class의 서브 클래스
RuntimeException
Class를 상속하지 않음- Transaction으로 Rollback이 진행 안됨 (
RollBackFor
을 사용하면 가능)
반드시 예외를 체크해야하는 경우 사용합니다.
프로그램 상으로 의도한 예외가 아니기 때문에 반드시 예외를 처리하는 코드를 함께 작성해야 합니다.
예외 처리 코드가 없다면 컴파일 에러가 발생합니다.
catch 문으로 잡거나 throws를 통해 메소드 밖으로 던질 수 있습니다.
IOException
이나 SQLException
등과 같은 예외가 있습니다.
🔍 @RestControllerAdvice
+ @ExceptionHandler
@ControllerAdvice, @ExceptionHandler를 이용한 예외처리 분리, 통합하기(Spring에서 예외 관리하는 방법, 실무에서는 어떻게?)
@ControllerAdvice
는 프로젝트 전역에서 발생하는 모든 예외를 잡아줍니다.
@ExceptionHandler
는 발생한 특정 예외를 잡아서 하나의 메소드에서 공통 처리해줄 수 있게 해줍니다.
⇒ 모든 예외를 잡은 후에, Exception 종류별로 메소드를 공통 처리할 수 있습니다.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
public String custom() {
return "hello custom";
}
}
위와 같이 새로운 클래스파일을 만들어서 annotation을 붙이기만 하면 된다. 그 다음에 @ExceptionHandler
로 처리하고 싶은 예외를 잡아 처리하면 된다.
별도의 속성값이 없이 사용하면 모든 패키지 전역에 있는 컨트롤러를 담당하게 된다.
@RestControllerAdvice
: 모든 @Controller 즉, 전역에서 발생하는 예외를 잡아 처리해주는 annotation.
API서버여서 에러 응답으로 객체(JSON)를 리턴해야한다면 @ResponseBody
어노테이션이 추가된@RestControllerAdvice
를 적용하면 된다.
@ControllerAdvice
와 동일한 역할 즉, 예외를 잡아 핸들링 할 수 있도록 하는 기능을 수행하면서 @ResponseBody
를 통해 객체를 리턴할 수도 있다는 얘기다.
@ExceptionHandler
: @Controller, @RestController가 적용된 Bean내에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능을 한다.
@RestController
public class MyRestController {
...
...
@ExceptionHandler(NullPointerException.class)
public Object nullex(Exception e) {
System.err.println(e.getClass());
return "myService";
}
}
위와 같이 적용하기만 하면 된다. @ExceptionHandler라는 어노테이션을 쓰고 인자로 캐치하고 싶은 예외클래스를 등록해주면 끝난다.
→ @ExceptionHandler({ Exception1.class, Exception2.class}) 이런식으로 두 개 이상 등록도 가능하다.
💡 실무에서 사용법
필자의 경험상 에러 인터페이스 정의를 제대로 해야한다.
무슨 얘기냐면 에러메시지로 나가는 포맷이 일정해야 한다는 얘기다.
우리 코드: Http code, 자체적으로 쓰는 status code, message를 담은 ENUM 객체
만약 로그인 모듈에서 발생한 예외에 응답하는 메세지는 에러코드랑 설명을 리턴해준다고 하고, 배송 모듈에서 발생한 예외는 에러코드랑 에러가 난 배송 번호를 리턴해준다고 하자
그러면 @RestControllerAdvice
를 이용해서
통합으로 처리하려고 했지만 리턴 타입이 다르니까 통합해서 처리할 수 없다.
HTTP 상태코드, ErrorResponse같은 경우는 좋은 예다.
에러 인터페이스, 포맷이 다 같아야 클라이언트 측에서도 이해하기 좋은 에러가 날라오는 것이다.
그래서 @ExceptionHandler와 함께 @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
이런 것도 집어넣어서 HTTP상태코드를 리턴하기도 한다. (앞에서 설명하진 않았지만...)
다시 한 번 정리하지만 에러 메시지가 잘 정의되어있어야 하는게 전제 조건이다.
ENUM을 이용해 불변 상수로 갖고 있는게 좋다.
@ResponseStatus
와 함께 쓰기
: Controller나 Exception, ExceptionHandler에 사용하여 status 정보를 설정하여 리턴해 준다.
기타 런타임 예외 처리
RuntimeException, Exception 은 글로벌 핸들러에서 따로 받기
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<?> handleInternalError(final RuntimeException ex) {
log.error("Uncaught {} - {}", ex.getClass().getSimpleName(), ex.getMessage());
ex.printStackTrace();
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error(StatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), ex.getMessage()));
}
'Back-end > Spring Boot' 카테고리의 다른 글
[Spring Boot] 연관관계 생성 메서드 삽질 (0) | 2023.08.23 |
---|---|
[Spring JPA] 실시간으로 적재되는 데이터와 부모 엔티티 묶어서 가져오기 (0) | 2023.08.10 |
[Spring Boot] 스웨거 springdoc-openapi 적용 (webmvc-ui) (0) | 2023.08.09 |
[Spring Boot] 서비스에서 로그인된 유저 정보에 대한 의존성 최소화하기 (@AuthenticationPrincipal) + 스프링 계층 구조 지키기 (0) | 2023.07.30 |
[Spring Security] JWT 토큰 인증 이후에 유저 엔티티 객체 불러오기 (Authentication 객체가 담기는 곳) (0) | 2023.07.24 |