본문으로 바로가기
반응형

개요

기본적으로 웹 어플리케이션 개발을 시작하는 시점에 미리 해두어야 할 선행 작업으로 생각하는 포인트가 몇가지 존재하는데, 그 중 하나가 바로 전역 예외처리이다. 예외이지만 예외가 아닌 것으로 간주해야할 것들, 예를 들어서 유저가 존재하는지 확인하는 코드에서 존재하지 않는다면 UserNotFoundException 예외를 던지는 행위와 같은 것들이다. 코드상에서는 예외로 처리하지만 서버에서 예외로 간주하기보다 정해진 Response 규격에 맞게 클라이언트로 돌려주는 형태로 처리한다.

또한 해당 예외는 Sentry(파이썬의 경우)등과 같은 에러 트래킹 툴에 보고되지말아야 하므로 관련된 처리 또한 해주는게 일반적이다. 물론 우리가 정의한 커스텀 예외를 제외하고는 당연히 에러 트래킹에 잡혀야한다.

최상위 CustomException 클래스 생성

먼저 우리가 사용할 예외 클래스를 생성해야한다. 여기서는 이름을 CustomException이라고 했다.

@Getter
public class CustomException extends RuntimeException {
    private final HttpStatus statusCode;
    private final Integer errorCode;
    private final String detail;

    public CustomException(HttpStatus statusCode, Integer errorCode, String detail) {
        this.statusCode = statusCode;
        this.errorCode = errorCode;
        this.detail = detail;
    }
}

RuntimeException을 상속받아 구현했다. 일반적으로 HTTP 응답 코드에 해당하는 statusCode와 에러 내용을 표현하는 detail 2가지를 가지고 있겠지만 나는 좀 더 세부적으로 프론트와 에러 코드 규약을 맞춰 사용하길 원하기에 errorCode라는 변수를 추가했다.

커스텀 예외 클래스 생성

public class DuplicatedCodeException extends CustomException {
    public DuplicatedCodeException() {
        super(HttpStatus.BAD_REQUEST, 2001, "duplicated code");
    }
}

위에서 만든 최상위 예외 클래스를 상속받은 후 각 예외에 맞는 값을 채워넣어서 만들어줬다.

CustomExceptionResponse 클래스 생성

@Getter
public class CustomExceptionResponse {
    private Integer errorCode;
    private String detail;

    @Builder
    public CustomExceptionResponse(Integer errorCode, String detail) {
        this.errorCode = errorCode;
        this.detail = detail;
    }
}

최종적으로 응답을 내보내기전에 값을 감싸줄 클래스이다. 예외 클래스에서 내부 필드가 변경되었다면 위 클래스에도 변경사항을 적용해줘야 한다.

예외 핸들러 등록

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<CustomExceptionResponse> handler(CustomException e) {
        CustomExceptionResponse response = CustomExceptionResponse.builder()
                .detail(e.getDetail())
                .errorCode(e.getErrorCode())
                .build();

        return new ResponseEntity<>(response, e.getStatusCode());
    }
}

@ControllerAdvice는 스프링 전역의 모든 Controller에서 발생하는 예외를 처리할 수 있는 어노테이션이다. @RestControllerAdvice는 내부 코드로 들어가보면 알겠지만 @ControllerAdvice + @ResponseBody가 합쳐진 것으로 json형태로 응답할 때 사용한다.

@ExceptionHandler에 위에서 정의한 CustomException 클래스를 등록해줌으로써 해당 예외가 발생할 때 어떻게 처리할 지 정할 수 있다. 따라서 예외의 응답을 위한 클래스인 CustomExceptionResponse에 값을 담아주고 해당 값들을 통해 최종적으로 리턴해주면 된다. 참고로 ResponseEntity의 첫번째 인자에는 body가 들어가고 두번째 인자에는 HTTP 응답 코드가 들어간다.

반응형