๊ฒ์ฆ(Validation) ์ฒ๋ฆฌ ๋ฐฉ๋ฒ
by rlaehddnd0422๊ฒ์ฆ ์๊ตฌ์ฌํญ
์ํ ๊ด๋ฆฌ ์์คํ ์์ ์ํ ๋ฑ๋ก ํผ์ ์ ๋ ฅ๊ฐ์ ๋ํ ๊ฒ์ฆ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
ํ์ ๊ฒ์ฆ : ๊ฐ๊ฒฉ, ์๋์ ๋ฌธ์๊ฐ ๋ค์ด์ค๋ฉด ๊ฒ์ฆ ์ค๋ฅ ์ฒ๋ฆฌ
ํ๋ ๊ฒ์ฆ
- ์ํ๋ช : ํ์, ๊ณต๋ฐฑ X
- ๊ฐ๊ฒฉ : 1000์~1๋ฐฑ๋ง์ ์ดํ
- ์๋ : ์ต๋ 9999
ํน์ ํ๋์ ๋ฒ์๋ฅผ ๋์ด์๋ ๊ฒ์ฆ
- ๊ฐ๊ฒฉ*์๋ >= 10000์
- ์ปจํธ๋กค๋ฌ์ ์ค์ํ ์ญํ ์ค ํ๋๋ HTTP ์์ฒญ์ด ์ ์์ธ์ง ๊ฒ์ฆํ๋ ์ผ์ ๋๋ค.
- ๊ฒ์ฆ์๋ ํด๋ผ์ด์ธํธ ๊ฒ์ฆ, ์๋ฒ ๊ฒ์ฆ์ด ์๋๋ฐ ํด๋ผ์ด์ธํธ ๊ฒ์ฆ์ ์กฐ์์ด ๊ฐ๋ฅํด์ ๋ณด์์ ์ทจ์ฝํ๊ณ ,
- ์๋ฒ๋ง์ผ๋ก ๊ฒ์ฆํ๋ฉด, ์ฆ๊ฐ์ ์ธ ๊ณ ๊ฐ ์ฌ์ฉ์ฑ์ด ๋ถ์กฑํด์ง๋๋ค.
- ๋์ ์ ์ ํ ์ฌ์ฉํ๋, ์ต์ข ์ ์ธ ์๋ฒ๊ฒ์ฆ์ ํ์์ ์ ๋๋ค.
- API ๋ฐฉ์์ ์ฌ์ฉํ๋ฉด API ์คํ์ ์ ์ ์ํด์ ๊ฒ์ฆ ์ค๋ฅ๋ฅผ API ์๋ต ๊ฒฐ๊ณผ์ ์ ๋จ๊ฒจ์ฃผ์ด์ผ ํฉ๋๋ค.
@PostMapping("/add")์์ ์ํ ์ ์ฅ์ ์คํจํ๋ฉด Model์ ๊ฒ์ฆ ์ค๋ฅ ๊ฒฐ๊ณผ๋ฅผ ํฌํจํด์ ์ํ ๋ฑ๋ก ํผ์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ ํด์ค๋๋ค.
์ด๋ ๊ฒ ๊ฒ์ฆ์ ์คํจํ์ ๋, ๋ค์ ์ํ ๋ฑ๋ก ํผ์ ๋ณด์ฌ์ฃผ๊ณ , ์ค๋ฅ ๊ฒฐ๊ณผ๋ฅผ ํตํด ์ด๋ค ๊ฐ์ ์๋ชป์ ๋ ฅํ๋์ง ํด๋ผ์ด์ธํธ์๊ฒ ์๋ ค์ค๋๋ค.
์ด์ ๊ฒ์ฆ ๋ก์ง์ ์ง์ ๊ฐ๋ฐํด๋ณด๊ฒ ์ต๋๋ค.
๊ฒ์ฆ ์ฒ๋ฆฌ - ์ง์ ๊ฐ๋ฐ using Map
Controller
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes,
Model model) {
// ๊ฒ์ฆ ์ค๋ฅ ๊ฒฐ๊ณผ ๋ณด๊ด
Map<String,String> errors = new HashMap<>();
// ๊ฒ์ฆ ๋ก์ง - ์ํ๋ช
if(!StringUtils.hasText(item.getItemName()))
{
errors.put("itemName","์ํ ์ด๋ฆ์ ํ์์
๋๋ค.");
}
// ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
if(item.getPrice()==null || item.getPrice()<1000 || item.getPrice() > 1000000)
{
errors.put("price","๊ฐ๊ฒฉ์ 1,000 ~ 1,000,000์๊น์ง ํ์ฉํฉ๋๋ค.");
}
// ๊ฒ์ฆ ๋ก์ง - ์๋
if(item.getQuantity() == null || item.getQuantity() >= 9999)
{
errors.put("quantity","์๋์ ์ต๋ 9999๊น์ง ํ์ฉํฉ๋๋ค.");
}
// ๋ณตํฉ ๋ก์ง - ๊ฐ๊ฒฉ*์๋
if(item.getPrice() != null && item.getItemName()!=null)
{
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice<10000)
{
errors.put("globalError","๊ฐ๊ฒฉ * ์๋์ ํฉ์ 10,000์ ์ด์์ด์ด์ผ ํฉ๋๋ค. " +
"ํ์ฌ ๊ฐ = " + resultPrice + "์");
}
}
if(!errors.isEmpty())
{
log.info("Error message = {}",errors);
model.addAttribute("errors",errors);
return "validation/v1/addForm";
}
// ์ฑ๊ณต ๋ก์ง
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/validation/v1/items/{itemId}";
}
Logic
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes,
Model model) {
- Postํ ๋ ๋งคํ๋ ์์ฒญ ํ๋ผ๋ฏธํฐ ๊ธฐ๋ฐ์ผ๋ก @ModelAttribute Item item ์์ฑ
- โถ๏ธ item์ property ์ฐพ์ setter ์ฃผ์ ( ์คํ๋ง ๋ถํธ๊ฐ ์๋ํ )
// ๊ฒ์ฆ ์ค๋ฅ ๊ฒฐ๊ณผ ๋ณด๊ด
Map<String,String> errors = new HashMap<>();
- HashMap<String,value>์ ๊ฒ์ฆ ์ค๋ฅ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ดํฉ๋๋ค.
- <key,value> = < ์ค๋ฅ๊ฐ ๋ฐ์ํ ํ๋๋ช , ์ค๋ฅ ๋ฉ์์ง >
// ๊ฒ์ฆ ๋ก์ง - ์ํ๋ช
if(!StringUtils.hasText(item.getItemName()))
{
errors.put("itemName","์ํ ์ด๋ฆ์ ํ์์
๋๋ค.");
}
// ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
if(item.getPrice()==null || item.getPrice()<1000 || item.getPrice() > 1000000)
{
errors.put("price","๊ฐ๊ฒฉ์ 1,000 ~ 1,000,000์๊น์ง ํ์ฉํฉ๋๋ค.");
}
// ๊ฒ์ฆ ๋ก์ง - ์๋
if(item.getQuantity() == null || item.getQuantity() >= 9999)
{
errors.put("quantity","์๋์ ์ต๋ 9999๊น์ง ํ์ฉํฉ๋๋ค.");
}
// ๋ณตํฉ ๋ก์ง - ๊ฐ๊ฒฉ*์๋
if(item.getPrice() != null && item.getItemName()!=null)
{
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice<10000)
{
errors.put("globalError","๊ฐ๊ฒฉ * ์๋์ ํฉ์ 10,000์ ์ด์์ด์ด์ผ ํฉ๋๋ค. " +
"ํ์ฌ ๊ฐ = " + resultPrice + "์");
}
}
1. ๊ฒ์ฆ ๋ก์ง - ์ํ๋ช
- item ๊ฐ์ฒด์ Name์ด ๋น์ด์์ ๊ฒฝ์ฐ ๋งต์ itemName (= ์ค๋ฅ๋ฐ์ ํ๋๋ช ), ์ค๋ฅ ๋ฉ์์ง put
2. ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
- item ๊ฐ์ฒด์ ๊ฐ๊ฒฉ == null || <1000 || > 1000000 ์ด๋ฉด ๋งต์ price, ์ค๋ฅ๋ฉ์์ง put
3. ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
- item ๊ฐ์ฒด์ ์๋ == null || >= 9999 ๋ฉด ๋งต์ quantity, ์ค๋ฅ๋ฉ์์ง put
4. ๋ณตํฉ ๋ก์ง - ๊ฐ๊ฒฉ*์๋
- item ๊ฐ์ฒด์ ๊ฐ๊ฒฉ*์๋ < 10000 ์ด๋ฉด ๋งต์ globalError, ์ค๋ฅ๋ฉ์์ง put
if(!errors.isEmpty())
{
log.info("Error message = {}",errors);
model.addAttribute("errors",errors);
return "validation/v1/addForm";
}
// ์ฑ๊ณต ๋ก์ง
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/validation/v1/items/{itemId}";
๊ฒ์ฆ ๊ณผ์ (์คํจ)
์๋ฌ ๋งต์ด ๋น์ด์์ง ์์ผ๋ฉด ์ฆ, ์ค๋ฅ ๋ฉ์์ง๊ฐ ํ๋๋ผ๋ ์์ผ๋ฉด ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด
model์ errors(์๋ฌ ์ ๋ณด๊ฐ ๋ด๊ธด ๋งต)์ ๋ด์ addForm ๋ทฐ ํ ํ๋ฆฟ์ ์ฌ ํธ์ถํฉ๋๋ค.
( addForm์๋ ์๋ฌ ๋ฉ์์ง๋ฅผ ์ถ๊ฐํ๊ธฐ ์ํ ์์๋ฅผ ์ถ๊ฐ์ ์ผ๋ก ์ค์ ํด ์ฃผ์ด์ผ ๊ฒ ์ฃ ? )
๊ฒ์ฆ ๊ณผ์ (์ฑ๊ณต)
์ฑ๊ณต ๋ก์ง ์คํ ( repository ์ ์ฅ -> redirectAttribute์ itemId์ ๋ฆฌ๋ค์ด๋ ํธ status=true ์ค์ -> ์์ดํ ์์ธ ํผ์ผ๋ก ๋ฆฌ๋ค์ด๋ ์ )
addForm.html
<style>
.container {
max-width: 560px;
}
.field-error {
border-color: #dc3545;
color: #dc3545;
}
</style>
์ฐ์ class='field-error'๋ก ์ง์ ๋ ๊ฒฝ์ฐ ์๊น์ ๋ณ๊ฒฝํ๊ธฐ ์ํด style ํ๊ทธ์ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํด์ฃผ์์ด์.
<div th:if="${errors?.containsKey('globalError')}">
<p class="field-error" th:text="${errors['globalError']}">
๊ธ๋ก๋ฒ ์ค๋ฅ ๋ฉ์์ง</p>
</div>
์ฐ์ ๊ธ๋ก๋ฒ ์ค๋ฅ ๋ฉ์์ง๋ถํฐ ์ฒ๋ฆฌํด๋ณด๊ฒ ์ต๋๋ค.
th:if ๋ฌธ์ ์ด์ฉํด errors ๋งต์ด 'globalError'์ ๊ฐ๊ณ ์๋์ง ํ์ธํฉ๋๋ค.
๋ง์ฝ ์๋ค๋ฉด ํ์ํ๊ทธ๋ฅผ ์คํํ๊ณ ์๋ค๋ฉด ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ์ง ์์ต๋๋ค.
โ๏ธerrors ๋ค์ ?๋ฅผ ๋ถ์ธ errors?. ์ errors๊ฐ null์ธ ๊ฒฝ์ฐ NPE(NullPointerException)์ ๋ฐ์์ํค์ง ์๊ณ null์ ๋ฐํํ๋ ๋ฌธ๋ฒ์ ๋๋ค. th:if์์ null์ ์คํจ๋ก ์ฒ๋ฆฌ๋๋ฏ๋ก ์ค๋ฅ ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋์ง ์์ต๋๋ค.
<div>
<label for="itemName" th:text="#{label.item.itemName}">์ํ๋ช
</label>
<input type="text" id="itemName" th:field="*{itemName}"
th:class=" ${errors?.containsKey('itemName')} ? 'form-control field-error' : 'form-control' "
class="form-control" placeholder="์ด๋ฆ์ ์
๋ ฅํ์ธ์">
<div class="field-error" th:if="${errors?.containsKey('itemName')}" th:text="${errors['itemName']}"></div>
</div>
<div>
<label for="price" th:text="#{label.item.price}">๊ฐ๊ฒฉ</label>
<input type="text" id="price" th:field="*{price}"
th:classappend=" ${errors?.containsKey('price')} ? 'field-error' : _"
class="form-control" placeholder="๊ฐ๊ฒฉ์ ์
๋ ฅํ์ธ์">
<div class="field-error" th:if="${errors?.containsKey('price')}" th:text="${errors['price']}"></div>
</div>
<div>
<label for="quantity" th:text="#{label.item.quantity}">์๋</label>
<input type="text" id="quantity" th:field="*{quantity}" class="form-control"
th:class=" ${errors?.containsKey('quantity')} ? 'form-control field-error' : 'form-control' "
placeholder="์๋์ ์
๋ ฅํ์ธ์">
<div class="field-error" th:if="${errors?.containsKey('quantity')}" th:text="${errors['quantity']}"></div>
</div>
์ด์ ํ๋ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํด๋ณด๊ฒ ์ต๋๋ค.
- ํ๋ ์ค๋ฅ ์ฒ๋ฆฌ - th:class (์
๋ ฅ ํผ ์์ ์ ์ฉ)
- th:class๋ฅผ ํตํด errors ๋งต์ 'itemName'์ ํ๋ ์ค๋ฅ๊ฐ ์๋์ง ํ์ธํ๊ณ ์์ผ๋ฉด class๋ฅผ 'form-control', 'field-error'๋ก ์ค์ ํ๊ณ ์์ผ๋ฉด 'form-control'๋ก ์ค์ ํ์ต๋๋ค.
- th:classappend๋ฅผ ํตํด ์ฒ๋ฆฌํ ์๋ ์์ต๋๋ค ( price๋ th:classappend ๋ฅผ ์ฌ์ฉํด์ ์๋ฌ๋งต์ 'price' key๊ฐ ์์ผ๋ฉด field-error ํด๋์ค๋ฅผ append )
- ํ๋ ์ค๋ฅ ์ฒ๋ฆฌ - ๋ฉ์์ง
- ํ๋ ์ค๋ฅ ๋ฐ์ ์ th:if๋ฅผ ์ฌ์ฉํด ํด๋น ํ๋ ํค๊ฐ ์์ผ๋ฉด ํค์ ๋ํ value๋ฅผ ์ถ๋ ฅํ๋๋ก ์ค์
์ง์ ๊ฐ๋ฐํ ๊ฒ์ฆ ์ฒ๋ฆฌ
- ๊ฒ์ฆ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ ๋ ฅํผ(addForm)์ ๋ค์ ๋ณด์ฌ์ฃผ๋๋ก ์ค์ ํ์ต๋๋ค.
- ๊ฒ์ฆ ์ค๋ฅ ๋ฐ์์ ์ค๋ฅ ๋ฉ์์ง๋ค์ ํด๋ผ์ด์ธํธ์๊ฒ ํ์ํด์ฃผ์์ต๋๋ค.
- ๊ฒ์ฆ ์ค๋ฅ๊ฐ ๋ฐ์ํด๋ ๊ณ ๊ฐ์ด ์ ๋ ฅํ ๋ฐ์ดํฐ๊ฐ ๊ทธ๋๋ก ์ ์ง๋ฉ๋๋ค. How?
@GetMapping("/add")
public String addForm(Model model) {
model.addAttribute("item", new Item());
return "validation/v1/addForm";
}
์ต์ด ์ํ๋ฑ๋ก ํผ ์์ฒญ ์ Item์ ํ๋กํผํฐ๋ฅผ ์ค์ ํ์ง ์๊ณ ๋ชจ๋ธ์ ๋ด์์ฃผ์ด addForm์ผ๋ก ๋๊ฒจ์ฃผ์์์ต๋๋ค.
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes,
Model model) {
<input type="text" id="itemName" th:field="*{itemName}"
<input type="text" id="price" th:field="*{price}"
๊ฒ์ฆ ์ค๋ฅ ๋ฐ์ ์ addForm์์ @ModelAttribute Item item์ ํ๋กํผํฐ ์ฃผ์ ๋ ๊ฐ๋ค์ addForm์์ ํ๋๊ฐ์ผ๋ก ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๊ฒ์ฆ ์ค๋ฅ๊ฐ ๋ฐ์ํด๋ ๊ณ ๊ฐ์ด ์ ๋ ฅํ ๋ฐ์ดํฐ๊ฐ ๊ทธ๋๋ก ์ ์ง๋ฉ๋๋ค.
๋จ์ ๋ฌธ์ ์
- ๋ทฐํ ํ๋ฆฟ์์์ ์ค๋ณต์ฒ๋ฆฌ๊ฐ ๋ง์.
- ํ์ ์ค๋ฅ ์ฒ๋ฆฌ
- ํด๋ผ์ด์ธํธ๊ฐ ํ์ ์ค๋ฅ๊ฐ ์ ๋ ฅ ์ ( ex) price์ ๋ฌธ์์ ๋ ฅ ) ์ ๋ ฅํ ๊ฐ ๋ณ๋ ๊ด๋ฆฌ ํ์
BindingResult๋ฅผ ์ฌ์ฉํ ๊ฒ์ฆ ์ฒ๋ฆฌ
์ง๊ธ๋ถํฐ๋ ์คํ๋ง์ด ์ ๊ณตํ๋ ๊ฒ์ฆ ์ค๋ฅ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ์ ์์๋ณด๊ฒ ์ต๋๋ค.
Controller
@PostMapping("/add")
public String addItemV1(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
// ๊ฒ์ฆ ๋ก์ง - ์ํ๋ช
if(!StringUtils.hasText(item.getItemName()))
{
bindingResult.addError(new FieldError("item","itemName","์ํ ์ด๋ฆ์ ํ์์
๋๋ค."));
}
// ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
if(item.getPrice()==null || item.getPrice()<1000 || item.getPrice() > 1000000)
{
bindingResult.addError(new FieldError("item","price","๊ฐ๊ฒฉ์ 1,000 ~ 1,000,000๊น์ง ํ์ฉํฉ๋๋ค."));
}
// ๊ฒ์ฆ ๋ก์ง - ์๋
if(item.getQuantity() == null || item.getQuantity() >= 9999)
{
bindingResult.addError(new FieldError("item","quantity","์๋์ ์ต๋ 9,999๊น์ง ํ์ฉํฉ๋๋ค."));
}
if(item.getPrice() != null && item.getItemName()!=null)
{
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice<10000)
{
bindingResult.addError(new ObjectError("item","๊ฐ๊ฒฉ * ์๋์ ํฉ์ 10,000์ ์ด์์ด์ด์ผ ํฉ๋๋ค." +
"ํ์ฌ ๊ฐ = " + resultPrice));
}
}
if(bindingResult.hasErrors())
{
log.info("Error message = {}",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}";
}
BindingResult๋ ์ธํฐํ์ด์ค๋ก ์ค์ ๋ก ๋์ด์ค๋ ๊ตฌํ์ฒด๋ BeanPropertyBindingResult๋ฅผ ์ฌ์ฉํฉ๋๋ค.
BindingResult๋์ Errors๋ฅผ ์ฌ์ฉํด๋ ๋์ง๋ง Errors ์ธํฐํ์ด์ค๋ ๋จ์ํ ์ค๋ฅ ์ ์ฅ๊ณผ ์กฐํ ๊ธฐ๋ฅ๋ง ์ ๊ณตํ๊ณ , BindingReuslt๋ ์ฌ๊ธฐ์ ๋ํด์ ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. addError()๋ BindingResult๊ฐ ์ ๊ณตํฉ๋๋ค.
๋์ ์๋ฆฌ
@ModelAttribute์ ๊ฒ์ฆ ์ค๋ฅ ๋ฐ์ ์ bindingResult์ ํ๋ ์ค๋ฅ๋ FieldError๋ฅผ, ๊ธ๋ก๋ฒ ์ค๋ฅ๋ ObjectError๋ฅผ ๋ฃ์ด์ค๋๋ค.
+ BindingResult๋ Model์ ์๋์ผ๋ก ํฌํจ๋ฉ๋๋ค.
โ๏ธBindingResult bindingResult์ ํ๋ผ๋ฏธํฐ ์์น๋ @ModelAttribute Item item ๋ค์์ ์์ผ ํฉ๋๋ค.
๊ธ๋ก๋ฒ ์ค๋ฅ - ObjectError
ObjectError์ ํ๋ผ๋ฏธํฐ
- objectName : @ModelAttribute์ ์ด๋ฆ
- defaultMesage : ์ค์ ํ ๊ธฐ๋ณธ ์ค๋ฅ ๋ฉ์์ง
Controller
if(item.getPrice() != null && item.getItemName()!=null)
{
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice<10000)
{
bindingResult.addError(new ObjectError("item","๊ฐ๊ฒฉ * ์๋์ ํฉ์ 10,000์ ์ด์์ด์ด์ผ ํฉ๋๋ค." +
"ํ์ฌ ๊ฐ = " + resultPrice));
}
}
ํน์ ํ๋๋ฅผ ๋์ด์๋ ์ค๋ฅ๊ฐ ์์ผ๋ฉด ObjectError ๊ฐ์ฒด๋ฅผ ์์ฑํด์ bindingResult์ ๋ด์๋๋ฉด ๋ฉ๋๋ค.
addForm
<div th:if="${#fields.hasGlobalErrors()}">
<p class="field-error" th:each="err : ${#fields.globalErrors()}"
th:text="${err}">๊ธ๋ก๋ฒ ์ค๋ฅ ๋ฉ์์ง</p>
</div>
ํ์๋ฆฌํ๋ ์คํ๋ง์ BindingResult๋ฅผ ํ์ฉํด์ ํธ๋ฆฌํ๊ฒ ๊ฒ์ฆ ์ค๋ฅ๋ฅผ ํํํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
- #fields : #fields๋ก BindingResult๊ฐ ์ ๊ณตํ๋ ๊ฒ์ฆ ์ค๋ฅ์ ์ ๊ทผํ ์ ์์ต๋๋ค.
- th:errors : ํด๋น ํ๋์ ์ค๋ฅ๊ฐ ์๋ ๊ฒฝ์ฐ์ ํ๊ทธ๋ฅผ ์ถ๋ ฅํฉ๋๋ค. ( th:if์ ํธ์ ๋ฒ์ )
- th:errorclass : th:field์์ ์ง์ ํ ํ๋์ ์ค๋ฅ๊ฐ ์์ผ๋ฉด class ์ ๋ณด๋ฅผ ์ถ๊ฐํฉ๋๋ค.
ํ๋ ์ค๋ฅ - FieldError
- objectName : @ModelAttribute ์ด๋ฆ
- field : ์ค๋ฅ๊ฐ ๋ฐ์ํ ํ๋ ์ด๋ฆ
- defaultMessage : ๊ธฐ๋ณธ ์ค์ ํ ์ค๋ฅ ๋ฉ์์ง
- + rejectedValue : ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฑฐ์ ๋ ๊ฐ
- + bindingFailure : ํ์ ์ค๋ฅ ๊ฐ์ ๋ฐ์ธ๋ฉ ์คํจ์ธ์ง, ๊ฒ์ฆ ์คํจ์ธ์ง ๊ตฌ๋ถ ๊ฐ
- + codes : ๋ฉ์์ง ์ฝ๋
- + arguments : ๋ฉ์์ง์์ ์ฌ์ฉํ๋ ์ธ์
๋ค์ ๋ค๊ฐ์ ํ๋ผ๋ฏธํฐ๋ ๋ค์์ ๋ค๋ฃจ๋๋ก ํ๊ฒ ์ต๋๋ค.
// ๊ฒ์ฆ ๋ก์ง - ์ํ๋ช
if(!StringUtils.hasText(item.getItemName()))
{
bindingResult.addError(new FieldError("item","itemName","์ํ ์ด๋ฆ์ ํ์์
๋๋ค."));
}
// ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
if(item.getPrice()==null || item.getPrice()<1000 || item.getPrice() > 1000000)
{
bindingResult.addError(new FieldError("item","price","๊ฐ๊ฒฉ์ 1,000 ~ 1,000,000๊น์ง ํ์ฉํฉ๋๋ค."));
}
// ๊ฒ์ฆ ๋ก์ง - ์๋
if(item.getQuantity() == null || item.getQuantity() >= 9999)
{
bindingResult.addError(new FieldError("item","quantity","์๋์ ์ต๋ 9,999๊น์ง ํ์ฉํฉ๋๋ค."));
}
์ํ๋ช ๊ฒ์ฆ ๋ก์ง์ ์๋ฅผ๋ค๋ฉด,
objectName = "item", field = "itemName", defaultMessage = "์ํ ์ด๋ฆ์ ํ์์ ๋๋ค" ๋ก ์ง์ ํ๋๋ฐ,
if์กฐ๊ฑด์ ๋ง์กฑํ๋ฉด objectName์ @ModelAttribute Item "item"์ผ๋ก, "itemName"์ ํ๋์ด๋ฆ์ผ๋ก, "์ํ ์ด๋ฆ์ ํ์์ ๋๋ค"๋ฅผ ๊ธฐ๋ณธ ์ค๋ฅ ๋ฉ์์ง๋ก ์ค์ ํด์ bindingResult์ ๋ํด์ฃผ์์ต๋๋ค.
@ModelAttribute์ ๋ฐ์ธ๋ฉ ์ ํ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด
BindingResult๊ฐ ์์ผ๋ฉด โฃ @ModelAttribute์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ค๋ฅ ์ ๋ณด๋ฅผ BindingResult์ ๋ด์์ ์ปจํธ๋กค๋ฌ๋ฅผ ํธ์ถํฉ๋๋ค.
BindingResult๊ฐ ์์ผ๋ฉด โฃ 400 ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด์ ์ปจํธ๋กค๋ฌ๊ฐ ํธ์ถ๋์ง ์๊ณ , ์ค๋ฅ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.
์ฆ BindingResult๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ ์ค๋ฅ๋ ์คํ๋ง์ด ์๋์ผ๋ก BindingResult์ FieldError๋ฅผ ๋ฃ์ด์ค๋๋ค.
BindingResult๋ฅผ ์ฌ์ฉํด ๊ฒ์ฆ ์ค๋ฅ๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ธ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
1. ์์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๊ฐ๋ฐ์๊ฐ ์ง์ ๋ฃ์ด์ค๋ค.(ํ๋ ์ค๋ฅ, ๊ธ๋ก๋ฒ ์ค๋ฅ)
2. @ModelAttribute์ ๊ฐ์ฒด์ ํ์ ์ค๋ฅ ๋ฑ์ผ๋ก ๋ฐ์ธ๋ฉ์ด ์คํจํ๋ ๊ฒฝ์ฐ ์คํ๋ง์ด FieldError ์์ฑํด์ BindingResult์ ๋ฃ์ด์ค๋๋ค.
3. Validator ์ฌ์ฉ
<์ ๋ฆฌ>
BindingResult, FieldError, ObjectError๋ฅผ ์ฌ์ฉํด์ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด์์ต๋๋ค.
ํ์ง๋ง ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ๊ณ ๊ฐ์ด ์ ๋ ฅํ ๋ด์ฉ์ด ๋ชจ๋ ์ฌ๋ผ์ง๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด๋ณด๊ฒ ์ต๋๋ค.
FieldError, ObjectError ์ถ๊ฐ ํ๋ผ๋ฏธํฐ ์ค์
FieldError๋ ์ค๋ฅ ๋ฐ์์ ์ฌ์ฉ์ ์ ๋ ฅ ๊ฐ์ ์ ์ฅํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
@PostMapping("/add")
public String addItemV2(@ModelAttribute Item item, BindingResult bindingResult,
RedirectAttributes redirectAttributes)
{
if(!StringUtils.hasText(item.getItemName()))
{
bindingResult.addError(
new FieldError("item",
"itemName",
item.getItemName(),
false,
null,
null,
"์ํ ์ด๋ฆ์ ํ์์
๋๋ค."));
}
// ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
if(item.getPrice()==null || item.getPrice()<1000 || item.getPrice() > 1000000)
{
bindingResult.addError(
new FieldError("item",
"price",
item.getPrice(),
false,
null,
null,
"๊ฐ๊ฒฉ์ 1,000 ~ 1,000,000๊น์ง ํ์ฉํฉ๋๋ค."));
}
// ๊ฒ์ฆ ๋ก์ง - ์๋
if(item.getQuantity() == null || item.getQuantity() >= 9999)
{
bindingResult.addError(
new FieldError("item",
"quantity",
item.getQuantity(),
false,
null,
null,
"์๋์ ์ต๋ 9,999๊น์ง ํ์ฉํฉ๋๋ค."));
}
if(item.getPrice() != null && item.getItemName()!=null)
{
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice<10000)
{
bindingResult.addError(
new ObjectError("item",
null,
null,
"๊ฐ๊ฒฉ * ์๋์ ํฉ์ 10,000์ ์ด์์ด์ด์ผ ํฉ๋๋ค. ํ์ฌ ๊ฐ = " + resultPrice));
}
}
if(bindingResult.hasErrors())
{
log.info("Error message = {}",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}";
}
FiledError๋ ๋๊ฐ์ง ์์ฑ์๋ฅผ ์ง์ํฉ๋๋ค.
- objectName : @ModelAttribute ์ด๋ฆ
- field : ์ค๋ฅ๊ฐ ๋ฐ์ํ ํ๋ ์ด๋ฆ
- defaultMessage : ๊ธฐ๋ณธ ์ค์ ํ ์ค๋ฅ ๋ฉ์์ง
- + rejectedValue : ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฑฐ์ ๋ ๊ฐ
- + bindingFailure : ํ์ ์ค๋ฅ ๊ฐ์ ๋ฐ์ธ๋ฉ ์คํจ์ธ์ง, ๊ฒ์ฆ ์คํจ์ธ์ง ๊ตฌ๋ถ ๊ฐ
- + codes : ๋ฉ์์ง ์ฝ๋ ( defalutMessgae ๋์ฒดํ ํ ๋ ์ฌ์ฉ, ๋ค์ ์ค๋ช )
- + arguments : ๋ฉ์์ง์์ ์ฌ์ฉํ๋ ์ธ์ ( defalutMessgae ๋์ฒดํ ํ ๋ ์ฌ์ฉ, ๋ค์ ์ค๋ช )
if(!StringUtils.hasText(item.getItemName()))
{
bindingResult.addError(
new FieldError("item",
"itemName",
item.getItemName(),
false,
null,
null,
"์ํ ์ด๋ฆ์ ํ์์
๋๋ค."));
}
// ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
if(item.getPrice()==null || item.getPrice()<1000 || item.getPrice() > 1000000)
{
bindingResult.addError(
new FieldError("item",
"price",
item.getPrice(),
false,
null,
null,
"๊ฐ๊ฒฉ์ 1,000 ~ 1,000,000๊น์ง ํ์ฉํฉ๋๋ค."));
}
// ๊ฒ์ฆ ๋ก์ง - ์๋
if(item.getQuantity() == null || item.getQuantity() >= 9999)
{
bindingResult.addError(
new FieldError("item",
"quantity",
item.getQuantity(),
false,
null,
null,
"์๋์ ์ต๋ 9,999๊น์ง ํ์ฉํฉ๋๋ค."));
}
rejectedValue๋ ๋ฐ๋ก ์ค๋ฅ ๋ฐ์ ์ ์ฌ์ฉ์ ์ ๋ ฅ ๊ฐ์ ์ ์ฅํ๋ ํ๋์ ๋๋ค.
ํ์๋ฆฌํ์ ์ฌ์ฉ์ ์ ๋ ฅ ๊ฐ ์ ์ง
- th:field="*{price}"
- ํ์๋ฆฌํ์ th:field๋ ๋งค์ฐ ๋๋ํ๊ฒ ๋์ํ๋๋ฐ, ์ ์ ์ํฉ์๋ ๋ชจ๋ธ ๊ฐ์ฒด์ ๊ฐ์ ์ฌ์ฉํ์ง๋ง, ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด FieldError์์ ๋ณด๊ดํ ๊ฐ์ ์ฌ์ฉํด์ ๊ฐ์ ์ถ๋ ฅํฉ๋๋ค.
์คํ๋ง์ ๋ฐ์ธ๋ฉ ์ค๋ฅ ์ฒ๋ฆฌ
- ํ์ ์ค๋ฅ๋ก ๋ฐ์ธ๋ฉ์ ์คํจํ๋ฉด ์คํ๋ง์ FieldError๋ฅผ ์์ฑํ๋ฉด์ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฐ์ ์๋์ผ๋ก ๋ฃ์ด์ค๋๋ค!
- ๊ทธ๋ฆฌ๊ณ ํด๋น ์ค๋ฅ๋ฅผ BindingResult ์ ๋ด์์ ์ปจํธ๋กค๋ฌ๋ฅผ ํธ์ถํฉ๋๋ค. ๋ฐ๋ผ์ ํ์ ์ค๋ฅ ๊ฐ์ ๋ฐ์ธ๋ฉ ์คํจ์์๋ ์ฌ์ฉ์์ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์ ์์ ์ผ๋ก ์ถ๋ ฅํ ์ ์์ต๋๋ค.
<์ฐธ๊ณ ์๋ฃ>
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard
'๐ Backend > MVC Pattern' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Bean Validation - ๊ฒ์ฆ ์ด๋ ธํ ์ด์ ์ฌ์ฉ (0) | 2023.03.22 |
---|---|
์ค๋ฅ ์ฝ๋์ ๋ฉ์์ง ์ฒ๋ฆฌ (0) | 2023.03.21 |
๋ฉ์์ง, ๊ตญ์ ํ ๊ธฐ๋ฅ (0) | 2023.03.20 |
Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋ฅ - ์๋ต ๋งคํ @ResponseBody, MessageConverter, ์์ฒญ ๋งคํ ํธ๋ค๋ฌ ์ด๋ํฐ ๊ตฌ์กฐ (0) | 2023.03.13 |
Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋ฅ - ์์ฒญ ๋งคํ @RequestMapping, @RequestParam (0) | 2023.03.12 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422