12. ๊ฒ์ฆ (Validation)
๐ค ์ฌ์ฉ์๊ฐ ์นํ์ด์ง์ ์ํ์ ๋ฑ๋กํ๊ณ ์์ ํ ์ ์๋ <์ํ ๊ด๋ฆฌ="" ์์คํ ="">์ ๊ฐ๋ฐํ๋ค๊ณ ๊ฐ์ ํด๋ณด์. ์ฌ๊ธฐ์ ๋ค์ํ ์๊ตฌ์ฌํญ์ด ์๊ฒผ๋ค.์ํ>
- ํ์ ๊ฒ์ฆ
- ๊ฐ๊ฒฉ, ์๋์ ๋ฌธ์๊ฐ ๋ค์ด๊ฐ๋ฉด ๊ฒ์ฆ ์ค๋ฅ ์ฒ๋ฆฌ
- ํ๋ ๊ฒ์ฆ
- ์ํ๋ช : ํ์, ๊ณต๋ฐฑ X
- ๊ฐ๊ฒฉ : 1,000์ ์ด์ ~ 1,000,000์ ์ดํ
- ์๋ : ์ต๋ 9,999๊ฐ๊น์ง ๋ฑ๋ก ๊ฐ๋ฅ
- ํน์ ํ๋์ ๋ฒ์๋ฅผ ๋์ด์๋ ๊ฒ์ฆ.
- ๊ฐ๊ฒฉ * ์๋ = 10,000์ ์ด์
์ข์ ์น ์๋น์ค๋ ์ด๋ฌํ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฐ์ ์ ์งํ ์ํ๋ก ์ด๋ค ์ค๋ฅ๊ฐ ๋ฐ์ํ๋์ง ์ฌ์ฉ์์๊ฒ ์น์ ํ ์๋ ค์ฃผ์ด์ผ ํ๋ค.
์ถ์ฒ: ๊น์ํ์ ์คํ๋งMVC (์ธํ๋ฐ)
์, ์ด์ ๊ฐ๋ฐํด๋ณด์ ๐๐ปโโ๏ธ
1๏ธโฃ ย BindingResult
- ์คํ๋ง์ด ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ด๋ค.
- ๊ฒ์ฆ ์ค๋ฅ๋ฅผ ๋ณด๊ดํ๋ ๊ฐ์ฒด๋ก, ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ค๋ฅ๋ด์ฉ์ ์ ์ฅํด์ค๋ค.
- BindingResult ๊ฐ ์์ผ๋ฉด @ModelAttribute ์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ์ ์ค๋ฅ๊ฐ ์์ด๋ ์ปจํธ๋กค๋ฌ๋ฅผ ํธ์ถํด์ค๋ค.
- ๋ค๋ง, @RequestBody ๋ก ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ํ๋ JSON ๋ฐ์ดํฐ์ ๊ฒฝ์ฐ ํ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ๋ชจ๋ธ ๊ฐ์ฒด์ ๋ฐ์ดํฐ๊ฐ ๋ด๊ธฐ์ง ์๊ณ , ์ปจํธ๋กค๋ฌ๋ฅผ ํธ์ถํ ์ ์๊ฒ ๋๋ค. ์ด ๊ฒฝ์ฐ๋ ์์ธ ์ฒ๋ฆฌ๋ก ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํด์ผ ํ๋๋ฐ ์ด๊ฒ์ ๋ค์ ๊ฐ์์์ ์์ธํ ์์๋ณด์.
2๏ธโฃ ย BindingResult - rejectValue(), reject() ๋ฉ์๋
- BindingResult ๋ rejectValue(), reject() ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค. ํ๋๋ช ๊ณผ ์๋ฌ์ฝ๋๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋๊ธฐ๋ฉด, ๊ทธ์ ํด๋นํ๋ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ธ๋ผ์ฐ์ ์ ์ถ๋ ฅํ ์ ์๋ค.
- rejectValue() : FieldError ๋ฅผ ๊ฒ์ฆํ๋ ๋ฉ์๋.
- reject() : ObjectError ๋ฅผ ๊ฒ์ฆํ๋ ๋ฉ์๋.
/* ๊ฒ์ฆ ๋ก์ง ์์ */
if (!StringUtils.hasText(item.getItemName())) {
//ItemName ์ด ๊ณต๋ฐฑ์ด๋ผ๋ฉด,
bindingResult.rejectValue("itemName", "required");
//itemName ํ๋์ required ์๋ฌ ์ฝ๋๋ฅผ bindingResult์ ๋ด๋๋ค.
}
//bindingResult ์ ์๋ฌ๊ฐ ๋ด๊ฒจ์๋ค๋ฉด,
if (bindingResult.hasErrors()) {
//์ฌ์ฉ์๊ฐ ์
๋ ฅํ ํผ์ผ๋ก ๋๋๋ ค๋ณด๋ธ๋ค.
return "validation/v2/addForm";
}
3๏ธโฃ ย errors.properties
- ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ฝ๊ฒ ๋ณ๋์ ํ์ผ๋ก ๊ด๋ฆฌํ์.
- ์ค๋ฅ ์ฝ๋๋ฅผ ๋ง๋ค ๋๋ ์์ธํ ๋ง๋ค ์ ์๊ณ , ๋จ์ํ๊ฒ ๋ง๋ค ์๋ ์๋ค.
#level1 - ์์ธํ ๋ง๋ค๊ธฐ (์๋ฌ๋ช
.object๋ช
.field๋ช
)
required.item.itemName=์ํ ์ด๋ฆ์ ํ์ ์
๋๋ค.
#level2 - ๋จ์ํ๊ฒ ๋ง๋ค๊ธฐ
required=ํ์ ๊ฐ ์
๋๋ค.
- ๋จ์ํ๊ฒ ๋ง๋ค๋ฉด ๋ฒ์ฉ์ฑ์ด ์ข์์ ์ฌ๊ธฐ์ ๊ธฐ์ ์ฌ์ฉํ ์ ์์ง๋ง ๋ฉ์์ง๋ฅผ ์ธ๋ฐํ๊ฒ ๋ง๋ค ์ ์๊ณ , ๋ฐ๋๋ก ๋๋ฌด ์์ธํ๊ฒ ๋ง๋ค๋ฉด ๋ฒ์ฉ์ฑ์ด ๋จ์ด์ง๋ค.
- ์คํ๋ง์ ์ธ๋ฐํ๊ฒ ์์ฑ๋ ์๋ฌ ์ฝ๋(level1)๋ฅผ ์ฐ์ ์ผ๋ก ์ ์ฉํ๊ธฐ ๋๋ฌธ์ ์ํฉ์ ๋ง๊ฒ ์์ฑํด๋๋ฉด ๋๋ค.
4๏ธโฃ ย ๊ฒ์ฆ ์ค๋ฅ๋ฅผ ์ ์ฉํ๋ 3๊ฐ์ง ๋ฐฉ๋ฒ
1. ์คํ๋ง์ด FieldError ๋ฅผ ์์ฑํด์ BindingResult ์ ์ง์ ๋ฃ์ด์ค๋ค.
2. ๊ฐ๋ฐ์๊ฐ ์ง์ ๋ฃ์ด์ค๋ค.
3. Validator ๋ฅผ ๋ถ๋ฆฌํ๋ค.
๊ฒ์ฆ ์๊ตฌ์ฌํญ ์ค <1๋ฒ. ํ์ ๊ฒ์ฆ>์์ ๊ฐ๊ฒฉ, ์๋์ ๋ฌธ์์ด์ ๋ฃ์ผ๋ฉด @ModelAttribute ๋ฅผ ํตํด Item ๊ฐ์ฒด์ ๋ฐ์ดํฐ๊ฐ ์ฃผ์ ๋์ง ์๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ๋ typeMismatch๋ก ์คํ๋ง์ด ์๋์ผ๋ก FieldError ๋ฅผ ์์ฑํด์ค๋ค.
๊ฒ์ฆ ์๊ตฌ์ฌํญ ์ค <3๋ฒ. ํน์ ํ๋์ ๋ฒ์๋ฅผ ๋์ด์๋ ๊ฒ์ฆ> ์ ObjectError ์ ํด๋นํ๋ ๊ธ๋ก๋ฒ ์ค๋ฅ๋ก, ๊ฐ๋ฐ์๊ฐ ์ง์ ์๋ฐ ์ฝ๋๋ก ์๋ฌ์ํฉ์ ์ ์ํ ์ดํ ์ง์ bindingResult ์ ์๋ฌ์ฝ๋๋ฅผ ๋ด๋๋ค.
/* ๊ฐ๋ฐ์๊ฐ ์ง์ ์ ์ํ๋ ๊ฒ์ฆ ๋ก์ง */
if (item.getPrice() != null && item.getQuantity() != null) {
int resultPrice = item.getPrice() * item.getQuantity();
if (resultPrice < 10000) {
bindingResult.reject("totalPriceMin", new Object[]{1000, resultPrice}, null);
}
}
- ํ์ง๋ง ์ด๋ฌํ ๊ฒ์ฆ ๋ก์ง์ด โ์ปจํธ๋กค๋ฌโ์ ๋ค์ด๊ฐ ์๋ ๊ฒ์ด ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค. ์ปจํธ๋กค๋ฌ์ ์ญํ ์ด ์ปค์ง๊ณ ์ ์ง๋ณด์์ฑ์ด ๋จ์ด์ง๊ธฐ ๋๋ฌธ์ Validator ๋ผ๋ ํด๋์ค๋ฅผ ๋ณ๋๋ก ๋ง๋ค์ด์ ๋ก์ง์ ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
public String addItemV5(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
//๊ฒ์ฆ ๋ก์ง์ ์ํ (ํ๊ฒ, BindingResult ๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋๊ธด๋ค)
itemValidator.validate(item, bindingResult);
//๊ฒ์ฆ์ ์คํจํ๋ฉด ๋ค์ ์
๋ ฅ ํผ์ผ๋ก
if (bindingResult.hasErrors()) {
log.info("errors = {}", bindingResult);
return "validation/v2/addForm";
}
//์ค๋ฅ๊ฐ ์๋ค๋ฉด ์ฑ๊ณต ๋ก์ง ์ํ
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/validation/v2/items/{itemId}";
}