대게 개발을 진행할 때 front에서 validate를 체크하고 민감한 정보에 대해서는 한번더 체크를 진행하고 작업을 했었다.
하지만 클라이언트에서만 validation을 체크하게 되는 경우 브라우저에서 악의적인 행동에 대해서 대응하기 어려워질수 있기 때문에 백엔드에서도 Validation을 처리해야한다.
여기서 사용되는 @valid 어노테이션들을 알아보자.
1. DTO validation 선언
우선적으로 DTO에 각 속성에 필요한 @valid 옵션들을 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package com.wedul.springboottest.rest.dto; import lombok.*; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; /** * 사용자 클래스. * User: wedul * Date: 2018-08-05 * Time: 오후 9:47 */ @Data @AllArgsConstructor @EqualsAndHashCode @NoArgsConstructor public class UserDto { @NotBlank(message = "이름을 입력해주세요.") private String name; private int age; @NotBlank(message = "이메일을 입력해주세요.") @Email(message = "이메일을 양식을 지켜주세요.") private String email; public void updateInfo(UserDto user) { this.age = user.getAge(); this.email = user.getEmail(); } } | cs |
@Valid 데이터 종류
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @AssertFalse : false 값만 통과 가능 @AssertTrue : true 값만 통과 가능 @DecimalMax(value=) : 지정된 값 이하의 실수만 통과 가능 @DecimalMin(value=) : 지정된 값 이상의 실수만 통과 가능 @Digits(integer=,fraction=) : 대상 수가 지정된 정수와 소수 자리수보다 적을 경우 통과 가능 @Future : 대상 날짜가 현재보다 미래일 경우만 통과 가능 @Past : 대상 날짜가 현재보다 과거일 경우만 통과 가능 @Max(value) : 지정된 값보다 아래일 경우만 통과 가능 @Min(value) : 지정된 값보다 이상일 경우만 통과 가능 @NotNull : null 값이 아닐 경우만 통과 가능 @Null : null일 겨우만 통과 가능 @Pattern(regex=, flag=) : 해당 정규식을 만족할 경우만 통과 가능 @Size(min=, max=) : 문자열 또는 배열이 지정된 값 사이일 경우 통과 가능 @Valid : 대상 객체의 확인 조건을 만족할 경우 통과 가능 출처: http://goldenraccoon.tistory.com/entry/Valid-annotation-종류 [황금너구리 블로그] | cs |
2. @Request에서 전달받는 파라미터 설정
간단하게 @Valid 설정을 통해 전달받을 객체에 대한 유효성 체크를 설정할 수 있다.
1 2 3 4 5 6 7 8 9 10 | /** * Edit user info response entity. * * @param user the user * @return the response entity */ @PutMapping(value = "/user/info") public ResponseEntity<?> editUserInfo(@RequestBody @Valid UserDto user) { return ResponseEntity.ok(userService.updateUser(user)); } | cs |
3. 테스트
위의 설정한 validation 설정을 위반하여 request를 보내보자
요청
응답
validation 위반 시 400에러와 함께 오류 field와 기존에 설정한 defaultMessage를 확인할 수 있다.단순하게 regex와 null, email 등등에 대한 validation 체크 이외에 중복 체크등을 진행하기 위해서는 별도의 작업이 필요하다.
4-1) 기존의 Validation과 동일한 방식으로 표현하기 위해서 별도의 RuntimeException 클래스를 만든다.
이 예외 클래스는 Response Stauts는 400으로 전달될 수 있게 @ResponseStatus(HttpStatus.BAD_REQUEST)로 설정하고 예외가 발생한 field와 defaultMessage를 출력해주기 위해 별도의 속성을 만들어준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package com.wedul.springboottest.rest.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; import java.util.List; /** * Created by Leo. * User: wedul * Date: 2018-08-09 * Time: 오후 9:06 */ // 기존에 validation의 에러와 동일하게 400 에러가 발생하도록 추가 @ResponseStatus(HttpStatus.BAD_REQUEST) public class ValidationException extends RuntimeException { private Error[] errors; public ValidationException(String defaultMessage, String field){ this.errors = new Error[]{new Error(defaultMessage, field)}; } public ValidationException(Error[] errors) { this.errors = errors; } public Error[] getErrors() { return errors; } public static class Error { private String defaultMessage; private String field; public Error(String defaultMessage, String field) { this.defaultMessage = defaultMessage; this.field = field; } public String getDefaultMessage() { return defaultMessage; } public String getField() { return field; } } } | cs |
이 생성된 ValidationException 클래스를 ErrorAttributes 인터페이스를 재 정의하여 Validation 오류가 발생하였을 때 별도의 작업을 할 수 있도록 처리해준다. (이 Bean 객체는 Application에 정의해준다.)
※ ErrorAttributes는 기본적으로 스프링 부트에서 에러 처리하는 BasicErrorController인데 이 인터페이스는 DefaultErrorAttributes 클래스를 참조하여 커스터마이징이 가능하다.
참고
https://brunch.co.kr/@sbcoba/9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.WebRequest; @SpringBootApplication public class SpringboottestApplication { public static void main(String[] args) { SpringApplication.run(SpringboottestApplication.class, args); } @Bean public ErrorAttributes errorAttributes() { return new DefaultErrorAttributes() { @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace); Throwable error = getError(webRequest); // validatijon Exception에 경우 별도의 처리를 진행한 에러 데이터 추가 if (error instanceof ValidationException) { errorAttributes.put("errors", ((ValidationException)error).getErrors()); } return errorAttributes; } }; } | cs |
그리고 서비스 코드 영역에서 중복, 금지 등등을 확인하고 ValidationException을 throw하면 front에 동일한 형태에 에러코드를 전송해 줄 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 서비스 코드 @Override public ResultDto insertUser(UserDto user) { if (null == users) { return ResultDto.fail("UserList Empty"); } if (null == user) { return ResultDto.fail("Request User Is Null"); } else { if (users.contains(user)) { // 중복된 부분에 대해 ValidationException throw new ValidationException("name", "이름이 중복됩니다."); } users.add(user); return ResultDto.success(); } } | cs |
테스트 해보면 다음과 같이 중복코드에 대해서 체크가 정상적으로 되는 것을 알 수 있다.
물론 이렇게 RuntimeException을 만들지 않고 팩토리 객체를 만들어서 동일하게 실패 메시지를 만들어서 줘도 상관없다.
하지만 예측이 어려운 Runtime Exception을 잘 정의해놓으면 편하게 처리할 수 있다.
'web > Spring' 카테고리의 다른 글
Spring Boot application.properties 암호화 내역 복호화 방법 (0) | 2018.10.04 |
---|---|
Spring boot hibernate jpa에서 Auditing 사용 - update, create 시간 자동 변경 (3) | 2018.10.03 |
Spring Application Test 정리 (0) | 2018.08.07 |
Java 9 이후 deprecated된 Spring @PostConstruct와 @PreDestory 대안소개 (0) | 2018.08.07 |
Swagger 라이브러리를 사용하여 API 내용 정리하기 (0) | 2018.08.07 |