📌 데이터 검증 / Validator
: 포맷 데이터를 파라미터로 받아 데이터를 만들고 모델에 담아 뷰에 보여주는 과정.
→ 파라미터가 데이터로서 사용 가능한지 파악하는 단계
- build.gradle update
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
▶️ ContentDto
: 기본적으로 페이지끼리 전달할 데이터 정보
- 폼 데이터의 유효성은 검증 온 두단계에서 이루어진다
1. 클라이언트인 Html 페이지에서 자바스크립트를 통한 검사
2. 서버 페이지인 Jsp.Servlet에서 파라미터로 받은 후 검증
import lombok.Data;
@Data
public class ContentDto {
private int id;
private String writer;
private String content;
}
▶️ ContentValidator
: 필요한 필드만 검증하는 로직 (support, validate) 구현
커맨드 객체를 검증하기 위해 ContentDto 를 사용해, 내용이 null이거나 비어있으면 에러가 발생해다는 로그 출력 및 key-value 형태로 값을 넣어준 것
- 데이터가 유효성 검사에서 통과를 못했다면 errors 객체 변수에 에러내용을 담는다.
모든 요소를 검증할 필요는 없다. 필요한 필드나 (요소)만을 검증하는 로직을 만들면 된다.
public class ContentValidator implements Validator{
@Override
public boolean supports(Class<?> arg0) {
// arg0 : 검증할 클래스 타입 정보
return ContentDto.class.isAssignableFrom(arg0);
}
@Override
public void validate(Object obj, Errors errors) { //원하는 커맨드 객체 obj
ContentDto dto = (ContentDto) obj; //형변환하고 변수에 저장
String sWriter = dto.getWriter(); //컨맨드 객체로부터 작성자의 값을 구해와서
/*
커멘드 객체로 부터 작성자의 값을 구해와서
값이 널이지 공백인지를 체크하는 로직이다
*/
if(sWriter == null || sWriter.trim().isEmpty()) {
System.out.println("Writer is null or empty");
errors.rejectValue("writer", "trouble");
}
String sContent = dto.getContent();
if(sContent ==null || sContent.trim().isEmpty()) {
System.out.println("Content is null or empty");
errors.rejectValue("content", "trouble");
}
}
}
→ 참고! :: `ValidationUtils` 클래스도 이용가능! (아래 예제 continue)
ValidationUtils.rejectIfEmptyOrWhitespace
(errors, "writer", "writer is empty.");
▶️ MyController
@Controller
public class MyController {
// @ResponseBody 에 의해 String "Validator(1)" 이 그대로 호출
@RequestMapping("/")
public @ResponseBody String root() throws Exception {
return "Validator (1)";
}
// createPage.jsp 호출
@RequestMapping("/insertForm")
public String insert1() {
return "createPage";
}
@RequestMapping("/create")
public String insert2(@ModelAttribute("dto") ContentDto contentDto,
BindingResult result) {
// 커맨드 객체 파라미터로 폼 데이터를 받아서 처리
String page = "createDonePage";
System.out.println(contentDto);
// 유효성 검증 객체를 만든다.
ContentValidator validator = new ContentValidator();
validator.validate(contentDto, result); // 파라미터로 받음
// result 값이 있다면 에러가 있다. -> 체크를 한다.
if(result.hasErrors()) {
page = "createPage";
}
// 에러가 없다면 결과페이지 jsp를 호출,에러가 있다면 입력 페이지의 jsp 를 리턴
return page;
}
}
☑️ /create
: createDonePage.jsp 가 호출해야하지만 ContentValidator 클래스와 validate의 데이터 검증과정을 통해 유효한 값이 입력되면 그대로 호출하고 에러가 발생하면 다시 createPage로 돌아간다.
- @ModelAttribute("dto") : jsp 페이지에서 dto이라는 변수명으로 접근이 가능
<%
**String conPath = request.getContextPath();**
%>
<form action = "<%= conPath %>/create" >
작성자 : <input type = "text" **name = "writer" value = "${dto.writer }**"><br/>
내용 : <input type = "text" **name = "content" value = "${dto.content }**"><br/>
<input type = "submit" value ="전송"> <br/>
</form>
</body>
</html>
<!--
request.getContextPath() 는 프로젝트의 경로 를 가져온다.
form형식에 맞게 작성자 부분과 내용 부분을 입력하면 그값은 각각
dto.wrtier, dto.content 값들이 되고 validate함수에 의해 데이터 겁증 과정을 거친다.
-->
▶️ Result
// writer content가 3 자리 이하일때
ContentDto(id=0, writer=작가, content=hello helllo )
getAllErrors: [Field error in object 'dto' on field
'writer': rejected value [작가]; codes [
writer is too short.dto.writer,writer is too short.writer,
writer is too short.java.lang.String,writer is too short];
arguments []; default message [null]]
1:writer is too short
// 완벽한 result
ContentDto(id=0, writer=나는작가, content=hello helllo )
👉 위에서는 공백 처리에 대한 validator을 알아봤다면 글자수도 제한시켜보자!
▶️ ContentValidator
public class ContentValidator implements Validator{
@Override
public boolean supports(Class<?> arg0) {
return ContentDto.class.isAssignableFrom(arg0);
// 검증할 객체의 클래스 타입 정보
}
@Override
public void validate(Object obj, Errors errors) {
ContentDto dto = (ContentDto) obj;
**ValidationUtils.rejectIfEmptyOrWhitespace
(errors, "writer", "writer is empty.");**
String sWriter = dto.getWriter();
/**
* writer 나 content 값이 비어있거나 null 이면 해당 에러를 출력.
* 특히 "writer" 부분은 길이가 3보다 작을 때도 에러 출력
*/
if(sWriter.length() < 3) {
errors.rejectValue("writer", "writer is too short");
}
**ValidationUtils.rejectIfEmptyOrWhitespace
(errors, "content", "content is empty.");**
}
}
▶️ MyContoller
@Controller
public class MyController {
// 약한결합과 디펜던시를 적용시킴
@RequestMapping("/")
public @ResponseBody String root() throws Exception {
return "Valid_initBinder (3)";
}
@RequestMapping("/insertForm")
public String insert1() {
return "createPage";
}
/* @Valid contentDto 객체변수에 대한 유효성 검증을 하겠다고 표시하는 것이다
객체 변수가 들어오면 스프링이 binder변수에 저장된 갹체를 통해서 즉시 유효성 검사를 하고
에러가 있다면 result 변수에 담아둔다.
BindingResult : Validator를 상속받는 클래스에서 객체값을 검증하는 방식
*/
@RequestMapping("/create")
public String insert2(@ModelAttribute("dto") @Valid ContentDto contentDto,
BindingResult result) {
String page = "createDonePage";
System.out.println(contentDto);
// ContentValidator validator = new ContentValidator();
// validator.validate(contentDto, result);
if(result.hasErrors()) {
System.out.println("getAllErrors: " + result.getAllErrors());
if(result.getFieldError("writer") != null) {
System.out.println("1:" + result.getFieldError("writer").getCode());
}
if(result.getFieldError("content") != null) {
System.out.println("1:" + result.getFieldError("content").getCode());
}
page = "createPage";
}
return page;
/**
* ContentValidator 클래스에 유효성 검증 에러를 출력, 스프링에서 제공되는 Api 를 사용
* 에러를 담은 결과만 리턴받기 때문에 MyController 클래스에서 에러를 출력하도록 수정하였다.
*/
}
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new ContentValidator());
}
}
📌 ValidationUtils
: org.springframework.validation.Validator - 애플리케이션에서 사용하는 객체 검증용 인터페이스
Validator - rejecting empty fields
특징
- 어떤한 계층과도 관계가 없다. => 모든 계층(웹, 서비스, 데이터)에서 사용해도 좋다.
- 구현체 중 하나로, JSR-303(Bean Validation 1.0)과 JSR-349(Bean Validation 1.1)을 지원한다. (LocalValidatorFactoryBean)
- DataBinder에 들어가 바인딩 할 때 같이 사용되기도 한다.
인스턴스
boolean supports(Class clazz): 어떤 타입의 객체를 검증할 때 사용할 것인지 결정함
내가 검증해야 하는 인스턴스의 클래스가 이 밸리데어터가 지원하는지 확인하는 메서드(검증할 수 있는 클래스인지 확인하는 메서드)
void validate(Object obj, Errors e): 실제 검증 로직을 이 안에서 구현
실질적으로 검증을 하는 로직. 구현할 때 ValidationUtils 사용하며 편리함.
- 유효성 검사 :
구체적으로 말하면 유효성검사는 웹티어에 묶이지 않아냐 하고 쉽게 지역화해야 하고 이용가능한 어떤 밸리데이터(validator)에 연결할 수 있어야 한다. 스프링은 어플리케이션의 모든 레이어에서 기본이 되고 아주 사용하기 편리한 Validator 인터페이스를 제안했다.
데이터바인딩은 어플리케이션의 도메인 모델(또는 사용자 입력을 처리하려고 사용하는 어떤 객체)에 사용자 입력을 동적으로 바인딩하는데 유용하다. 스프링은 이 작업을 하기 위해서 DataBinder를 제공한다. Validator와 DataBinder는 주로 MVC 프레임워크에서 사용되지만 제한이 있는 것은 아닌 validation 패키지를 구성한다.
✅ validator parameter 분석
public String insert2(@ModelAttribute("dto") @Valid ContentDto contentDto,
BindingResult result)
☑️ @ModelAttribute
- 파라미터로 넘겨준 타입의 오브젝트를 자동으로 생성
@ModelAttribute User user
- 인수를 넘기면 User타입의 user 오브젝트를 자동으로 생성
- 생성된 오브젝트에 HTTP 로 넘어온 값들을 자동으로 바인딩
: HTTP 파라미터는 문자열이기 때문에 오브젝트에 맞는 형 변환이 필요. 스프링 에디터 사용
- 오브젝트로 넘어온 값을 검증
: 사용자가 자체적으로 검증기를 등록해 Validation 체크 진행
☑️ @Valid
- @Valid는 객체에 들어가는 값을 검증해주는 어노테이션
- 유효한 값인지 검증은 소스코드 여러군데서 이루어지기 때문에 불필요한 중복코드가 늘어나고 복잡
☑️ BindingResult
- Validator를 상속받는 클래스에서 객체값을 검증하는 방식
✅ @Valid 어노테이션을 사용하는 방법, BindingResult를 사용하는 방법 두가지로 나뉜다
BindingResult
: join url에 호출될 때 마다 initBinder 함수가 호출되면서 UserInfoValidator 객체를 생성한 후 해당 객체의 validate 함수를 호출해서 값이 유효한지 체크한다
- model : 검증 클래스를 따로 만들어서 거기서 유효값을 체크
- validator : Validator를 인터페이스 상속 후 supports, validate 함수를 재정의; 유효한지 체
public String join(@Validated UserInfo userInfo, BindingResult bindingResult){
System.out.println("name:"+userInfo.getName());
System.out.println("pass:"+userInfo.getPassword());
System.out.println("error:"+bindingResult.hasErrors());
if(bindingResult.hasErrors()){
return "join";
}
return "home";
}
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new UserInfoValidator());
}
@Valid
: hibernate-validatori/ Validator를 상속하는 유효검증 클래스를 만든 후 검증을 구현
public String join(@Validated UserInfo userInfo, BindingResult bindingResult){
System.out.println("name:"+userInfo.getName());
System.out.println("pass:"+userInfo.getPassword());
System.out.println("error:"+bindingResult.hasErrors());
if(bindingResult.hasErrors()){
//에러 리스트는 getAllErrors() 함수로 가져온다
List<ObjectError> list = bindingResult.getAllErrors();
for(ObjectError e : list) {
System.out.println(e.getDefaultMessage());
}
return "join";
}
return "home";
}
public class UserInfo {
@NotEmpty(message="name을 입력해주세요")
private String name;
@NotEmpty(message="비밀번호를 입력해주세요")
@Size(min = 6, max=10, message = "길이가 알맞지 않습니다")
private String password;
@Range(min = 1, max = 5)
private int grade;
}
✅ 스프링에서 바인딩 일어날때 값에 적절한 형변환
✔️ PropertyEditor
: 스프링 기본적으로 제공하는 바인딩용 타입(형) 변환 API
- 커스텀 프로퍼티 에디터를 만들 때는 PropertyEditorSupport 클래스를 상속해서 필요한 메소드(getAsText, setAsText)만 구성
levelPropertyEditor.java - 커스텀 프로퍼티 에디터
static class LevelPropertyEditor extends PropertyEditorSupport {
public void setAsText(String text) throws IllegalArgumentException {
this.setValue(Level.valueOf(Integer.parseInt(text.trim())));
}
public String getAsText() {
return String.valueOf(((Level)getValue()).intValue());
}
}
✔️ InitBinder
: WebDataBinder 초기화 메서드를 사용해서 커스텀 프로퍼티 에디터를 등록할 수 있다.
@InitBinder 메소드
@InitBinder
public void initBinder(WebDataBinder dataBinder){
dataBinder.registerCustomEditor(Level.class, new LevelPropertyEditor());
//dataBinder에 커스텀에디터를 등록한다. Level 오브젝트를 만나면
// LevelPropertyEditor와 연결된다.
}
@initBinder 애노테이션을 통해 level 타입을 무조건 LevelPropertyEditor 와 연결
참고 :

'🌈 Spring Framework > 🌱 Spring-boot' 카테고리의 다른 글
| Lombok 사용시 주의사항 (0) | 2021.05.03 |
|---|---|
| @SpringBootApplication (0) | 2021.02.18 |
| IoC - ApplicationContext / Bean (0) | 2021.02.18 |