request ํ, ์๋ฒ ์ธก์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉํ ๋ ๋ฐ์ดํฐ๊ฐ ์ ํจํ์ง ๊ฒ์ฌํ๋ ๊ฒ์ ๋งํ๋ค.
๐ก ๋ฐ์ธ๋ฉ (binding)
์ฌ์ฉ์ ์ ๋ ฅ์ ๊ฐ์ง๊ณ ์ปค๋งจ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ์์ ์ ์คํ๋ง์์๋ ๋ฐ์ธ๋ฉ(binding)ํ๋ค๊ณ ํ๋ค.
์คํ๋ง์ด ์ ๊ณตํ๋ ๊ฒ์ฆ ์ค๋ฅ ๋ณด๊ด ๊ฐ์ฒด์ด๋ค. ๋ฐ์ดํฐ์ ์ ํจํ์ง ์์ ์์ฑ์ด ์๋ค๋ฉด, ๊ทธ์ ๋ํ ์ค๋ฅ ์ ๋ณด๊ฐ ๋ด๊ธด๋ค. ํด๋น ์ ๋ณด๋ฅผ ์ปจํธ๋กค๋ฌ์ ์ ๋ฌํด ์ค๋ฅ ํ์ด์ง๋ฅผ ๋ณ๋๋ก ์ฒ๋ฆฌํด ์ค ์ ์๋ค.
๐จ ์ปจํธ๋กค๋ฌ์ ํ๋ผ๋ฏธํฐ๋ก ์ฌ์ฉํ ๋, ๋ฐ๋์ @Valid ๋ค์ ์์นํด์ผ ํ๋ค.
์๋ฐ ํ์ค ๊ฒ์ฆ ์ด๋ ธํ ์ด์ ์ด๋ค. ์ปจํธ๋กค๋ฌ์ ๋ฉ์๋์ ์์ฑํ๋ค. ์ปจํธ๋กค๋ฌ์์๋ง ๋์ํ๊ธฐ ๋๋ฌธ์, ๋ค๋ฅธ ๊ณ์ธต์์์ ๊ฒ์ฆ์ด ํ์ํ ๋๋ @Validated์ ๊ฒฐํฉํด์ผ ํ๋ค. @Valid์ ์ํ ์์ธ ํด๋์ค๋ MethodArgumentNotValidException์ด๋ค.
์คํ๋ง์์ ์ ๊ณตํ๋ ๊ฒ์ฆ ์ด๋ ธํ ์ด์ ์ด๋ค. @Validated๋ฅผ ํด๋์ค๋ช ์์ ์์ฑํ๊ณ , ์ ํจ์ฑ์ ๊ฒ์ฆํ ๋ฉ์๋์ @Valid๋ฅผ ์์ฑํด์ผ ํ๋ค. @Validated์ ์ํ ์์ธ ํด๋์ค๋ ConstraintViolationException์ด๋ค.
๋ค์๊ณผ ๊ฐ์ ์ํฐํฐ๊ฐ ์๋ค.
package com.example.demo.entity;
import java.time.LocalDate;
import javax.validation.constraints.*;
import com.example.demo.entity.*;
import lombok.*;
@Data
@AllArgsConstructor
@Builder
@Accessors(chain=true)
public class Member {
private String username;
private String password;
private String irum;
private String email;
private LocalDate birthday;
private LocalDate joinday;
private Boolean enabled;
private String authority;
private String checkcode;
private Integer count;
private Level levels;
private Integer loginFailCnt;
}
์ด๋ฌํ ์ํฐํฐ์์ ์์ด๋(username), ๋น๋ฐ๋ฒํธ, ์ด๋ฆ, ์ด๋ฉ์ผ๋ง ์ ํจ์ฑ ๊ฒ์ฆ์ ํด์ผ ํ๋ค๊ณ ์๊ฐํด ๋ณด์. ์ด ์ํ ๊ทธ๋๋ก ๋๊ธฐ๊ฒ ๋๋ฉด, ํ์ํ์ง ์์ ํ๋๋ค์ null ๊ฐ์ผ๋ก ๋์ด๊ฐ๊ฒ ๋๋ค. ์ด๋ ํ์ํ ๊ฒ์ด DTO์ด๋ค.
package com.example.demo.dto;
import java.time.LocalDate;
import javax.validation.constraints.*;
import com.example.demo.entity.Member;
import lombok.*;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MemberDto {
@Data
public static class Join {
@NotEmpty(message = "์์ด๋๋ ํ์ ์
๋ ฅ์
๋๋ค.")
@Pattern(regexp = "^[A-Z0-9]{8,10}$", message = "์์ด๋๋ ๋๋ฌธ์๋ ์ซ์ 8-10์์
๋๋ค.")
private String username;
@NotEmpty(message = "๋น๋ฐ๋ฒํธ๋ ํ์ ์
๋ ฅ์
๋๋ค.")
private String password;
@NotEmpty(message = "์ด๋ฆ์ ํ์ ์
๋ ฅ์
๋๋ค.")
private String irum;
@NotEmpty(message = "์ด๋ฉ์ผ์ ํ์ ์
๋ ฅ์
๋๋ค.")
private String email;
// @NotEmpty(message = "์์ผ์ ํ์ ์
๋ ฅ์
๋๋ค.")
private LocalDate birthday;
public Member toEntity() {
return Member.builder().username(username).password(password).irum(irum).email(email).birthday(birthday).build();
}
}
}
Join ํด๋์ค์ ๊ฐ ํ๋ ์ ์ฝ ์กฐ๊ฑด์ ๋ฐ๋ผ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ๋ค.
@Validated
@Controller
public class MemberController {
@Autowired
private MemberService service;
@PreAuthorize("isAnonymous()")
@PostMapping("/member/join")
public String join(@Valid MemberDto.Join dto, BindingResult bindingResult, RedirectAttributes ra) {
service.join(dto);
ra.addFlashAttribute("msg", "๊ฐ์
ํ์ธ ์ฝ๋๋ฅผ ์ด๋ฉ์ผ๋ก ๋ณด๋์ต๋๋ค. ์ด๋ฉ์ผ์ ํ์ธํ์ธ์");
return "redirect:/member/check_join";
}
}
์ปจํธ๋กค๋ฌ ํด๋์ค๋ช
์์ @Validated
๋ฅผ ์์ฑํ๊ณ , ์ ํจ์ฑ ๊ฒ์ฆ์ ํ ๋ฉ์๋์ @Valid
๋ฅผ ์์ฑํ๋ค. ๋ง์ฝ ๊ฒ์ฆ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด, ํด๋น ๊ฒ์ฆ ์ค๋ฅ๋ ConstraintViolationException
์ผ๋ก bindingResult
์ ๋ด๊ธด๋ค.
package com.example.demo.controller.advice;
import javax.validation.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.*;
@ControllerAdvice(basePackages = "com.example.demo.controller.mvc")
public class MyShopAdvice {
@ExceptionHandler(ConstraintViolationException.class)
public String constraintViolationException(ConstraintViolationException e, RedirectAttributes ra) {
ra.addFlashAttribute("msg", e.getMessage());
return "redirect:/system/error";
}
}
ConstraintViolationException
์ ControllerAdvice
์ ์ํด ์ฒ๋ฆฌ๋๋ค.
@NotNull: ํด๋น ๊ฐ์ด null์ด ์๋์ง ๊ฒ์ฆํจ
@NotEmpty: ํด๋น ๊ฐ์ด null์ด ์๋๊ณ , ๋น ์คํธ๋ง("") ์๋์ง ๊ฒ์ฆํจ(" "์ ํ์ฉ๋จ)
@NotBlank: ํด๋น ๊ฐ์ด null์ด ์๋๊ณ , ๊ณต๋ฐฑ(""๊ณผ " " ๋ชจ๋ ํฌํจ)์ด ์๋์ง ๊ฒ์ฆํจ
@AssertTrue: ํด๋น ๊ฐ์ด true์ธ์ง ๊ฒ์ฆํจ
@Size: ํด๋น ๊ฐ์ด ์ฃผ์ด์ง ๊ฐ ์ฌ์ด์ ํด๋นํ๋์ง ๊ฒ์ฆํจ(String, Collection, Map, Array์๋ ์ ์ฉ ๊ฐ๋ฅ)
@Min: ํด๋น ๊ฐ์ด ์ฃผ์ด์ง ๊ฐ๋ณด๋ค ์์ง ์์์ง ๊ฒ์ฆํจ
@Max: ํด๋น ๊ฐ์ด ์ฃผ์ด์ง ๊ฐ๋ณด๋ค ํฌ์ง ์์์ง ๊ฒ์ฆํจ