validation이란 프로그래밍에 있어서 가장 필요한 부분이다.
Java/Kotlin 에서는 null값에 대해 접근하려고 할 때, null point exception이 발생한다. 이런 부분을 방지하기 위해 미리 검증하는 과정을 Validation이라고 함
Size | 문자길이측정 | Future | 미래날짜 |
---|---|---|---|
NotNull | null 불가 | FutureOrPresent | 오늘이거나 미래 |
NotEmpty | null, ‘’ 불가 | Pattern | 정규식 |
NotBlank | null,’’, ‘ ‘ 불가 | Max | 최대값 |
Past | 과거 날짜 | Min | 최소값 |
PastOrPresent | 오늘이나 과거날짜 | Valid | object validation |
우선 validation을 적용 할 곳을 고민해보자. 나는 현재 시작하는 프로젝트에 간단하게 적용 해 보려고한다. 회원가입하는 곳에서 사용자 정보를 가지고왔을 때 체크 해주는 부분을 작성해보고자한다
package com.wool.modulink.dto.user; public class User { private String name; private String password; private String email; private String phone; private int age; // getter, setter, toString }
package com.wool.modulink.controller.user; import com.wool.modulink.dto.user.User; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/auth") public class AuthController { @PostMapping("/user") public User user(@RequestBody User user) { System.out.println(user); return user; } }
{ "name":"test", "password":"1234", "email":"paullee@mail.com", "phone":"01012341234", "age":1000 }
기존에는 아래와같이 if문을 사용해서 컨트롤러 내부나 서비스로직 내부에서 검사를했다
package com.wool.modulink.controller.user; import com.wool.modulink.dto.user.User; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/auth") public class AuthController { @PostMapping("/user") public ResponseEntity user(@RequestBody User user) { System.out.println(user); if(user.getAge() > 200){ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(user); } if(user.getEmail().contains("@")){ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(user); } return ResponseEntity.ok(user); } }
이제 위의 부분을 좀 더 Spring이 제공하는 방법들로 이쁘고 간편하게 바꿔보려고한다
기본적으로 제공되는 Validation들이 적용되기 위해서는 Valid 하고자하는 곳의 데이터에 어노테이션이 적용되어야한다.
기본적인 컨트롤러에 우선 Valid를 붙이고 다음으로 넘어가자
@PostMapping("/user") public ResponseEntity user(@Valid @RequestBody User user) { // ... }
이메일은 DTO에서 이메일을 담는 변수에 @Email
어노테이션을 사용 해 주면 된다.
그리고 해당하는 어노테이션이 동작하기 위해서는, 컨트롤러의 RequestBody 앞쪽에 @Valid
어노테이션으로 “검증을 할 것이다” 라고 스프링에게 알려주어야 한다
import javax.validation.constraints.Email; public class User { private String name; private String password; @Email private String email; private String phone; private int age; }
package com.wool.modulink.controller.user; import com.wool.modulink.dto.user.User; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @RestController @RequestMapping("/auth") public class AuthController { @PostMapping("/user") public ResponseEntity user(@Valid @RequestBody User user) { System.out.println(user); } }
이부분은 @Pattern 어노테이션
의 regexp 옵션을 사용해서 정규식을 사용하는 모든 곳에 적용 할 수 있다
package com.wool.modulink.dto.user; import javax.validation.constraints.Email; import javax.validation.constraints.Pattern; public class User { private String name; private String password; @Email private String email; @Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$",message = "핸드폰 번호의 양식과 맞지 않습니다. 01x-xxx(x)-xxxx") private String phone; private int age; }
숫자가 들어오는 경우 나이는 1살 이상, 100살 이하로 제한을 둘 수 있다.
각각의 상황에 맞게 max/min 값을 지정 해 주어야 한다
public class User { private String name; private String password; @Email private String email; @Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$",message = "핸드폰 번호의 양식과 맞지 않습니다. 01x-xxx(x)-xxxx") private String phone; @Min(value = 0,message = "적절한 연령을 입력해주세요.") @Max(value = 150,message = "적절한 연령을 입력해주세요.") private int age;
validation을 한번에 모아받는 친구가 존재한다. BindingResult
라는 친구인데, validation에서 실패한 모든값을 들고있다. 반복문을 통해 error 메시지를 뽑아 줄 수 있다.
public ResponseEntity user(@Valid @RequestBody User user, BindingResult bindingResult) { if(bindingResult.hasErrors()) { StringBuilder sb = new StringBuilder(); bindingResult.getAllErrors().forEach(objectError -> { FieldError field = (FieldError) objectError; String message = objectError.getDefaultMessage(); System.out.println(field.getField() + ": " + message); sb.append("field: " + field.getField()); sb.append("message: " + message); }); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb.toString()); } } }
{ "name":"test", "password":"1234", "email":"paulleeemail.com", "phone":"01012341234", "age":1000 }
위의 데이터를 전송했을 때, 콘솔창에 아래와 같이 나온다
phone: 핸드폰 번호의 양식과 맞지 않습니다. 01x-xxx(x)-xxxx age: 적절한 연령을 입력해주세요. email: 올바른 형식의 이메일 주소여야 합니다
API return값도 잘 꾸며주도록 나중에 작업 하면 좋을 것 같다.