๊ฒ์ฆ ๊ธฐ๋ฅ์ ๋งค๋ฒ BingdingResult์ฒ๋ผ ์ฝ๋๋ก ๊ตฌํํ๋๊ฑด ์ข ํ๋ค๋ค... ์ด๋ ๊ฒ ๋๋ฉด ์์ ํ๋ก์ ํธ๋ ๊ด์ฐฎ์ง๋ง, ์กฐ๊ธ๋ง ํ๋ก์ ํธ๊ฐ ์ปค์ ธ๋ Controller์ ํฌ๊ธฐ๊ฐ ๋๋ฌด๋๋ฌด ์ปค์ง๋ฉฐ ๋จ์ผ ์ฑ
์ ์์น์ ์๋ฐฐ๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋์จ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก โญ๏ธBean Validationโญ๏ธ
: ์ ์ฝ ์กฐ๊ฑด์ ์ค์ ํด ์ฌ๋ฐ๋ฅธ ๊ฐ์ ๊ฐ์ง๊ณ ์๋์ง ๊ฒ์ฆํ๋ ํ์คํ๋ ๋ฐฉ๋ฒ
์ ๋ชจ๋ฅผ๋ ์ธ์ ๋ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์!
๐ Hibernate Validator ๊ณต์ ๋ฌธ์
๋จผ์ ์์กด์ฑ์ ์ถ๊ฐํด์ผํ๋ค
//build.gradle
implementation 'org.springframework.boot:spring-boot-starter-validation'
| Annotation | ์กฐ๊ฑด |
|---|---|
@NotNull | null โ / ๊ณต๋ฐฑ(โ โ) โ / ๋น๊ฐ(โโ) โ / CharSequence ํ์ ํ์ฉ |
@NotBlank | null โ / ๋ชจ๋ ํ์ |
@NotEmpty | null โ / ๋น๊ฐ(โโ) โ / CharSequence, Collection, Map, Array ํ์ฉ |
@Range(min = 1, max = 120) ์ ๊ฐ์
Annotation์ ์ ์ฉ์ํค๋๊ฒ ๋ง์ผ๋ก Validation์ ํต์ฝ๊ฒ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค!!( ๊ฐธ๋ฐ)
@Data
public class TestDto{
@notBlank
private String stringField;
@NotNull
@Range(min = 1, max = 9999)
private Integer integerField;
๋จ์ํ Annotation์ ์ ์ธํด์ฃผ๋ฉด ๊ฒ์ฆ์ด ์๋ฃ๋๋ ์ด์ ๋ Validator๊ฐ ์กด์ฌํ๊ธฐ ๋๋ฌธ
Spring Boot๋ validation ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์ ํ๋ฉด
'org.springframework.boot:spring-boot-starter-validation' ์๋์ผ๋ก Bean Validator๋ฅผ
Spring์ ํตํฉ๋๋๋ก ์ค์ ํด์ค๋ค!
| Annotation | ํน์ง |
|---|---|
| @Valid | Java ํ์ค ๊ฒ์ฆ ์ด๋ ธํ ์ด์ |
| @Validated | Spring์์ ์ ๊ณตํ๋ ํ์ฅ ์ด๋ ธํ ์ด์ , Group Validation ์ง์ |
@Validated๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ๊ทธ๋ฃน์ ์ง์ ํ์ฌ ๊ฒ์ฆ์ ์ํ ๊ฐ๋ฅ!
Bean Validation์ ์ ์ฉํ ํ ๊ฒ์ฆ์ค๋ฅ ํ์ธํ๋ฉด ์ค๋ฅ๊ฐ ์ด๋
ธํ
์ด์
์ด๋ฆ์ผ๋ก ๋ฑ๋ก๋ ๊ฒ์ ๋ณผ ์ ์๋ฐ.
๊ทผ๋ฐ ๋๋ ๋ค๋ฅธ ๋ฉ์ธ์ง๋ฅผ ์ ๋ฌํ๊ณ ์ถ์ ์๋ ์์๋..?
๊ทธ๋ด๋ message ์์ฑ์ ์ฌ์ฉํ์
@Data
public class TestDto {
@NotBlank(message = "๋ฉ์ธ์ง ์์ ๊ฐ๋ฅ")
private String stringField;
}
๋ง์ฝ postํ ๋๋ ๊ฐ๊ฒฉ์ ๋ฒ์๊ฐ 100์ด์์ด์ด์ผํ๋๋ฐ, put(์์ )ํ ๋๋ ๊ฐ๊ฒฉ ๋ฒ์๊ฐ ์๊ด์์ด๋ ๋๋ค๋ฉด,, ๋ฒ์ ์ค์ ์ ๋ฐ๋ก ํด์ฃผ์ด์ผํ ๊ฒ์ด๋ค
์ด๋ ์ฌ์ฉํ ์ ์๋ ๊ฒ์ด ๋ฐ๋ก Spring์ groups ๊ธฐ๋ฅ๊ณผ DTO๋ฅผ ๋ฐ๋ก ๋ถ๋ฆฌํด์ฃผ๋ ๊ฒ์ด๋ค!
1. groups ์ฌ์ฉํ๊ธฐ
// ์ ์ฅ์ฉ group
public interface SaveCheck {
}
// ์์ ์ฉ group
public interface UpdateCheck {
}
@Data
public class ProductRequestDtoV2 {
// ์ ์ฅ, ์์ @NotBlank Validation ์ ์ฉ
@NotBlank(groups = {SaveCheck.class, UpdateCheck.class})
private String name;
// ์ฌ์ฉํ๋ ๋ชจ๋ ๊ณณ์์ @NotNull Validation ์ ์ฉ, ์ ์ฅ๋ง @Range ๋ฐ์
@NotNull
@Range(min = 10, max = 10000, groups = SaveCheck.class)
private Integer price;
...
@RestController
public class ProductController {
@PostMapping("/v2/product")
public String save(
// ์ ์ฅ ์์ฑ๊ฐ ์ค์
@Validated(SaveCheck.class) @ModelAttribute ProductReques
) {
log.info("์์ฑ API๊ฐ ํธ์ถ ๋์์ต๋๋ค.");
// Validation ์ฑ๊ณต์ repository ์ ์ฅ๋ก์ง ํธ์ถ
return "์ํ ์์ฑ์ด ์๋ฃ๋์์ต๋๋ค";
}
@PutMapping("/v2/product/{id}")
public String update(
@PathVariable Long id,
// ์์ ์์ฑ๊ฐ ์ค์
@Validated(UpdateCheck.class)
...
groups๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ๋ก ์ง์ ํด์ค ์ ์์ง๋ง, UpdateCheck, SaveCheck์ฒ๋ผ ๋ฐ๋ก ๋ง๋ค์ด์ ๊ฐ๊ฐ ์ ์ฉํด์ฃผ์ด์ผํ๊ธฐ ๋๋ฌธ์ ์ฝ๋์ ๊ฐ๋
์ฑ์ด ๋งค์ฐ๋งค์ฐ ๋จ์ด์ง๋คใ
ใ
๋ง์ฝ groups ๋ฅผ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด @Valid๊ฐ ์๋@Validated๋ฅผ ์ฌ์ฉํ๋๋ก ํ์ (groups๋ ์๋ฐ๊ฐ ์๋๋ผ ์คํ๋ง์ ๊ธฐ๋ฅ์ด๊ธฐ ๋๋ฌธ)
๋ ์ ์์ผ๋ฉด DTO๋ฅผ ๋ถ๋ฆฌํด์ ์ฌ์ฉํ๋ ๋ฐฉ์์ ์ฌ์ฉํ๋๋ก ํ์
2. ๐ DTO ๋ถ๋ฆฌ
๋ค์ด๋ฐ์ ์ผ๊ด์ฑ์๊ฒ ์์ฑํ๊ธฐ(SaveRequestDto, UpdateRequestDto)
์ฌ๋งํ๋ฉด DTO๋ฅผ ๋ถ๋ฆฌํ๋ ๋ฐฉํฅ์ผ๋ก ์์ฑํ๋๊ฒ ์ ์ผ ์ข๋ค!!
| ๊ตฌ๋ถ | @ModelAttribute | @RequestBody |
|---|---|---|
| ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ | ํผ ๋ฐ์ดํฐ (x-www-form-urlencoded, multipart/form-data) | JSON (application/json) |
| ๊ฒ์ฆ๋ฐฉ์ | BindingResult ์ฌ์ฉ ๊ฐ๋ฅ (FieldError, ObjectError ์ง์ ์ฒ๋ฆฌ ๊ฐ๋ฅ) | @Valid ๋๋ @RequestBody ๊ฒ์ฆ ์คํจ ์ ์์ธ ๋ฐ์ |
| ์ปจํธ๋กค๋ฌ ํธ์ถ ์ฌ๋ถ | ์ค๋ฅ ์์ด๋ ์ปจํธ๋กค๋ฌ ๋ฉ์๋ ์คํ | ๊ฒ์ฆ ์ค๋ฅ๊ฐ ์์ผ๋ฉด ์ปจํธ๋กค๋ฌ๊ฐ ์คํ๋์ง ์์ |
| ์ค๋ฅ ๋ด๋ ๊ณณ | BindingResult์ ์ ์ฅ | MethodArgumentNotValidException ๋ฐ์ |
| ์ค๋ฅ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ | bindingResult.hasErrors()๋ก ์ปจํธ๋กค๋ฌ์์ ์ง์ ์ฒ๋ฆฌ ๊ฐ๋ฅ | @ExceptionHandler ๋๋ ControllerAdvice๋ฅผ ์ฌ์ฉํ์ฌ ์์ธ ์ฒ๋ฆฌ ํ์ |
@RequestBody์ ๊ฒฝ์ฐ ์ค๋ฅ๊ฐ ์๊ธฐ๋ฉด ์ปจํธ๋กค๋ฌ๊ฐ ํธ์ถ๋์ง ์๋๋ค!
โ @RequestBody์ ๊ฒ์ฆ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๋ ค๋ฉด @ExceptionHandler ๋๋ @ControllerAdvice๊ฐ ํ์ํจ.
โ @ModelAttribute์ @RequestBody ๋ชจ๋ FieldError์ ObjectError๋ฅผ ๊ฐ์ง ์ ์์
FieldError
: ํน์ ํ๋(์์ฑ) ์ ๋ํ ๊ฒ์ฆ ์ค๋ฅ.
์ ์ฝ ์กฐ๊ฑด(@NotNull, @NotBlank, @Size ๋ฑ)์ ์๋ฐํ๋ฉด ๋ฐ์.
ObjectError
ํน์ ํ๋๊ฐ ์๋๋ผ ๊ฐ์ฒด ์ ์ฒด์ ๋ํ ์ค๋ฅ.
๋น์ฆ๋์ค ๋ก์ง ๊ฒ์ฆ์ ๋ง์ด ์ฌ์ฉ๋จ (์: ๋น๋ฐ๋ฒํธ ํ์ธ, ๋ ์ง ๊ฒ์ฆ ๋ฑ).
BindingResult.getGlobalErrors()๋ก ํ์ธ ๊ฐ๋ฅ.