728x90

1. validation( 미리 검증 )

1. build.gradle > dependencies
   implementation 'org.springframework.boot:spring-boot-starter-validation'

2. bean validation spec : https://beanvalidation.org/2.0-jsr380/
3. 핸드폰번호 정규식 "^\\d{2,3}-\\d{3,4}-\\d{4}$")
 
@Size : 문자길이 측정( int type 불가 )
@Pattern : 정규식 적용( 이메일, 폰번호 같은것 )

@NotNull : null 불가
@NotEmpty : null , "" 불가
@NotBlank : null , "" , " " 불가

@Past : 과거 날짜
@PastOrPresent  : 오늘이거나 과거 날짜
@Future  :  미래 날짜
@FutureOrPresent :  오늘이거나 미래날짜

@Max : 최대값
@Min : 최소값 

@AssertTrue/False : 별도 로직 적용하여 검증
@Valid  : 해당 object validation 실행
 
 
public class User {
    private String name;

    @Min( value = 0 )
    private int age;

    @Email
    private String email;

    @Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$" , message = "핸드폰 번호 양식과 맞지 않습니다.")
    private String phoneNumber;

    :: 1. annotation 만들어서 검증처리
    @YearMonth
    private String reqYearMonth; //yyyyMMdd  
   ________________________________________________
   :: 2. 별도로 검증처리 ( 아래 코드는 getter 이용함 )
   ::    boolean 값은 메소드 명이 앞에 is 붙어야 한다.
   ::    이 방식의 단점은 User calss 에 종속되어 있어 좋지 않다.

   private String reqYearMonth1; //yyyyMMdd

   @AssertTrue(message = "yyyyMMdd 형식어야아 합니다")
   public boolean isReqYearMonthValidation(){ 
  	  try {
		LocalDate localDate = LocalDate.parse(getReqYearMonth1() , DateTimeFormatter.ofPattern("yyyyMMdd"));
	  }catch(Exception e){
		return false;
	  }
	  return true;
   }
   ________________________________________________
    :: 다른 클래스 참조시 Vaild 붙여주어야 Validation 처리가 된다.
    @Valid
    private List<Car> cars;
   
   .. getter , setter , toString ...
}
 
public class Car {

    @NotBlank
    private String car_name;

    @NotBlank
    @JsonProperty("car_number")
    private String carNumber;

    @NotBlank
    @JsonProperty("TYPE")
    private String type; 

   .. getter , setter , toString ...
}
 
 
== annotation YearMonth 을 만들어서 검증처리 ==

