프로젝트를 진행하다 보면 정말 수많은 에러와 직면하게 된다.
각 에러들은 종류도 다양하며 발생할 수 있는 상황 또한 다양하다.
하지만, 기존의 예외들은 모두 정해져있는 포맷을 반환한다. 예외 메시지나 예외 발생 후 처리 등이 모두 기존에 작성된 대로만 흘러간다.
이러한 방식은 프로젝트를 진행할 때 불친절하게 느껴진다.
특히나 협업을 진행할 때, 이 예외는 어떤 예외이고 왜 나왔는지 프론트엔드 개발자에게 설명해줄 필요가 있다.
더 나아가면 예외 발생 시 데이터를 잘 정제된 방식으로 반환시키도록 만들고 싶기도 하다.
스프링에서는 이러한 예외 처리가 가능하도록 도와준다. 어떤 방법으로 내가 원하는 예외 처리를 할 수 있을까?
기본으로 제공되는 자바 예외 클래스 대신 제공하기 위하여, 개발자가 정의한 맞춤형 예외 클래스로 예외 처리하는 방식
애플리케이션의 오류를 더 명확하고 구조적으로 관리할 수 있도록 도와줌
1. 명확한 의도 전달
NullPointerException , IllegalArgumentException 등 기본 예외 클래스는 일반적인 문제 처리를 위한 것2. 코드 가독성 향상
3. 표준화된 에러 처리
4. 유지보수성 증가
1. 사용자 정의 예외 클래스 생성
RuntimeException 혹은 Exception 을 상속 받아 새로운 예외 클래스 생성2. 문제되는 부분에서 해당 예외 발생
throw 로 해당 예외를 강제로 발생3. 예외 처리
DTO 객체를 사용하기도 함1. Custom Exception Class
2. Global Exception Handler
@ControllerAdvice : 글로벌 예외 처리를 위한 어노테이션@ExceptionHandler : 특정 예외 처리를 위한 메서드 정의 어노테이션3. Error Response Object
errorCode : 에러를 구분하기 위한 코드message : 사용자에게 전달할 메시지💡
Tip: 예외 처리 방식의 종류
1. Checked Exception
Exception 상속try-catch , 메서드 선언부에 throws 선언 등 예외를 처리해야만 함IOException , SQLException ...2. Unchecked Exception
RuntimeException 상속NullPointerException , IllegalArgumentException ...@ControllerAdvice 등을 따로 지정해주지 않았다면, 정의한 컨트롤러 내에서만 적용1. CustomException 클래스 정의
@Getter
public class CustomExcepiton extends RuntimeException {
private final String errorCode;
public CustomException(String errorCode, String msg) {
super(msg);
this.errorCode = errorCode;
}
}
RuntimeException 을 상속받은 CustomException 생성errorCode 로 예외 발생 코드까지 전달할 수 있도록 설정2. GlobalExceptionHandler 클래스 정의
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
public ResponseEntity<?> handleCustomException(CustomException ex) {
ErrorResponse errorResponse = new ErrorResponse(ex.getErrorCode(), ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleException(Exception ex) {
ErrorResponse errorResponse = new ErrorResponse("UNKNOWN_ERROR", "Compile Error!!");
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@ControllerAdvice 로 전역에서 예외 발생 시 GlobalExceptionHandler 클래스를 찾아오도록 정의@ExceptionHandler 로 예외 타입별로 처리 메서드 구현CustomException 과 나머지 Exception 을 다르게 처리3. ErrorResponse 클래스 정의
@Getter
public class ErrorResponse {
private String errorCode;
private String message;
public ErrorResponse(String errorCode, String message) {
this.errorCode = errorCode;
this.message = message;
}
}
JSON 형식으로 반환하기 위한 DTO 클래스 정의4. 컨트롤러에서 예외 발생시키기
@RestController
public class ExceptionController {
@GetMapping
public String getException() {
throw new CustomException("400", "Bad Reqeust!!");
}
}
/ 를 호출하면 CustomException 이 발생하도록 설계errorCode : 404message : "Not Found Error"5. 실행 결과
/ 호출{
"errorCode": "400",
"message": "Bad Request!!"
}
BAD REQUEST / 400{
"errorCode": "UNKNOWN_ERROR",
"message": "Compile Error!!"
}
INTERNAL SERVER ERROR / 500💡
@ControllerAdvice와@ExceptionHandler는 모두 선언적 코드이다. 선언적 코드에 대해서도 추가로 정리해보았다.
@ExceptionHandler 로 예외를 지정하기만 하면, 예외 발생 시 스프링이 알아서 해당 메서드 호출명령형 코드
public String exceptionHandler() {
try {
throw new NoSuchElementException("예외 발생");
} catch (NoSuchElementException e) {
return e.getMessage();
}
}
try-catch 를 이용해 모두 직접 처리선언적 코드
@RestController
public class ExceptionHandler() {
@GetMapping
public String exceptionHandler() {
throw new NoSuchElementException("예외 발생");
}
}
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NoSuchElementException.class)
public String handleNoSuchException(NoSuchException ex) {
return ex.getMessage();
}
}
handleNoSuchException 은 NoSuchException 이 발생할 때 예외를 처리하도록 선언한 것1. 어노테이션 기반
2. 가독성 향상
3. 명확한 의도
참고) OpenAI. (2024).ChatGPT(4o)[Large language model].https://chatgpt.com/