# ์˜ค๋ฅ˜ ์ฝ”๋“œ์™€ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
Study Repository

์˜ค๋ฅ˜ ์ฝ”๋“œ์™€ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ

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์— ๊ฒ€์ฆ๊ธฐ๋ฅผ ๋‹ด์•„ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

Study Repository

rlaehddnd0422

ํ™œ๋™ํ•˜๊ธฐ