@Constraint(validatedBy = { YearMonthValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {
    String pattern() default "yyyyMMdd";
    String message() default "{yyyyMMdd 형식에 맞지 않습니다.}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}
 
== validator ==

public class YearMonthValidator implements ConstraintValidator<YearMonth,String> {
    private String pattern;

    @Override
    public void initialize(YearMonth constraintAnnotation) { // 초기화
        this.pattern = constraintAnnotation.pattern();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) { // 유효성 검증
        //yyyMMdd
        try {
            LocalDate localDate = LocalDate.parse(value , DateTimeFormatter.ofPattern(this.pattern));
        }catch(Exception e){
            return false;
        }
        return true;
    }
}
 

2. Exception 처리

@ControllerAdvice : Grobal 예외처리 및 특정 package / Controller 예외처리
@ExceptionHandler : 특정 Controller 예외처리

1. 에러 페이지
2. 4xx  , 5xx 에러
3. client가 200 외에 처리하지 못할때는 200 내려주고 별도 에러 메시지 전달
 
 
우선순위 1 : Controller에서 예외처리
_________________ ApiController ____________________

@RestController
@RequestMapping("/api/user")
@Validated                      ::  @Validated을 붙여서
public class ApiController {

    @GetMapping("")
    public User get(
            @Size(min = 2)      :: 매개변수 값을 검증 할 수 있다.
            @RequestParam String name ,

            @NotNull
            @Min(1)
            @RequestParam Integer age ){
        User user = new User();
        user.setName(name);
        user.setAge(age);
        return user;
    }

    @PostMapping("")
    public User post(@Valid @RequestBody User user ){
        System.out.println(user);
        return user;
    }

    :: 이 Controller 에서 발생하는 예외는 이 코드에서 처리 할 수 있다. 
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
        System.out.println("api Controller MethodArgumentNotValidException");
        return ResponseEntity.status(
                HttpStatus.BAD_REQUEST
        ).body(e.getMessage());
    }
}
 
:: 우선순위 2 : advice 패키지 > GlobalControllerAdvice 컨으롤러 에서 통합예외처리하기
_________________ ApiControllerAdvice _______________________________________

:: @RestControllerAdvice(basePackages = "com.example.exception.controller")
    - 특정 패키지의 예외처리를 하는거야.~ (basePackages  = ~~~) 없으면 해당 패키지.

:: @RestControllerAdvice(basePackageClasses = ApiController.class)
    - 특정 클래스 에서 동작하는 예외처리

_____________________________________________________________________________
:: @ControllerAdvice   // view resolver
@RestControllerAdvice(basePackageClasses = ApiController.class)
public class ApiControllerAdvice {
    _________________________________________________________________________
    :: 통합 예외 처리
    @ExceptionHandler(value = Exception.class)
    public ResponseEntity exception( Exception e){
        System.out.println("___________________");
        System.out.println("exception : ");
        System.out.println(e.getClass().getName());
        System.out.println("___________________");
        return ResponseEntity.status( HttpStatus.INTERNAL_SERVER_ERROR ).body("");
    }
    _________________________________________________________________________
    :: 특정 예외 메소드의 예외 처리 등록
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e , HttpServletRequest httpServletRequest ){

        List<Error> errorList = new ArrayList<>(); 

        BindingResult bindingResult = e.getBindingResult();
        bindingResult.getAllErrors().forEach(error->{
            FieldError field = (FieldError)error;

            Error errorMessage = new Error();
            errorMessage.setField(field.getField());
            errorMessage.setMessage(field.getDefaultMessage());
            errorMessage.setInvalidValue(field.getRejectedValue().toString());
            errorList.add(errorMessage);
        });

        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setErrorList(errorList);
        errorResponse.setMessage("");
        errorResponse.setRequestUrl(httpServletRequest.getRequestURI());
        errorResponse.setStatusCode(HttpStatus.BAD_REQUEST.toString());
        errorResponse.setResultCode("FAIL");
        return ResponseEntity.status( HttpStatus.BAD_REQUEST ).body(errorResponse);
    }
    _________________________________________________________________________
    @ExceptionHandler(value = ConstraintViolationException.class)
    public ResponseEntity constraintViolationException(ConstraintViolationException e , HttpServletRequest httpServletRequest ){

        List<Error> errorList = new ArrayList<>(); 

        e.getConstraintViolations().forEach(error ->{ 
            Stream<Path.Node> stream = StreamSupport.stream(
                    error.getPropertyPath().spliterator() , false
            );

            List<Path.Node> list = stream.collect(Collectors.toList());
            String field = list.get(list.size() -1).getName();
            String message = error.getMessage();
            String invalidValud = error.getInvalidValue().toString();

            Error errorMessage = new Error();
            errorMessage.setField(field);
            errorMessage.setMessage(message);
            errorMessage.setInvalidValue(invalidValud);
            errorList.add(errorMessage);
        });

        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setErrorList(errorList);
        errorResponse.setMessage("");
        errorResponse.setRequestUrl(httpServletRequest.getRequestURI());
        errorResponse.setStatusCode(HttpStatus.BAD_REQUEST.toString());
        errorResponse.setResultCode("FAIL"); 
        return ResponseEntity.status(  HttpStatus.BAD_REQUEST ).body(errorResponse);
    }
    _________________________________________________________________________
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ResponseEntity missingServletRequestParameterException( MissingServletRequestParameterException e , HttpServletRequest httpServletRequest ){

        List<Error> errorList = new ArrayList<>();     

        String fieldName = e.getParameterName();
        String invalidValud = e.getMessage();

        Error errorMessage = new Error();
        errorMessage.setField(fieldName);
        errorMessage.setInvalidValue(invalidValud);
        errorList.add(errorMessage);

        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setErrorList(errorList);
        errorResponse.setMessage("");
        errorResponse.setRequestUrl(httpServletRequest.getRequestURI());
        errorResponse.setStatusCode(HttpStatus.BAD_REQUEST.toString());
        errorResponse.setResultCode("FAIL"); 
        return ResponseEntity.status( HttpStatus.BAD_REQUEST).body(errorResponse);
    }
    _________________________________________________________________________
}
 
public class Error {

    private String field;
    private String message;
    private String invalidValue;

    public String getField() {  return field; } 
    public void setField(String field) { this.field = field;  } 
    public String getMessage() {  return message;  } 
    public void setMessage(String message) {  this.message = message;  } 
    public String getInvalidValue() { return invalidValue; } 
    public void setInvalidValue(String invalidValue) {
        this.invalidValue = invalidValue;
    }
}
 
public class ErrorResponse {

    String statusCode;
    String requestUrl;
    String code;
    String message;
    String resultCode;

    List<Error> errorDtoList;

    public String getStatusCode() { return statusCode;  } 
    public void setStatusCode(String statusCode) { this.statusCode = statusCode; } 
    public String getRequestUrl() { return requestUrl; } 
    public void setRequestUrl(String requestUrl) { this.requestUrl = requestUrl; } 
    public String getCode() { return code;  } 
    public void setCode(String code) { this.code = code; } 
    public String getMessage() { return message; } 
    public void setMessage(String message) {  this.message = message; } 
    public String getResultCode() {  return resultCode; } 
    public void setResultCode(String resultCode) { this.resultCode = resultCode;  } 
    public List<Error> getErrorList() { return errorDtoList; } 
    public void setErrorList(List<Error> errorDtoList) { this.errorDtoList = errorDtoList;}
}
 
public class User {

    @NotEmpty
    @Size(min = 1 , max = 10)
    private String name;

    @Min(1)
    @NotNull
    private Integer age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {  return name;  } 
    public void setName(String name) {  this.name = name; } 
    public int getAge() { return age;  } 
    public void setAge(int age) {   this.age = age; }
}
 

 

 

728x90

+ Recent posts