-
[김영한 스프링] 48. API 예외 처리 - @ControllerAdviceSpring/스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 2023. 10. 5. 01:53
API 예외 처리 - @ControllerAdvice
@ExceptionHandler를 사용해서 예외를 깔끔하게 처리할 수 있게 되었지만, 정상 코드와 예외 처리 코드가 하나의 컨트롤러에 섞여 있다. @ControllerAdvice 또는 @RestControllerAdvice를 사용하면 둘을 분리할 수 있다.
ExControllerAdvice
main/java/hello/exception/exhandler/advice/ExControllerAdvice 생성
package hello.exception.exhandler.advice; import hello.exception.exception.UserException; import hello.exception.exhandler.ErrorResult; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @Slf4j @RestControllerAdvice public class ExControllerAdvice { @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(IllegalArgumentException.class) public ErrorResult illegalExHandler(IllegalArgumentException e) { log.error("[exceptionHandler] ex", e); return new ErrorResult("BAD", e.getMessage()); } @ExceptionHandler public ResponseEntity<ErrorResult> userExHandler(UserException e) { log.error("[exceptionHandler] ex", e); ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage()); return new ResponseEntity(errorResult, HttpStatus.BAD_REQUEST); } @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler public ErrorResult exHandler(Exception e) { log.error("[exceptionHandler] ex", e); return new ErrorResult("EX", "내부 오류"); } }
ApiExceptionV2Controller의 illegalExHandler, userExHandler, exHandler을 붙여넣기
@ControllerAdvice
- @ControllerAdvice는 대상으로 지정한 여러 컨트롤러에 @ExceptionHandler, @InitBinder 기능을 부여해주는 역할을 한다.
- @ControllerAdvice에 대상을 지정하지 않으면 모든 컨트롤러에 적용된다. (글로벌 적용)
- @RestControllerAdvice는 @ControllerAdvice와 같고, @ResponseBody가 추가되어 있다.
- @Controller, @RestController의 차이와 같다.
ApiExceptionV2Controller 코드에 있는 @ExceptionHandler 모두 제거
package hello.exception.api; import hello.exception.exception.UserException; import hello.exception.exhandler.ErrorResult; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @Slf4j @RestController public class ApiExceptionV2Controller { // @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // @ExceptionHandler // public ErrorResult exHandler(Exception e) { // log.error("[exceptionHandler] ex", e); // return new ErrorResult("EX", "내부 오류"); // } // // @ExceptionHandler // public ResponseEntity<ErrorResult> userExHandler(UserException e) { // log.error("[exceptionHandler] ex", e); // ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage()); // return new ResponseEntity(errorResult, HttpStatus.BAD_REQUEST); // } // // @ResponseStatus(HttpStatus.BAD_REQUEST) // @ExceptionHandler(IllegalArgumentException.class) // public ErrorResult illegalExHandler(IllegalArgumentException e) { // log.error("[exceptionHandler] ex", e); // return new ErrorResult("BAD", e.getMessage()); // } @GetMapping("/api2/members/{id}") public MemberDto getMember(@PathVariable("id") String id) { if (id.equals("ex")) { throw new RuntimeException("잘못된 사용자"); } if (id.equals("bad")) { throw new IllegalArgumentException("잘못된 입력 값"); } if (id.equals("user-ex")) { throw new UserException("사용자 오류"); } return new MemberDto(id, "hello " + id); } @Data @AllArgsConstructor static class MemberDto { private String memberId; private String name; } }
ExControllerAdvice에 추가했으므로 주석처리
실행
대상 컨트롤러 지정 방법
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}
// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class,
AbstractController.class})
public class ExampleAdvice3 {}https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-anncontroller-advice(스프링 공식 문서 참고)
스프링 공식 문서 예제에서 보는 것처럼 특정 애노테이션이 있는 컨트롤러를 지정할 수 있고, 특정 패키지를 직접 지정할 수도 있다. 패키지 지정의 경우 해당 패키지와 그 하위에 있는 컨트롤러가 대상이 된다. 그리고 특정 클래스를 지정할 수도 있다.
대상 컨트롤러 지정을 생략하면 모든 컨트롤러에 적용된다.
정리
@ExceptionHandler와 @ControllerAdvice를 조합하면 예외를 깔끔하게 해결할 수 있다.
출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2
'Spring > 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술' 카테고리의 다른 글
[김영한 스프링] 50. 스프링 타입 컨버터 - 소개 (1) 2023.10.05 [김영한 스프링] 49. 스프링 타입 컨버터 - 프로젝트 생성 & 세팅 (0) 2023.10.05 [김영한 스프링] 47. API 예외 처리 - @ExceptionHandler (0) 2023.10.05 [김영한 스프링] 46. API 예외 처리 - 스프링이 제공하는 ExceptionResolver (2) 2023.09.27 [김영한 스프링] 45. API 예외 처리 - HandlerExceptionResolver 시작 & 활용 (0) 2023.09.27