1. 오류 처리 위치
오류는 각 Controller 나 Service 또는 Repository 등에서 발생할 수 있다.
오류뿐만 아니라 유효성 검사, 인증과 같은 방식이 필요할 수 있는데, 이를 각 Controller에서 관리하기에는 너무 많은 양이다.
예를 들어, 아파트에서 외부인의 출입 금지를 위해서는 방법이 두 가지가 있다고 하자.
첫 번째는, 각 집집마다 주민에게 외부인의 출입을 막아 달라고 하는 방법이 있다.
두 번째는, 아파트 입구의 경비원에게 부탁하는 방법이 있다.
두 가지 방법 중 첫 번째는 일일이 찾아가 부탁하기에는 너무 작업이 많기 때문에, 쉽고 빠르게 처리할 수 있는 두 번째 방법이 적절할 것이다.

여기서 Dispatcher Servlet이 경비원이고, Controller가 주민이라고 볼 수 있다. 마찬가지로 각 집마다 오류를 알리는 것보다 입구 경비원인 Dispatcher Servlet 에 역할을 맡기고 일을 처리하는 것이 훨씬 수월할 것이다.
이러한 이유 때문에 전체적인 오류를 관리하고 묶어 놓는 위치가 필요하다.
예시로 코드를 보기 위해 다음과 같이 폴더를 생성하고 파일을 만든다.

1. GlobalExceptionHandler
package shop.mtcoding.blog.core.error;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import shop.mtcoding.blog.core.error.ex.*;
import shop.mtcoding.blog.core.util.Script;
@RestControllerAdvice
public class GlobalExceptionHandler {
// 유효성 검사 실패 (잘못된 클라이언트의 요청)
@ExceptionHandler(Exception400.class)
public String ex400(Exception e) {
return Script.back(e.getMessage());
}
// 인증 실패 (클라이언트가 인증없이 요청했거나, 인증을 하거나 실패했거나)
@ExceptionHandler(Exception401.class)
public String ex401(Exception e) {
return Script.back(e.getMessage());
}
// 권한 실패 (인증은 되어 있는데, 삭제하려는 게시글이 내가 적은게 아니다)
@ExceptionHandler(Exception403.class)
public String ex403(Exception e) {
return Script.back(e.getMessage());
}
// 서버에서 리소스(자원) 찾을 수 없을때
@ExceptionHandler(Exception404.class)
public String ex404(Exception e) {
return Script.back(e.getMessage());
}
// 서버에서 심각한 오류가 발생했을때 (알고 있을 때)
@ExceptionHandler(Exception500.class)
public String ex500(Exception e) {
return Script.back(e.getMessage());
}
// 서버에서 심각한 오류가 발생했을때 (모를 때)
@ExceptionHandler(Exception.class)
public String ex(Exception e) {
return Script.back(e.getMessage());
}
}
예시의 오류로 5가지만 참고하여 사용한다. 이를 GlobalExceptionHandler에 담아 하위에서 발생하는 오류를 잡는다.
각 오류 코드들의 파일은 다음과 같이 작성하였다.
package shop.mtcoding.blog.core.error.ex;
// 유효성 검사
public class Exception400 extends RuntimeException{
public Exception400(String message) {
super(message);
}
}2. GlobalExceptionHandler → Exception
public class Exception extends Throwable {
@java.io.Serial
static final long serialVersionUID = -3387516993124229948L;
/**
* Constructs a new exception with {@code null} as its detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*/
public Exception() {
super();
}
/**
* Constructs a new exception with the specified detail message. The
* cause is not initialized, and may subsequently be initialized by
* a call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public Exception(String message) {
super(message);
}실제로 오류가 넘어가는 과정은 GlobalExceptionHandler에서 그에 맞는 오류 코드의 클래스를 따라갔다가 Exception으로 갈 것이다.
3. Exception → Thorwable
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}public String getMessage() {
return detailMessage;
}이러한 방식으로 하위에서 ‘getMessage()’를 가지고 있기 때문에 상위에서 이를 정의할 필요가 없다.
Controller에서 앞으로 계속 throw Exception을 해준다면 GlobalExceptionHandler에서 한 번에 정리가 가능하다.
Share article