99클럽 2일차 TIL: Spring Boot 에러 핸들링

2024. 4. 5. 17:5699클럽/TIL

728x90
반응형

#1. 오늘의 학습 키워드

자바스프링부트에서 커스텀 된 에러 메시지를 controller 로 보내고 싶을 때 방법

  • api 호출 → controller → service로 이어질 때, 일반적인 exception 이 아닌, 사용자 request 의 논리적 오류를 핸들하고 싶을 때:
    • 이런 상황에서의 Exception은 RuntimeException 을 상속한 커스텀 Exception 클래스인 ApiException으로 던져진다.
      • 실질적인 오류가 아닌 논리 오류로 validation fail에 해당하기 때문에 이렇게 처리를 하였다.
    • 프론트엔드 단과 합의된 반환 코드(사용자의 입력값 오류 (클라이언트 error 4xx))와, 커스텀된 메시지로 오류를 throw 한다.
public int method(Boolean isError) {
    try {
        if (isError) {
            throw new ApiException(MessageCode.USER_ERROR_CODE,"커스텀에러 메시지");
        }
        return 1;
    } catch (ApiException e) {
        throw e;
    } catch (Exception e) {
        throw new ApiException(MessageCode.ERROR_CODE, e.getLocalizedMessage());
    }
}

#2. 공부한 내용

  • @ExceptionHandler
    • 위 예시 service 메서드에서의 catch 블록에서 아래와 같이 로깅을 남겨도 되지만,
public int method(Boolean isError) {
    try {
        if (isError) {
            throw new ApiException(MessageCode.USER_ERROR_CODE,"커스텀에러 메시지");
        }
        return 1;
    } catch (ApiException e) {
        log.warn("Exception : {}", e.getStackTrace()[0]);
        throw e;
    } catch (Exception e) {
        log.error("Exception : {}", e.getStackTrace()[0]);
        throw new ApiException(MessageCode.ERROR_CODE, e.getLocalizedMessage());
    }
}
  • ExceptionHandler 어노테이션으로 구현한 에러 핸들링을 처리하는 메서드를 통해 ApiException 이 던져졌을 때 코드 범위를 통해서 로그 warn이나 error를 찍는 방법이 있다
	@ExceptionHandler({ApiException.class})
	public <T extends ApiException> ResponseEntity<JsonResponse> handleApiException(T exception,
		HttpServletRequest request) {

		if (exception.getJsonResponse().getCode() == MessageCode.USER_ERROR_CODE) {
				log.warn("<<ErrorCode {}>> Program Exception : {}",
				exception.getJsonResponse().getCode(),
				exception.getJsonResponse().getMessage());
		} else {
			log.error("<<ErrorCode {}>> Program Error : {} : {}",
				exception.getJsonResponse().getCode(),
				exception.getJsonResponse().getMessage(),
				exception.getLocalizedMessage());
		}

		//...
	}
  • @RestControllerAdvice
    • 해당 메서드에서 뿐만 아니라, 다른 메서드에서도 동일한 이유 ( 사용자 입력값의 논리적 오류 ) 로 에러를 던질 수 있어야 하고, 해당 에러들에 대한 동일한 처리가 이뤄질 수 있어야 한다.
    • 위 @ExceptionHandler 어노테이션은 전역적으로 지원이 되고, 해당 어노테이션은 해당 @RestControllerAdvice 어노테이션이 지원을 한다.
    • 원래 @ExceptionHandler 어노테이션은 controller 단위로 에러 핸들링을 지원하였다고 한다. 즉 controller 마다 따로 핸들링을 해주거나, 에러 핸들링을 설정한 base controller를 다른 controller가 상속하여 사용해야 돼서, 번거롭다.
    • 이는 status code나 response body에 대한 제어를 허용하고, 여러개의 다른 exception class의 에러 핸들링을 같은 메서드에서 할 수 있게끔 허용한다.
	@ExceptionHandler({ApiException1.class, ApiException2.class})

#3. 오늘의 회고

  • 에러 핸들링이나 로깅 같은 부분은 API 구현 과정에서 처음에는 중요하지 않아 보일 수 있다. 그러나 구현된 API들이 배포가 되면, 프론트 개발 과정에서 호출을 하면서 에러는 필히 발생되게 되고, 이때 비로소 중요해진다.
    • 의도된 에러 핸들링과 의도되지 않은 에러 핸들링을 구분할 필요가 있으며, 프론트엔드 단 개발 시 해당 에러 핸들링된 메시지를 어떻게 사용자에게 보여줄 지 결정할 때 중요한 지표가 되기도 한다.
    • 또한 로깅 툴을 통해 ( grafana / prometheus) 에러 로그를 직관적으로 볼 수 있으며, 화면에서는 보이지 않지만 발생되는 실질적 에러들에 대해서 보수를 할 수 있게끔 도와준다.
728x90
반응형