[ Spring ]/REST API

[Spring] AOP를 이용한 Exception Handing

쿠릉쿠릉 쾅쾅 2022. 5. 8. 04:31
728x90

 

 

@ControllerAdvice 애노테이션과 ResponseEntityExceptionHandler 추상 클래스 상속을 통해 예외처리를 할 수 있다.
@ControllerAdvice 애노테이션을 사용하여 Exception 처리를 한 곳으로 모으는 경우, ResponseEntityExceptionHandler를 상속 받도록 하여 Spring MVC에서 기본으로 제공되는 Exception들의 처리를 간단하게 등록할 수 있다.
ResponseEntityExceptionHandler 추상 클래스에는 각 Exception 처리를 위한 메서드들을 모두 protected로 선언되어 있어서 필요에 따라 Override를 해야한다.

📌 ExceptionResponse

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExceptionResponse {

    private LocalDateTime timestamp;
    private String message;
    private String details;

}

오류를 담을 객체다.

📌 UserNotFoundException

public class UserNotFoundException extends RuntimeException {

    public UserNotFoundException(String message) {
        super(message);
    }

}

사용자 정의 예외 클래스다.

📌 CustomizedResponseEntityExceptionHandler

@RestControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    // 오류 정보를 담을 객체를 만드는 메서드
    private ExceptionResponse createExceptionResponse(Exception ex, WebRequest request) {
        return new ExceptionResponse(LocalDateTime.now(), ex.getMessage(), request.getDescription(false));
    }


    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
        ExceptionResponse exceptionsResponse = createExceptionResponse(ex, request);
        return new ResponseEntity<>(exceptionsResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }


    @ExceptionHandler(UserNotFoundException.class)
    public final ResponseEntity<Object> handleUserNotFoundException(Exception ex, WebRequest request) {
        ExceptionResponse exceptionsResponse = createExceptionResponse(ex, request);
        return new ResponseEntity<>(exceptionsResponse, HttpStatus.NOT_FOUND);
    }

}

 

스프링 MVC Excpetion Handler 기능을 한다. 
@ControllerAdivce를 사용하면 Global Controller Exception Handling을 할 수 있다.
ResponseEntityExceptionHandler 추상 클래스를 상속 받고 있다. ResponseEntityExceptionHandler 추상 클래스는 Spring MVC에서 발생할 수 있는 예외에 대해 미리 Handling 해놓은 클래스다. 
즉, @ControllerAdvice 애노테이션으로 모든 컨트롤러에서 발생하는 Exception을 캐지하고 캐치한 Exception들(사용자 정의 Exception 클래스 제외)을 기본적으로 ResponseEntityExceptionHandler에서 정의된 메서드로 처리할 수 있다.
WebRequest는 HttpServletRequest 클래스와 비슷하지만 HttpServletRequest와 달리 서블릿에 종속되지 않는다. HttpServletRequest보다 WebRequest를 사용할 것을 권장한다. WebRequest를 통해 예외가 어느 URI에서 발생했는지 알 수 있다. WebReqeust#getDescription(boolean includeClientInfo) 를 통해 예외가 발생한 URI 주소를 가져올 수 있다. 파라미터를 true로 할 경우 클라이언트에 대한 정보도 가져온다. 

 

🧷 오류 메시지 캐싱

더보기

📌 ExampleException

public class ExampleException extends RuntimeException{

    public ExampleException() {
        super();
    }

    public ExampleException(String message) {
        super(message);
    }

    public ExampleException(String message, Throwable cause) {
        super(message, cause);
    }

    public ExampleException(Throwable cause) {
        super(cause);
    }

    protected ExampleException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

 사용자 정의 오류 클래스

📌 ExceptionResponse

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExceptionResponse {

    private LocalDateTime timestamp;
    private String message;
    private String details;

}

 오류를 담을 객체

 📌 CustomizedResponseEntityExceptionHandler

@RestControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    // 오류 정보를 담을 객체를 만드는 메서드
    private ExceptionResponse createExampleException(Exception ex, WebRequest request) {
        return new ExceptionResponse(LocalDateTime.now(), resolveMessage(ex, ex.getMessage()), request.getDescription(false));
    }

    /**
     * 오류 메시지 캐싱
     */
    private final Map<Class<? extends Exception>, String> messageMappings 
            = Collections.unmodifiableMap(new LinkedHashMap<>(){
                
        {
            put(ExampleException.class, "심플예제 입니다.");
        }
        
    });


    private String resolveMessage(Exception ex, final String defaultMessage) {
        return messageMappings.entrySet()
                .stream()
                .filter(entry -> entry.getKey().isAssignableFrom(ex.getClass()))
                .findFirst()
                .map(Map.Entry::getValue)
                .orElse(defaultMessage);
    }
    
    @ExceptionHandler(ExampleException.class)
    public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
        ExceptionResponse exceptionsResponse = createExceptionResponse(ex, request);
        return new ResponseEntity<>(exceptionsResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

 messageMappings : key를 Exception Class로 하고, value를 String 타입인 에러 메시지로 했다. 순서가 있는 순서가 있는 LinkedHashMap을 만들고 이 map을 unmodifiableMap으로 불변 객체로 만들어 캐싱한다.
 resovleMeessage : 파라미터로 Exception과 defaulMessage를 받는다. messageMappings에서 일치하는 Exception이 있는지 확인하고 존재하면 value 값을 가져와서 사용하고 없다면 인자로 받은 defaultMessage를 사용한다.

 

 

 

 

 

👀 참고 자료

https://velog.io/@modsiw/Spring-Controller%EC%97%90%EC%84%9C-%EB%8B%A4%EC%96%91%ED%95%9C-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-Request-%EC%9A%94%EC%B2%AD%EB%B0%9B%EA%B8%B0-PathVariable-RequestParam-ModelAttribute-RequestBody

 

[Spring] Controller에서 다양한 타입으로 Request 요청( PathVariable, RequestParam, ModelAttribute, RequestBody)

Spring에서 Client로 받은 요청을 객체로 바인딩하기 위해 사용하는 방법 ServletRequest, ServletResponseHttpServletRequest, HttpServletResponse : 위에꺼를 상속받음HttpServletRequest.getI

velog.io

 

https://programmingrecoding.tistory.com/20

 

parameter 추출하기(HttpServletRequest, WebRequest ,@RequestParam, @PathVariable)

Contorller에서 Request를 받는방식에는 @RequesetMapping, @PostMapping, @GetMapping이 있다. @RequestMapping의 사용형태 ex) value는 Request해주는 요청값이고(form태그의 action="test1"이렇게 받았다면 위..

programmingrecoding.tistory.com

 

https://blog.naver.com/PostView.naver?blogId=writer0713&logNo=221605253778&parentCategoryNo=&categoryNo=83&viewDate=&isShowPopularPosts=true&from=search 

 

[Spring] Rest API Exception Handling

Spring MVC (+ Spring Boot)를 사용하여 Rest API를 만들때마다 Exception Handling을 하면서...

blog.naver.com

 

 

728x90