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