본문 바로가기
개발/Spring

ControllerAdvice

by BellOne4222 2024. 2. 17.
@GetMapping("/")
public String root() throws Exception{
    throw new Exception("테스트");
}
  • 결과 : java.lang.Exception: 테스트

 

BasicErrorController

  • 스프링부트의 기본 응답
  • BasicErrorController를 상속받아서 응용 할 수 있다.
    • 특정 메소드만 오버라이드
    • 특정 핸들러 메소드를 추가
  • BasicErrorController의 핸들러 메소드
    • BasicErrorController.errorHtml()
      • 뷰 응답
    • BasicErrorController.error()
      • json body 응답

 

static html 이나 template 파일을 추가해서 커스텀 페이지를 등록하는 법


• 단일 기본 페이지
 • /resources/static/error.html
 • /resources/public/error.html
 • /resources/template/error.[템플릿확장자]

• http status 별 기본 페이지
 • /resources/[static|public|template]/error/{http status 번호}.[html|템플릿확장자]
 • /resources/[static|public|template]/error/4xx.[html|템플릿확장자]
 • /resources/[static|public|template]/error/5xx.[html|템플릿확장자]

 

@ExceptionHandler

  • 비즈니스 로직이 던진 예외에 반응하는 핸들러 메소드
    • 위치 : 특정 컨트롤러 클래스 내부, @ControllerAdvice 컴포넌트 내부
    • 특정 예외에 반응한다.
    • 예외 처리 범위
      • 컨트롤러 안에 작성했을 경우 : 해당 컨트롤러만 처리
      • @ControllerAdvice에 작성했을 경우 : 프로젝트 전체 처리
  •  입출력 자료형도 핸들러 메소드와 유사하지만 차이점으로 예외를 입력 인자로 다룰 수 있다.
    • 입력 인자로 받은 예외만 처리할 수 있다.

 

@ControllerAdvice

  • @ExceptionHandler를 모아서 글로벌하게 적용 할 때 쓰는 애노테이션
  • 종류
    • @ControllerAdvice : 뷰 에러 처리
    • @RestControllerAdvice = @ControllerAdvice + @ResposeBody : JSON API 에러 처리
  • 속성
    • • value == basePackages
      • basePackages: 적용 범위를 문자열을 이용해 특정 패키지로 지정
      • basePackageClasses: 적용 범위를 대표 클래스 한 개를 이용해 특정 패키지로 지정
      • basePackages 를 type-safe 하게 사용하기 위해 제공하는 옵션
      • assignableTypes: 적용 범위를 특정 클래스에 할당할 수 있는 컨트롤러로 지정
      • annotations: 적용 범위를 특정 애노테이션을 사용한 컨트롤러로 지정

 

@ResposeEntityExceptionHandler

  • Spring MVC에서 내부적으로 발생하는 예외들을 처리하는 클래스
  • API 예외 처리를 담당하는 @ControllerAdvice 클래스에서 상속 받아 사용
  • 커스터마이징을 원하는 특정 메소드를 오버라이딩

 

// BaseErrorController

@Controller // 이 클래스가 컨트롤러로 사용됨을 표시
public class BaseErrorController implements ErrorController { // 오류 처리를 위해 ErrorController 인터페이스를 구현함

	@RequestMapping(path = "/error", produces = MediaType.TEXT_HTML_VALUE) // /error 경로에 대한 HTTP 요청 매핑
	public ModelAndView errorHtml(HttpServletResponse response) { // HTML 오류 처리를 위한 메서드
		HttpStatus httpStatus = HttpStatus.valueOf(response.getStatus()); // 응답에서 HTTP 상태 가져오기
		ErrorCode errorCode = httpStatus.is4xxClientError() ? ErrorCode.BAD_REQUEST : ErrorCode.INTERNAL_ERROR; // HTTP 상태에 따라 오류 코드 결정

		if (httpStatus == HttpStatus.OK) { // 상태가 OK인 경우
			httpStatus = HttpStatus.FORBIDDEN; // 상태를 FORBIDDEN으로 변경
			errorCode = ErrorCode.BAD_REQUEST; // 오류 코드를 BAD_REQUEST로 설정
		}

		return new ModelAndView( // 새로운 ModelAndView 객체 생성
			"error", // 뷰 이름
			Map.of( // 모델 속성
				"statusCode", httpStatus.value(), // HTTP 상태 코드
				"errorCode", errorCode, // 오류 코드
				"message", errorCode.getMessage(httpStatus.getReasonPhrase()) // 오류 메시지
			),
			httpStatus // HTTP 상태
		);
	}
}

 

@ExceptionHandler

// APIEventController 내의 범위의 핸들러 메서드 GeneralException 처리 -> 발생하면 아래 형식으로 json 조립해서
	@ExceptionHandler
	public ResponseEntity<APIErrorResponse> general(GeneralException e){
		ErrorCode errorCode = e.getErrorCode();
		HttpStatus status = errorCode.isClientSideError() ?
			HttpStatus.BAD_REQUEST :
			HttpStatus.INTERNAL_SERVER_ERROR;

		return ResponseEntity
			.status(status)
			.body(APIErrorResponse.of(
				false, errorCode, errorCode.getMessage(e)
			));
	}

 

