์ค๋ฅ ์ฝ๋์ ๋ฉ์์ง ์ฒ๋ฆฌ
by rlaehddnd0422์ค๋ฅ ์ฝ๋์ ๋ฉ์์ง ์ฒ๋ฆฌ by ๋ฉ์์ง ํ์ผ ์ฒ๋ฆฌ
FieldError์ ObjectError์ ์์ฑ์์๋ codes์ argument๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด ํ๋ผ๋ฏธํฐ๋ ์ค๋ฅ ๋ฐ์์ ์ค๋ฅ ์ฝ๋๋ก ๋ฉ์์ง๋ฅผ ์ฐพ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ผ ํ๊ตฐ๋ฐ์ ๋ชจ์ผ๊ณ ์ค๋ฅ์ฝ๋๋ฅผ ์ด์ฉํด์ ์์ ๋ฐฐ์ด ๋ฉ์์ง ์ฒ๋ฆฌ๋ฅผ ์์ฉํด ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
errors ๋ฉ์์ง ํ์ผ ์์ฑ
errors.properties
required.item.itemName=์ํ ์ด๋ฆ์ ํ์์
๋๋ค.
range.item.price=๊ฐ๊ฒฉ์ {0} ~ {1} ๊น์ง ํ์ฉํฉ๋๋ค.
max.item.quantity=์๋์ ์ต๋ {0} ๊น์ง ํ์ฉํฉ๋๋ค.
totalPriceMin=๊ฐ๊ฒฉ * ์๋์ ํฉ์ {0}์ ์ด์์ด์ด์ผ ํฉ๋๋ค. ํ์ฌ ๊ฐ = {1}
application.properties์ ๋ฑ๋ก
spring.messages.basename=messages,errors
Controller ์์ - Field ๊ฒ์ฆ
// ๊ฒ์ฆ ๋ก์ง - ์ด๋ฆ
if(!StringUtils.hasText(item.getItemName()))
{
bindingResult.addError(new FieldError("item","itemName",item.getItemName(),false,
new String[]{"required.item.itemName"},null,null));
}
// ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
if(item.getPrice()==null || item.getPrice()<1000 || item.getPrice() > 1000000)
{
bindingResult.addError(new FieldError("item","itemName",item.getPrice(),false,
new String[]{"range.item.price"},new Object[]{1000,100000},null));
}
// ๊ฒ์ฆ ๋ก์ง - ์๋
if(item.getQuantity() == null || item.getQuantity() >= 9999)
{
bindingResult.addError(new FieldError("item","quantity",item.getQuantity(),false,
new String[]{"max.item.quantity"},new Object[]{9999},null));
}
์ถ๊ฐ ํ๋ผ๋ฏธํฐ์ธ codes์ argument๋ฅผ ์ด์ฉํด ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ฑ๋กํ ์ ์์ต๋๋ค.
- codes : required.item.itemName์ ์ฌ์ฉํด์ ๋ฉ์์ง ์ฝ๋๋ฅผ ์ง์ ํ์ต๋๋ค.
- ๋ฉ์์ง ์ฝ๋๋ ํ๋๊ฐ ์๋๋ผ ๋ฐฐ์ด๋ก ์ฌ๋ฌ๊ฐ์ ์ ๋ฌํ ์ ์๋๋ฐ, ์์๋๋ก ๋งค์นญํด์ ์ฒ์ ๋งค์นญ๋๋ ๋ฉ์์ง๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- argument : Object[]{1000,10000}์ ์ฌ์ฉํด์ ์ฝ๋์ {0},{1}๋ก ์นํํ ๊ฐ์ ์ ๋ฌํ๋ ์ญํ ์ ํฉ๋๋ค.
Controller ์์ - ๋ณตํฉ Field ๊ฒ์ฆ
if(item.getPrice() != null && item.getItemName()!=null)
{
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice<10000)
{
bindingResult.addError(new ObjectError("item",new String[]{"totalPriceMin"},new Object[]{10000,resultPrice},null));
}
}
์ค๋ฅ ์ฝ๋์ ๋ฉ์์ง ์ฒ๋ฆฌ by reject(), rejectValue()
์ด๋ฒ์๋ bindingResult์ FieldError๋ ObjectError๋ฅผ addErrorํ์ง ์๊ณ ์ข ๋ ์๋ํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค.
- Bindingresult๋ addError๋ฟ๋ง ์๋๋ผ rejectValue() (FieldError์ ๊ฒฝ์ฐ ์ฌ์ฉ), reject() (ObjectError์ ๊ฒฝ์ฐ ์ฌ์ฉ) ๋ฉ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์ด rejectValue(), reject() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด FieldError๋ ObjectError๋ฅผ ์ง์ ์์ฑํ์ง ์๊ณ ๊น๋ํ๊ฒ ๊ฒ์ฆ ์ค๋ฅ๋ฅผ ๋ค๋ฃฐ ์ ์์ต๋๋ค.
if(!StringUtils.hasText(item.getItemName()))
{
bindingResult.rejectValue("itemName","required", null, null);
}
// ๊ฒ์ฆ ๋ก์ง - ๊ฐ๊ฒฉ
if(item.getPrice()==null || item.getPrice()< 1000 || item.getPrice() > 1000000)
{
bindingResult.rejectValue("price","range", new Object[]{1000,1000000}, null );
}
// ๊ฒ์ฆ ๋ก์ง - ์๋
if(item.getQuantity() == null || item.getQuantity() >= 9999)
{
bindingResult.rejectValue("quantity","max", new Object[]{9999},null);
}
if(item.getPrice() != null && item.getItemName()!=null)
{
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice<10000)
{
bindingResult.reject("totalPriceMin",new Object[]{10000,resultPrice},null);
}
}
errors.properties์ ์๋ ๋ฉ์์ง ์ฝ๋๋ฅผ ์ง์ ์ ๋ ฅํ์ง ์์๋๋ฐ ์ด๋ป๊ฒ ๋ ๊ฒ์ผ๊น์??
์ฐ์ rejectValue()๋ ํ๋ผ๋ฏธํฐ๋ก ๋ค๊ฐ์ง๋ฅผ ์ ๋ ฅ๋ฐ์ต๋๋ค.
1) field : ์ค๋ฅ ํ๋๋ช
2) errorCode : ์ค๋ฅ ์ฝ๋ ( ๋ฉ์์ง ํ์ผ์ ๋ฑ๋ก๋ ์ฝ๋๊ฐ ์๋ messageResolver๋ฅผ ์ํ ์ค๋ฅ ์ฝ๋๋ก ๋ค์์ ๋ค๋ฃจ๋๋ก ํ๊ฒ ์ต๋๋ค. )
3) errorArgs : ์ค๋ฅ ๋ฉ์์ง์์ {}์ ์นํํ๊ธฐ ์ํ ๊ฐ
4) defaultMessage : ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์ฐพ์ ์ ์์ ๋ ์ฌ์ฉํ๋ ๊ธฐ๋ณธ ๋ฉ์์ง
BindingResult๋ ์ด๋ค ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ๊ฒ์ฆํ๋์ง ์ด๋ฏธ @ModelAttribute๋ฅผ ํตํด ์๊ณ ์์ต๋๋ค. ๋ฐ๋ผ์ ์ฌ์ค objectName์ ์ ๋ ฅ๋ฐ์ง ์์๋ BindingResult๋ ์๊ณ ์์ต๋๋ค.
reject, rejectValue()๋ ๊ฐ๋จํ๊ฒ ๋งํ๋ฉด ์ด๋ ๊ฒ ๋์ํฉ๋๋ค.
FieldError๋ ObjectError๋ฅผ ์ง์ ๋ค๋ฃฐ ๋๋ ์ค๋ฅ์ฝ๋๋ฅผ required.item.itemName์ฒ๋ผ ์ง์ ๋ชจ๋ ์ ๋ ฅํ์์ต๋๋ค. ๊ทธ๋ฐ๋ฐ rejectValue()๋ฅผ ์ฌ์ฉํ๊ณ ๋์๋ถํฐ๋ ์ค๋ฅ ์ฝ๋๋ฅผ required์ฒ๋ผ ๊ฐ๋จํ๊ฒ ์ ๋ ฅํ๋๋ฐ๋ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์ ์ฐพ์์ ์ถ๋ ฅํ์ต๋๋ค.
์ด ๋ถ๋ถ์ ์ฌ์ค MessageCodesResolver๊ฐ ๋์ํ์ฌ ๋ฉ์์ง ์ฝ๋๋ฅผ ์์ฑํ ๋๋ถ์ ๊ฐ๋ฅํ ์ผ์ ๋๋ค.
1. objectName๊ณผ(@ModelAttribute์ ํตํด ์ด๋ฏธ ์๊ณ ์์) ์ ๋ ฅ๋ฐ์ itemName, errorCode๋ฅผ ํตํด MessageResolver์์ ๋ฉ์์ง ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
2. ์์ฑ๋ ๋ฉ์์ง ์ฝ๋ ์์๋๋ก rejectValue()์ ๊ฒฝ์ฐ FieldError๋ฅผ, reject()์ ๊ฒฝ์ฐ์๋ ObjectError๋ฅผ ๋ง๋ค์ด ์ค๋๋ค.
3. ํ์๋ฆฌํ ๋ทฐํ ํ๋ฆฟ์์๋ th:errors๋ฅผ ์ด์ฉํด ์ค๋ฅ๊ฐ ์์ ๊ฒฝ์ฐ ์์ฑ๋ ์ค๋ฅ ๋ฉ์์ง ์ฝ๋๋ฅผ ์์๋๋ก ๋์๊ฐ๋ฉด์ ๋ฉ์์ง ์ฝ๋์ ๋ํ ๋ฉ์์ง๋ฅผ ์ฐพ์ ์ถ๋ ฅํฉ๋๋ค. ์๋ค๋ฉด ๋ํดํธ ๋ฉ์์ง ์ถ๋ ฅ.
์ฐ์ ์ค๋ฅ ์ฝ๋๋ฅผ ์ด๋ป๊ฒ ๋ง๋ค ์ ์๋์ง ๊ฐ๋จํ๊ฒ ์์๋ณด๊ณ , MessageCodesResolver ๋์ ๋ฐฉ์์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ค๋ฅ ์ฝ๋๋ฅผ ๋ง๋ค ๋๋
#Level 1
required.item.itemName = ์ํ ์ด๋ฆ์ ํ์์ ๋๋ค.
#Level 2
required = ํ์ ๊ฐ์ ๋๋ค.
์ด๋ ๊ฒ ๊ฐ์ฒด๋ช ๊ณผ ํ๋๋ช ์ ์กฐํฉํด ์ธ๋ฐํ ๋ฉ์์ง ์ฝ๋๋ฅผ ๋ง๋ค ์๋ ์๊ณ , ๊ฐ๋จํ๊ฒ ๊ฐ์ฒด๋ช ๋ง ๊ฐ์ง๊ณ ๋ง๋ค ์๋ ์์ต๋๋ค.
โ๏ธ ์ฐ์ ์์๋ ๊ฐ์ฒด๋ช ๊ณผ ํ๋๋ช ์ ์กฐํฉํ ์ธ๋ฐํ ๋ฉ์์ง ์ฝ๋๊ฐ ๋ ๋์ ์ฐ์ ์์๋ฅผ ๊ฐ์ง๋๋ค.
์ฌ์ค ์คํ๋ง์ MessageCodesResolver๋ผ๋ ๊ฒ์ผ๋ก ์ด๋ ๊ฒ ๋ฉ์์ง ์ฝ๋๋ฅผ ๋ง๋ค์ด์ฃผ๋ ๊ธฐ๋ฅ์ ์ง์ํฉ๋๋ค. ์ด์ MessageCodesResolver์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.
MessageCodesResolver
MessageCodesResolver๋ก ObjectError ๋ง๋ค๊ธฐ
- ๊ฒ์ฆ ์ค๋ฅ ์ฝ๋๋ก ๋ฉ์์ง ์ฝ๋๋ค์ ์์ฑํฉ๋๋ค.
- MessageCodesResolver๋ ์ธํฐํ์ด์ค์ด๊ณ DefaultMessageCodesResolver๊ฐ ๊ธฐ๋ณธ ๊ตฌํ์ฒด.
- DefaultMessageCodesResolver๋ errorCode์ objectName์ ํตํด ๋ฉ์์ง ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
DefaultMessageCodesResolver์ ObjectError ์์ฑ ๊ท์น
๊ฐ์ฒด ์ค๋ฅ์ ๊ฒฝ์ฐ ๋ค์ ์์๋ก 2๊ฐ์ง ์์ฑ
1 / code + "." + object name
2/ code
MessageCodesResolver๋ก FieldError ๋ง๋ค๊ธฐ
- DefaultMessageCodesResolver๋ errorCode์ objectName๊ณผ fieldName๊ณผ field type์ ํตํด ๋ฉ์์ง ์ฝ๋ 4๊ฐ๋ฅผ ์์ฑํฉ๋๋ค.
ํ๋ ์ค๋ฅ์ ๊ฒฝ์ฐ ๋ค์ ์์๋ก4๊ฐ์ง ๋ฉ์์ง ์ฝ๋ ์์ฑํฉ๋๋ค.
1.: code + "." + object name + "." + field
2.: code + "." + field
3.: code + "." + field type
4.: code
์๋ฅผ ๋ค์ด itemName ์ ๊ฒฝ์ฐ required ๊ฒ์ฆ ์ค๋ฅ ๋ฉ์์ง๊ฐ ๋ฐ์ํ๋ฉด ๋ค์ ์ฝ๋ ์์๋๋ก ๋ฉ์์ง๊ฐ ์์ฑ๋๋ค.
1. required.item.itemName 2. required.itemName 3. required.java.lang.String 4. required
๊ทธ๋ฆฌ๊ณ ์ด๋ ๊ฒ ์์ฑ๋ ๋ฉ์์ง ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์๋๋ก MessageSource์์ ๋ฉ์์ง๋ฅผ ์ฐพ์ต๋๋ค.
์ด ๊ธฐ๋ฅ์ ํตํด ์คํ๋ง์ด ์ง์ ๋ง๋ ์ค๋ฅ ๋ฉ์์ง๋ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
ํ์ ์ค๋ฅ ๋ฐ์ ์ ์คํ๋ง์ BindingResult์ ํ์ ์ค๋ฅ๋ฅผ FieldError์ ์๋์ผ๋ก ๋ด์ ์ค๋ค๊ณ ํ์์ฃ .
๋ก๊ทธ๋ฅผ ํ์ธํด๋ณด๋ฉด
codes๋ฅผ ์ดํด๋ณด๋ฉด ์คํ๋ง์ ํ์ ์ค๋ฅ ๋ฐ์ ์ typeMistmatch๋ผ๋ ์ค๋ฅ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
์ด ์ค๋ฅ ์ฝ๋๊ฐ MessagesCodesResolver๋ฅผ ํตํ๋ฉด์
typeMismatch.item.price, typeMismatch.price, typeMismatch.java.lang.Integer, typeMismatch
4๊ฐ์ง ๋ฉ์์ง ์ฝ๋๊ฐ ์์ฑ๋์์ต๋๋ค.
errors.properties์ ๋ฉ์์ง ์ฝ๋๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ด๋ ๊ฒ defaultMessage๋ก Failed to ~... ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ ๊ฒ์ธ๋ฐ์.
errors.properties์ ์ด๋ ๊ฒ ์ถ๊ฐํด์ฃผ๋ฉด ( 4๊ฐ ๋ค ์ํด๋ ๋จ )
#์ถ๊ฐ
typeMismatch.item.price=์ซ์๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.
typeMismatch.price=์ซ์๋ง ์
๋ ฅํด์ฃผ์ธ์.
typeMismatch.java.lang.Integer= ํ์
์ค๋ฅ์
๋๋ค.
typeMismatch= ํ์
์ด ์๋ชป๋์์ต๋๋ค.
ํ์ ์ค๋ฅ์ ๋ํ ๊ธฐ๋ณธ ๋ฉ์์ง๋ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
Validator ๋ถ๋ฆฌ
์ปจํธ๋กค๋ฌ์ ๊ตฌํ๋ ๋ณต์กํ ๊ฒ์ฆ๋ก์ง์ Validator๋ฅผ ์ฌ์ฉํด์ ๋ถ๋ฆฌํด๋ณด๊ฒ ์ต๋๋ค.
Validator๋ ์คํ๋ง์ด ์ ๊ณตํ๋ ๊ฒ์ฆ๊ธฐ์ด๋ฉฐ, ์ธํฐํ์ด์ค๋ก ๊ตฌํ๋์ด์์ต๋๋ค.
public interface Validator {
/**
* Can this {@link Validator} {@link #validate(Object, Errors) validate}
* instances of the supplied {@code clazz}?
* <p>This method is <i>typically</i> implemented like so:
* <pre class="code">return Foo.class.isAssignableFrom(clazz);</pre>
* (Where {@code Foo} is the class (or superclass) of the actual
* object instance that is to be {@link #validate(Object, Errors) validated}.)
* @param clazz the {@link Class} that this {@link Validator} is
* being asked if it can {@link #validate(Object, Errors) validate}
* @return {@code true} if this {@link Validator} can indeed
* {@link #validate(Object, Errors) validate} instances of the
* supplied {@code clazz}
*/
boolean supports(Class<?> clazz);
/**
* Validate the supplied {@code target} object, which must be
* of a {@link Class} for which the {@link #supports(Class)} method
* typically has (or would) return {@code true}.
* <p>The supplied {@link Errors errors} instance can be used to report
* any resulting validation errors.
* @param target the object that is to be validated
* @param errors contextual state about the validation process
* @see ValidationUtils
*/
void validate(Object target, Errors errors);
}
- supports(Class<?> clazz) : ํด๋น ๊ฒ์ฆ๊ธฐ๋ฅผ ์ง์ํ๋์ง ์ฌ๋ถ
- validate(Object target, Errors errors) : "๊ฒ์ฆ ๋์ ๊ฐ์ฒด"์ ์ค๋ฅ๋ฅผ ๋ด์ BindingResult
์ด Validator๋ฅผ ์ด์ฉํด ItemValidator๋ฅผ ๋ง๋ค๊ณ ItemValidator์ ๊ฒ์ฆ๋ก์ง์ ๊ตฌํํด์ Controller์์ ItemValidator๋ฅผ ๊ฐ์ ธ๋ค๊ฐ ์ฐ๋๋ก ํ๋ฉด ์ปจํธ๋กค๋ฌ ์ฝ๋๊ฐ ํจ์ฌ ๊น๋ํด์ง๊ณ ์ ์ง๋ณด์๊ฐ ํธ๋ฆฌํด์ง๋๋ค. ํ๋ฒ ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.
ItemValidator
@Component
public class ItemValidator implements Validator {
@Override
public boolean supports(Class<?> clazz)
{
return Item.class.isAssignableFrom(clazz);
// item == clazz
// item == subItem
}
@Override
public void validate(Object target, Errors errors)
{
Item item = (Item) target;
ValidationUtils.rejectIfEmptyOrWhitespace(errors,"itemName", "required");
if(item.getPrice()==null || item.getPrice() < 1000 || item.getPrice() > 1000000)
{
errors.rejectValue("price","range",new Object[]{1000,1000000},null);
}
if(item.getQuantity()==null || item.getQuantity() > 10000 || item.getQuantity() < 1 )
{
errors.rejectValue("quantity","max",new Object[]{9999},null);
}
if(item.getPrice() != null && item.getQuantity() != null)
{
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice<10000)
{
errors.reject("totalPriceMin",new Object[]{10000,resultPrice},null);
}
}
}
}
๊ฒ์ฆ๋ก์ง์ ItemValidator์์ Validator ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์ต๋๋ค.
private final ItemValidator itemValidator;
@PostMapping("/add")
public String addItemV5(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes)
{
// boolean supports = itemValidator.supports(item.getClass());
// System.out.println("supports = " + supports);
itemValidator.validate(item,bindingResult);
if(bindingResult.hasErrors())
{
log.info("errors={}",bindingResult);
return "validation/v2/addForm";
}
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("status",true);
redirectAttributes.addAttribute("itemId",savedItem.getId());
return "redirect:/validation/v2/items/{itemId}";
}
์ด์ ์ปจํธ๋กค๋ฌ์์ ์คํ๋ง ์ปจํ ์ด๋์ ๋ฑ๋กํ ItemValidor๋ฅผ ์ฌ์ฉํ๋๋ก ์์กด๊ด๊ณ๋ฅผ ์ฃผ์ ํด์ฃผ๊ณ , itemValidator์ validate๋ฅผ ํตํด ์ค๋ฅ ๋ฐ์์ bindingResult์ ๋ด์์ฃผ์์ต๋๋ค.
Validator ๋ถ๋ฆฌ2
์ฌ์ค ์คํ๋ง์ด ์ ๊ณตํ๋ Validator ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํด์ ๊ฒ์ฆ๊ธฐ๋ฅผ ๋ง๋ค๋ฉด ์ถ๊ฐ์ ์ธ ๋์์ ๋ฐ์ ์ ์์ต๋๋ค.
์ด๋ค ๋์์ ๋ฐ์ ์ ์๋์ง ์์๋ณด๊ฒ ์ต๋๋ค.
@InitBinder
public void init(WebDataBinder dataBinder)
{
log.info("init binder {}", dataBinder);
dataBinder.addValidators(itemValidator);
}
WebDataBinder๋ ๊ตฌํํ ๊ฒ์ฆ๊ธฐ(Validator)๋ฅผ ๋ด์ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
์ด๋ ๊ฒ WebDataBinder์ ๊ฒ์ฆ๊ธฐ๋ฅผ ์ถ๊ฐํ๊ณ @InitBinder๋ฅผ ์ฌ์ฉํ๋ฉด ์ปจํธ๋กค๋ฌ๊ฐ ํธ์ถ๋๋ฉด WebDataBinder์ ๊ตฌํํ Validator๋ฅผ ๋ฃ๊ณ ์ฌ์ฉํ ์ ์์ต๋๋ค.
@PostMapping("/add")
public String addItemV6(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes)
{
if(bindingResult.hasErrors())
{
log.info("errors={}",bindingResult);
return "validation/v2/addForm";
}
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("status",true);
redirectAttributes.addAttribute("itemId",savedItem.getId());
return "redirect:/validation/v2/items/{itemId}";
}
@Validated๋ ๊ฒ์ฆ๊ธฐ๋ฅผ ์คํํ๋ผ๋ ์ด๋ ธํ ์ด์ ์ธ๋ฐ ์ด ์ด๋ ธํ ์ด์ ์ด ๋ถ์ผ๋ฉด ์์ WebDataBinder์ ๋ฑ๋กํ ๊ฒ์ฆ๊ธฐ๋ฅผ ์ฐพ์์ ์คํํด์ค๋๋ค. ๊ทธ๋ฐ๋ฐ ์ฌ๋ฌ ๊ฒ์ฆ๊ธฐ๋ฅผ ๋ฑ๋กํ๋ค๋ฉด ๊ทธ์ค์ ์ด๋ค ๊ฒ์ฆ๊ธฐ๊ฐ ์คํ๋์ด์ผ ํ ์ง ๊ตฌ๋ถ์ด ํ์ํ๊ฒ ์ฃ . ์ด ๋ supports()๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
์ฌ๊ธฐ์๋ supports(Item.class)๊ฐ ํธ์ถ๋๊ณ , ๊ฒฐ๊ณผ๊ฐ true ์ด๋ฏ๋ก ItemValidator์ validate()๊ฐ ํธ์ถ๋ฉ๋๋ค -> ๊ฒ์ฆ ๋ก์ง์ ์คํจํ๋ฉด bindingResult์ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ด์ต๋๋ค. ( ์๋ํ )
+ Validator ๊ธ๋ก๋ฒ์ค์ ํ๊ธฐ
์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฑ๋ก
@SpringBootApplication
public class ItemServiceApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(ItemServiceApplication.class, args);
}
@Override
public Validator getValidator()
{
return new ItemValidator();
}
}
@PostMapping("/add")
public String addItemV6(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes)
{
if(bindingResult.hasErrors())
{
log.info("errors={}",bindingResult);
return "validation/v2/addForm";
}
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("status",true);
redirectAttributes.addAttribute("itemId",savedItem.getId());
return "redirect:/validation/v2/items/{itemId}";
}
์ด๋ ๊ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฑ๋กํ๋ฉด ๋ชจ๋ ์ปจํธ๋กค๋ฌ์ ์ ์ฉํ ์ ์์ต๋๋ค.
ํ์ง๋ง ๊ธ๋ก๋ฒํ๊ฒ ์ค์ ํ๋ ๊ฒฝ์ฐ๋ ๊ฑฐ์ ์๊ธฐ ๋๋ฌธ์ ์ปจํธ๋กค๋ฌ ๋ ๋ฒจ์์ @InitBinder๋ฅผ ์ฌ์ฉํด ์ปจํธ๋กค๋ฌ ํธ์ถ์์ WebDataBinder์ ๊ฒ์ฆ๊ธฐ๋ฅผ ๋ด์ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
'๐ Backend > MVC Pattern' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ก๊ทธ์ธ ์ฒ๋ฆฌ - ์ฟ ํค, ์ธ์ (0) | 2023.03.23 |
---|---|
Bean Validation - ๊ฒ์ฆ ์ด๋ ธํ ์ด์ ์ฌ์ฉ (0) | 2023.03.22 |
๊ฒ์ฆ(Validation) ์ฒ๋ฆฌ ๋ฐฉ๋ฒ (0) | 2023.03.20 |
๋ฉ์์ง, ๊ตญ์ ํ ๊ธฐ๋ฅ (0) | 2023.03.20 |
Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋ฅ - ์๋ต ๋งคํ @ResponseBody, MessageConverter, ์์ฒญ ๋งคํ ํธ๋ค๋ฌ ์ด๋ํฐ ๊ตฌ์กฐ (0) | 2023.03.13 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422