์คํ๋ง MVC 2ํธ์ ๊ฐ์ ๋ด์ฉ์ ์ ๋ฆฌํ ํฌ์คํ ์ ๋๋ค.
Bean Validation์ด๋
๊ฒ์ฆ ์ ๋
ธํ
์ด์
๊ณผ ์ฌ๋ฌ ์ธํฐํ์ด์ค๋ฅผ ๋ชจ์ ๊ธฐ์ ํ์ค์ผ๋ก, ๊ฐ์ ๋น์ด์๋ ์ฌ๋ถ๋ ๋ฒ์๋ฅผ ๊ฒ์ฆํ๋ ๊ฒ๊ณผ ๊ฐ์ด ์ผ๋ฐ์ ์ธ ๋ก์ง์ ๊ฒ์ฆํ ๋ ๋ชจ๋ ํ๋ก์ ํธ์ ์ ์ฉํ ์ ์๊ฒ ๊ณตํตํํ๊ณ ํ์คํํ ๊ฒ์ด๋ค.
ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ build.gradle์ ๋ค์๊ณผ ๊ฐ์ด ์์กด๊ด๊ณ๋ฅผ ์ถ๊ฐํด ์ฃผ๋ฉด ๋๋ค.
implementation 'org.springframework.boot:spring-boot-starter-validation'
๊ฒ์ฆ ์ ๋
ธํ
์ด์
์ ๋ค์๊ณผ ๊ฐ์ด ์ ์ฉํ ์ ์๋ค.@NotBlank
๋น ๊ฐ์ธ ๊ฒฝ์ฐ ํน์ ๊ณต๋ฐฑ๋ง ์๋ ๊ฒฝ์ฐ๋ฅผ ํ์ฉํ์ง ์๋๋ค.@NotNull
null์ ํ์ฉํ์ง ์๋๋ค.@Range(min = 10, max = 100)
10์์ 100 ์ฌ์ด์ ๊ฐ๋ง ํ์ฉํ๋ค.@Max(100)
์ต๋ 100๊น์ง์ ๊ฐ๋ง ํ์ฉํ๋ค.
์คํ๋ง ์ ์ฉ
์คํ๋ง ๋ถํธ๋ LocalValidatorFactoryBean์ ๊ธ๋ก๋ฒ Validator๋ก ๋ฑ๋กํ๋๋ฐ, ํด๋น ๊ฒ์ฆ๊ธฐ๋ ๊ฒ์ฆ ์ ๋
ธํ
์ด์
์ ๋ณด๊ณ ๊ฒ์ฆ์ ์ํํ๋ค. ๊ธ๋ก๋ฒ ๊ฒ์ฆ๊ธฐ๊ฐ ์ ์ฉ๋์ด ์๊ธฐ ๋๋ฌธ์ ๊ฒ์ฆ ๋์ ๊ฐ์ฒด์ @Valid
๋๋ @Validated
๋ง ์ ์ฉํ๋ฉด ๊ฒ์ฆ๊ธฐ๊ฐ ์คํ๋๋ค.(@Valid
๋ ์๋ฐ ํ์ค ๊ฒ์ฆ ์ ๋
ธํ
์ด์
, @Validated
๋ ์คํ๋ง ์ ์ฉ ๊ฒ์ฆ ์ ๋
ธํ
์ด์
์ผ๋ก ๊ธฐ๋ฅ์ ๊ฑฐ์ ์ผ์นํ๋ @Validated์๋ group๋ผ๋ ๊ธฐ๋ฅ์ด ์๋ค.)
๋ง์ฝ ๊ฒ์ฆ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ FieldError, ObjectError๋ฅผ ์์ฑํด์ BindingResult์ ๋ด์์ค๋ค.
์ด๋ ์ฃผ์ํด์ผ ํ ์ ์ด ์๋์ผ๋ก ๊ธ๋ก๋ฒ ๊ฒ์ฆ๊ธฐ๋ฅผ ๋ฑ๋กํ ๊ฒฝ์ฐ ์คํ๋ง ๋ถํธ๋ Bean Validator๋ฅผ ๊ธ๋ก๋ฒ ๊ฒ์ฆ๊ธฐ๋ก ๋ฑ๋กํ์ง ์๋๋ค๋ ๊ฒ์ด๋ค. ์ด๋ ์ ๋
ธํ
์ด์
๊ธฐ๋ฐ์ ๋น ๊ฒ์ฆ๊ธฐ๊ฐ ๋์ํ์ง ์์์ ์๋ฏธํ๋ค. ๋ฐ๋ผ์ ์๋์ผ๋ก ๋ฑ๋กํ ๊ฒฝ์ฐ ์ด๋ฌํ ์ ์ ์ธ์งํ๊ณ ์์ด์ผ ํ๋ค.
๊ฒ์ฆ ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
1. @ModelAttribute
๊ฐ ๊ฐ๊ฐ์ ํ๋์ ํ์
๋ณํ(๋ฐ์ธ๋ฉ)์ ์๋ํ๊ณ , ์ฑ๊ณต ์ ๋ค์ ๋จ๊ณ๋ก ๋์ด๊ฐ์ง๋ง ์คํจํ๋ฉด typeMismatch๋ก FieldError๋ฅผ ์ถ๊ฐํ๋ค.
2. Validator๋ฅผ ์ ์ฉํ๋ค.
ํด๋น ๊ณผ์ ์์ ๋ณผ ์ ์๋ฏ์ด ๋ฐ์ธ๋ฉ์ ์คํจํ ํ๋๋ ๋น ๊ฒ์ฆ๊ธฐ๋ฅผ ์ ์ฉํ์ง ์๋๋ฐ, ์ด๋ ์ ์ฉ์ ์๋ฏธ๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ฒ์ฆ ์กฐ๊ฑด์ ๋น๊ตํ๋ ค๋ฉด ์ฐ์ ๊ฐ์ด ์ ์์ ์ผ๋ก ํ ๋น๋์ด์ผ ํ๋๋ฐ, ์คํจํ ๊ฒฝ์ฐ์๋ ๊ฒ์ฆ์ ์๋ฏธ๊ฐ ์๋ค.
์๋ฌ ์ฝ๋
Bean Validation์ ์ ์ฉํ๊ณ BindingResult์ ๋ฑ๋ก๋ ๊ฒ์ฆ ์ค๋ฅ ์ฝ๋๋ MessageCodesResolver์ ์ํด ์์ฑ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ํด๋น ์ค๋ฅ ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ํ๋ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ฑ๋กํ ์ ์๋ค.
๋ค์์ @NotBlank๋ฅผ ์ด์ฉํ ์์์ด๋ค.
#์ค๋ฅ ์ฝ๋
NotBlank.item.itemName
NotBlank.itemName
NotBlank.java.lang.String
NotBlank
#๋ฐฉ๋ฒ 1 - errors.properties์ Bean Validation ์ถ๊ฐ, {0}์ ํ๋๋ช
NotBlank={0} ๊ณต๋ฐฑ์ ์
๋ ฅํ ์ ์์ต๋๋ค.
//๋ฐฉ๋ฒ 2 - ์ ๋
ธํ
์ด์
๋ด message ์์ฑ ์ฌ์ฉ
@NotBlank(message = "๊ณต๋ฐฑ์ ์
๋ ฅํ ์ ์์ต๋๋ค.")
private String itemName;
์ ๋ฆฌํ๋ฉด Bean Validation์ ๋ฉ์์ง๋ฅผ ์ฐพ๋ ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
1. ์์ฑ๋ ์ค๋ฅ ์ฝ๋ ์์๋๋ก messageSource์์ ๋ฉ์์ง ์ฐพ๊ธฐ
2. ์ ๋
ธํ
์ด์
์ message ์์ฑ ์ฌ์ฉ
3. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ๊ฐ ์ฌ์ฉ (e.g. ๊ณต๋ฐฑ์ผ ์ ์์ต๋๋ค.)
์ค๋ธ์ ํธ ์ค๋ฅ
Bean Validation์์ ํน์ ํ๋(FieldError)๊ฐ ์๋ ์ค๋ธ์ ํธ ๊ด๋ จ ์ค๋ฅ(ObjectError)๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ์ ๋
ธํ
์ด์
์ผ๋ก @ScriptAssert()๋ฅผ ์ด์ฉํ ์ ์์ง๋ง, ์ ์ฝ์ด ๋ง๊ณ ๋ณต์กํ๋ค๋ ๋จ์ ์ด ์กด์ฌํ๋ค.
๋ฐ๋ผ์ ์ค๋ธ์ ํธ ์ค๋ฅ์ ๊ฒฝ์ฐ ํด๋น ๋ถ๋ถ๋ง ์ง์ ์๋ฐ ์ฝ๋๋ก ์์ฑํ๋ ๊ฒ์ด ๊ถ์ฅ๋๋ค.
@PostMapping("/add")
public String addItem(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
if (item.getPrice() != null && item.getQuantity() != null) {
int resultPrice = item.getPrice() * item.getQuantity();
if (resultPrice < 10000) { //ํน์ ํ๋ ์์ธ๊ฐ ์๋ ์ ์ฒด ์์ธ
bindingResult.reject("totalPriceMin", new Object[]{10000, resultPrice}, null);
}
}
}
ํ๊ณ
๊ฐ์ ๊ฐ์ฒด์ ๋ํด ๋ค๋ฅธ ๊ฒ์ฆ ์กฐ๊ฑด์ ์ ์ฉํ ๊ฒฝ์ฐ ์ถฉ๋์ด ๋ฐ์ํ๋ค.
์๋ฅผ ๋ค์ด Item ๊ฐ์ฒด๋ฅผ ์ด์ฉํ์ฌ ์ํ์ ๋ฑ๋กํ ๋๋ ์๋์ 9999๊ฐ๊น์ง๋ง ํ์ฉํ์ง๋ง, ์์ ํ ๋๋ ์๋์ ๋ฌด์ ํ์ผ๋ก ๋ฐ์ ์ ์๋๋ก ์กฐ๊ฑด์ ๋ณ๊ฒฝํ๊ณ ์ถ๋ค๋ฉด? ๋ํ ๋ฑ๋ก ์์ ์๋ id ๊ฐ์ด ์กด์ฌํ์ง ์์ง๋ง ์์ ์์ ์๋ id ๊ฐ์ด ํ์์ธ ์ ์ ๊ณ ๋ คํ๋ฉด ์ถฉ๋์ด ์กด์ฌํ๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
@Data
public class Item {
@NotBlank
private String itemName;
@NotNull
@Range(min = 1000, max = 1000000)
private Integer price;
@NotNull
@Max(value = 9999)
private Integer quantity;
}
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋ ๊ฐ์ง๊ฐ ์๋ค.
๋์ 1 - groups
Bean Validation์ groups
๋ผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋๋ฐ, ํด๋น ๊ธฐ๋ฅ์ ์ด์ฉํ๋ฉด ๋ฑ๋ก ์ ๊ฒ์ฆํ ๊ธฐ๋ฅ๊ณผ ์์ ์ ๊ฒ์ฆํ ๊ธฐ๋ฅ์ ๊ฐ๊ฐ ๊ทธ๋ฃน์ผ๋ก ๋๋์ด ์ ์ฉํ ์ ์๋ค.
@Data
public class Item {
@NotNull(groups = UpdateCheck.class)
private Long id;
@NotBlank(groups = {SaveCheck.class, UpdateCheck.class})
private String itemName;
@NotNull(groups = {SaveCheck.class, UpdateCheck.class})
@Range(min = 1000, max = 1000000, groups = {SaveCheck.class, UpdateCheck.class})
private Integer price;
@NotNull(groups = {SaveCheck.class, UpdateCheck.class})
@Max(value = 9999, groups = SaveCheck.class)
private Integer quantity;
}
@PostMapping("/add")
public String addItem(
@Validated(SaveCheck.class) @ModelAttribute Item item,
BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
//๋ฑ๋ก ๋ก์ง ๊ตฌํ
}
๋์ 2 - Form ์ ์ก ๊ฐ์ฒด ๋ถ๋ฆฌ
๋ ๋ฒ์งธ ๋ฐฉ๋ฒ์ ๋ฑ๋ก์ฉ ํผ ๊ฐ์ฒด์ ์์ ์ฉ ํผ ๊ฐ์ฒด๋ฅผ ๋ถ๋ฆฌํด์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
์ฒซ ๋ฒ์งธ ๋ฐฉ๋ฒ์ ์ ์ฌ์ฉํ์ง ์๋ ๊ฒ์ ๊ฐ์ฒด์ ๋ถ์ผ์น์ฑ ๋๋ฌธ์ธ๋ฐ, ์๋ฅผ ๋ค์ด ํ์ ๋ฑ๋ก ์์๋ ํ์๊ณผ ๊ด๋ จ๋ ๋ฐ์ดํฐ๋ฟ๋ง ์๋๋ผ ์ฝ๊ด ์ ๋ณด ๋ฑ Item ๊ฐ์ฒด์ ๊ด๋ จ ์๋ ์๋ง์ ๋ถ๊ฐ ๋ฐ์ดํฐ๊ฐ ๋์ด์จ๋ค.
๋ฐ๋ผ์ ๋ฑ๋ก๊ณผ ์์ ์ ๊ฐ๊ฐ ์ฒ๋ฆฌํ๋ ์ ์ฉ ๊ฐ์ฒด๋ฅผ @ModelAttribute
๋ก ์ฌ์ฉํด ์ปจํธ๋กค๋ฌ์์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ๋ฐ๊ณ , ํ์ํ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํด Item ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์ด์ฉํ๋ค.
@Data
public class Item {
private Long id;
private String itemName;
private Integer price;
private Integer quantity;
}
@Data
public class ItemSaveForm {
@NotBlank
private String itemName;
@NotNull
@Range(min = 1000, max = 1000000)
private Integer price;
@NotNull
@Max(value = 9999)
private Integer quantity;
}
@Data
public class ItemUpdateForm {
@NotNull
private Long id;
@NotBlank
private String itemName;
@NotNull
@Range(min = 1000, max = 1000000)
private Integer price;
//์์ ์์๋ ์๋ ์์ ๋ณ๊ฒฝ ๊ฐ๋ฅ.
private Integer quantity;
}
@PostMapping("/add")
public String addItem(@Validated @ModelAttribute("item") ItemSaveForm form, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
/**
๊ฒ์ฆ ๋ก์ง
**/
Item item = new Item(); //๊ฒ์ฆ ๋ก์ง ํต๊ณผ ํ ๊ฐ์ฒด ์์ฑ
item.setItemName(form.getItemName());
item.setPrice(form.getPrice());
item.setQuantity(form.getQuantity());
}
์ฐธ๊ณ ๋ก @ModelAttribute
์์ "item"์ ๋ฃ์ด์ฃผ์ง ์์ผ๋ฉด ๋ค์ด๋ฐ ๊ท์น์ ์ํด itemSaveForm
์ด๋ผ๋ ์ด๋ฆ์ผ๋ก MVC ๋ชจ๋ธ์ ๋ด๊ธฐ๊ฒ ๋๋ค.
HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ
@Valid
, @Validated
๋ HttpMessageConverter(@RequestBody)์๋ ์ ์ฉํ ์ ์๋ค.
๋ค๋ง @ModelAttribute
์ ์ฐจ์ด์ ์ด ์๋ค๋ฉด @ModelAttribute
๋ ๊ฐ๊ฐ์ ํ๋ ๋จ์๋ก ์ธ๋ฐํ๊ฒ ์ ์ฉ๋๋ ๋ฐ๋ฉด, @RequestBody
๋ ์ ์ฒด ๊ฐ์ฒด ๋จ์๋ก ์ ์ฉ๋๋ค๋ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ ๋ฉ์์ง ์ปจ๋ฒํฐ์ ์๋์ด ์ฑ๊ณตํ ๋ค ๊ฐ์ฒด๊ฐ ์์ฑ๋์ด์ผ @Valid
, @Validated
๊ฐ ์ ์ฉ๋ ์ ์๋ค.
API ์์ฒญ ์ ๋ฐ์ํ ์ ์๋ ๊ฒฝ์ฐ์ ์๋ฅผ ์๊ฐํด ๋ณด์.
1. ์ฑ๊ณต ์์ฒญ - ์ฑ๊ณต
2. ์คํจ ์์ฒญ - JSON์ ๊ฐ์ฒด๋ก ๋ณํํ๋ ๊ฒ ์์ฒด๊ฐ ์คํจ
3. ๊ฒ์ฆ ์ค๋ฅ ์์ฒญ - JSON์ ๊ฐ์ฒด๋ก ๋ณํํ๋ ๊ฒ์ ์ฑ๊ณตํ์ผ๋ ๊ฒ์ฆ์์ ์คํจ
3๋ฒ์ ๊ฒฝ์ฐ BeanValidation ๋จ๊ณ์์ ์์ธ๊ฐ ๋ฐ์ํ์ง๋ง, 2๋ฒ์ ๊ฒฝ์ฐ HttpMessageConverter ๋จ๊ณ์์ ์์ธ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ค.@ModelAttribute
๋ ํน์ ํ๋๊ฐ ๋ฐ์ธ๋ฉ๋์ง ์์๋ ๋๋จธ์ง ํ๋๋ ์ ์ ๋ฐ์ธ๋ฉ ๋๊ณ , ๊ฒ์ฆ๊ธฐ๋ฅผ ์ด์ฉํด ๊ฒ์ฆ ์์
๋ ์ํํ ์ ์์ง๋ง @RequestBody
๋ HttpMessageConverter ๋จ๊ณ์์ JSON์ ๊ฐ์ฒด๋ก ๋ณํํ์ง ๋ชปํ๋ฉด ์ดํ ๋จ๊ณ๊ฐ ์งํ๋์ง ์๋๋ค. ๋ฐ๋ผ์ ์ปจํธ๋กค๋ฌ๋, ๊ฒ์ฆ๊ธฐ๋ ํธ์ถ๋์ง ์๋๋ค.
'Language & Framework > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์คํ๋ง MVC] ๋ก๊ทธ์ธ ์ฒ๋ฆฌ(2) - ํํฐ, ์ธํฐ์ ํฐ (0) | 2023.03.03 |
---|---|
[์คํ๋ง MVC] ๋ก๊ทธ์ธ ์ฒ๋ฆฌ(1) - ์ฟ ํค, ์ธ์ (0) | 2023.03.02 |
[์คํ๋ง MVC] ๊ฒ์ฆ(1) - Validation (0) | 2023.02.12 |
[์คํ๋ง MVC] ๋ฉ์์ง, ๊ตญ์ ํ (0) | 2023.02.11 |
[์คํ๋ง MVC] ๊ธฐ๋ณธ ๊ธฐ๋ฅ (0) | 2023.02.05 |