@ControllerAdvice

@ControllerAdvice // 컨트롤러에서 발생하는 예외를 처리하는 어드바이스 클래스
public class BaseExceptionHandler {

	@ExceptionHandler // 예외 처리 메서드
	public ModelAndView general(GeneralException e) { // GeneralException을 처리하는 메서드
		ErrorCode errorCode = e.getErrorCode(); // 예외로부터 오류 코드 가져오기

		return new ModelAndView( // 새로운 ModelAndView 객체 반환
			"error", // 뷰 이름
			Map.of( // 모델 속성
				"statusCode", errorCode.getHttpStatus().value(), // HTTP 상태 코드
				"errorCode", errorCode, // 오류 코드
				"message", errorCode.getMessage() // 오류 메시지
			),
			errorCode.getHttpStatus() // HTTP 상태
		);
	}

	@ExceptionHandler // 예외 처리 메서드
	public ModelAndView exception(Exception e, HttpServletResponse response) { // Exception을 처리하는 메서드
		HttpStatus httpStatus = HttpStatus.valueOf(response.getStatus()); // 응답에서 HTTP 상태 가져오기
		ErrorCode errorCode = httpStatus.is4xxClientError() ? ErrorCode.BAD_REQUEST : ErrorCode.INTERNAL_ERROR; // HTTP 상태에 따라 오류 코드 결정

		if (httpStatus == HttpStatus.OK) { // 상태가 OK인 경우
			httpStatus = HttpStatus.FORBIDDEN; // 상태를 FORBIDDEN으로 변경
			errorCode = ErrorCode.BAD_REQUEST; // 오류 코드를 BAD_REQUEST로 설정
		}

		return new ModelAndView( // 새로운 ModelAndView 객체 반환
			"error", // 뷰 이름
			Map.of( // 모델 속성
				"statusCode", httpStatus.value(), // HTTP 상태 코드
				"errorCode", errorCode, // 오류 코드
				"message", errorCode.getMessage(e) // 예외로부터 오류 메시지 가져오기
			),
			httpStatus // HTTP 상태
		);
	}

}

 

@RestControllerAdvice

@RestControllerAdvice(annotations = {RestController.class, RepositoryRestController.class}) // RestController 및 RepositoryRestController 어노테이션이 지정된 클래스에서 발생하는 예외를 처리하는 어드바이스 클래스
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {

	@ExceptionHandler // 예외 처리 메서드
	public ResponseEntity<Object> validation(ConstraintViolationException e, WebRequest request) { // ConstraintViolationException을 처리하는 메서드
		return handleExceptionInternal(e, ErrorCode.VALIDATION_ERROR, request); // 예외 처리 내부 메서드 호출
	}

	@ExceptionHandler // 예외 처리 메서드
	public ResponseEntity<Object> general(GeneralException e, WebRequest request) { // GeneralException을 처리하는 메서드
		return handleExceptionInternal(e, e.getErrorCode(), request); // 예외 처리 내부 메서드 호출
	}

	@ExceptionHandler // 예외 처리 메서드
	public ResponseEntity<Object> exception(Exception e, WebRequest request) { // Exception을 처리하는 메서드
		return handleExceptionInternal(e, ErrorCode.INTERNAL_ERROR, request); // 예외 처리 내부 메서드 호출
	}

	@Override
	protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
		return handleExceptionInternal(ex, ErrorCode.valueOf(status), headers, status, request); // 예외 처리 내부 메서드 호출
	}

	private ResponseEntity<Object> handleExceptionInternal(Exception e, ErrorCode errorCode, WebRequest request) { // 예외 처리 내부 메서드
		return handleExceptionInternal(e, errorCode, HttpHeaders.EMPTY, errorCode.getHttpStatus(), request); // 예외 처리 내부 메서드 호출
	}

	private ResponseEntity<Object> handleExceptionInternal(Exception e, ErrorCode errorCode, HttpHeaders headers, HttpStatus status, WebRequest request) { // 예외 처리 내부 메서드
		return super.handleExceptionInternal( // 부모 클래스의 예외 처리 내부 메서드 호출
			e,
			ApiErrorResponse.of(false, errorCode.getCode(), errorCode.getMessage(e)), // API 응답 오류 생성
			headers,
			status,
			request
		);
	}

}

 

 

 

'개발 > Spring' 카테고리의 다른 글

Test Driven Development(TDD)  (1) 2024.02.18
Controller Test  (0) 2024.02.18
MVC 패턴 - API 설계(2)  (0) 2024.02.17
함수형 프로그래밍  (0) 2024.02.16
MVC 패턴(2) - API 설계  (0) 2024.02.16