ABOUT ME

-

오늘 방문자
-
어제 방문자
-
전체
-
  • @Valid 와 @ControllerAdvice로 DTO 예외처리하기
    Spring 2020. 1. 12. 00:54


    @Valid 세팅 및 사용하기 해당편에 이어서


    @Valid와 @ControllerAdvice를 이용한 Exception처리를 하려고한다.

    @Vailid 사용법에 관한 설명은 생략한다. 위의 링크를 참고하면된다


    @Valid는 @ControllerAdvice와 같이 쓰면 궁합이 굉장히 좋다!


    회사에서도 물론 같이 쓰고있고 ㅎㅎ



    @ControllerAdvice 이란?

    @Controller 전역에서 발생할 수 있는 예외를 잡아 처리해주는 annotation이다.


    바로 코드로 보자

     

    패키지구조

    빨간점의 클래스들만 주의 깊게 보면 된다



     

     

    Controller

    package com.example.validtest.controller;

    import com.example.validtest.model.UserDto;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;

    import javax.validation.Valid;

    @Slf4j
    @RestController
    public class UserController {

    @PostMapping(value = "/user")
    public String insertUser(@RequestBody @Valid UserDto userDto){
    log.info("@ param : {}", userDto);
    return "success";
    }
    }


    model

    package com.example.validtest.model;

    import lombok.ToString;

    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotNull;

    @ToString
    public class UserDto {

    @NotNull(message = "번호를 입력해주세요")
    private long no;

    @NotNull(message = "이름을 입력해주세요")
    private String name;

    @Min(value = 5000, message = "금액은 5000원이상이여야 합니다") // 5000원 보다 같거나, 크면 통과
    private long Amt;

    }


    ErrorResponse 클래스는

    API에 내려줄 에러 형태를 정하였다

    code 변수 : enum을 이용하여 에러코드를 정의한 후 내려줄 것이다

    description : 어떠한 에러인지 간단히 넣어줄 것이고  ex) 필수값이 누락되었습니다

    detail : 에러의 세부내용을 넣어줄 것이다. ex)이름은 필수값입니다

    package com.example.validtest.exception;

    import lombok.Getter;
    import lombok.Setter;

    @Setter
    @Getter
    public class ErrorResponse {

    private String code;

    private String description;

    private String detail;

    public ErrorResponse(String code, String description) {
    this.code = code;
    this.description = description;
    }

    public ErrorResponse(String code, String description, String detail) {
    this.code = code;
    this.description = description;
    this.detail = detail;
    }
    }


    ErrorCode enum

    발생할 에러에 대한 코드, 간단한 에러 설명을 넣어 놓았다.

    세부에러내용에 대해서는 DTO 멤버필드에 지정해놓은 message를 보여줄 것이다

    package com.example.validtest.exception;

    import lombok.Getter;

    public enum ErrorCode {

    NOT_NULL("ERROR_CODE_0001","필수값이 누락되었습니다")
    , MIN_VALUE("ERROR_CODE_0002", "최소값보다 커야 합니다.")
    ;

    @Getter
    private String code;

    @Getter
    private String description;

    ErrorCode(String code, String description) {
    this.code = code;
    this.description = description;
    }
    }


    ExceptionController

    해당 클래스는 @Controller, @RestController에서 발생한 Exception에 대해서 모두 이곳에서

    처리 할 수 있다. 


    @Valid의 유효성을 통과하지 못하면 ,  MethodArgumentValidException이 발생한다.

    그러면 @ExceptionHandler를 통해서 캐치한 후 Error를 아주 이쁘게 내려주면 되는 것이다.
    (@ExceptionHandler에 여러개의 클래스도 지정 할 수 있다.)


    나는 makeErrorResponse라는 메소드를 만들고 그안에서 validation에 걸린 어노테이션명을 가져와

    해당하는 에러를 내려 주었다.

    package com.example.validtest.exception;

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;

    import javax.servlet.http.HttpServletRequest;


    @Slf4j
    @ControllerAdvice
    public class ExceptionController {

    /**
    * @valid 유효성체크에 통과하지 못하면 MethodArgumentNotValidException 이 발생한다.
    */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> methodValidException(MethodArgumentNotValidException e, HttpServletRequest request){
    log.warn("MethodArgumentNotValidException 발생!!! url:{}, trace:{}",request.getRequestURI(), e.getStackTrace());
    ErrorResponse errorResponse = makeErrorResponse(e.getBindingResult());
    return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    private ErrorResponse makeErrorResponse(BindingResult bindingResult){
    String code = "";
    String description = "";
    String detail = "";

    //에러가 있다면
    if(bindingResult.hasErrors()){
    //DTO에 설정한 meaasge값을 가져온다
    detail = bindingResult.getFieldError().getDefaultMessage();

    //DTO에 유효성체크를 걸어놓은 어노테이션명을 가져온다.
    String bindResultCode = bindingResult.getFieldError().getCode();

    switch (bindResultCode){
    case "NotNull":
    code = ErrorCode.NOT_NULL.getCode();
    description = ErrorCode.NOT_NULL.getDescription();
    break;
    case "Min":
    code = ErrorCode.MIN_VALUE.getCode();
    description = ErrorCode.MIN_VALUE.getDescription();
    break;
    }
    }

    return new ErrorResponse(code, description, detail);
    }
    }




    POSTMAN을 이용하여 호출한 결과





    'Spring' 카테고리의 다른 글

    spring batch 링크모음  (0) 2020.04.21
    Gradle 인식못할 때  (0) 2020.04.02
    @Valid 세팅 및 사용법  (0) 2019.10.26
    MyBatis속성 setting값의 우선순위는 어떻게 될까?  (0) 2019.09.15
    Gradle annotationProcessor  (0) 2019.09.07

    댓글

Designed by Tistory.