프로그래밍

[SpringBoot] day89 : 유효성검사(Validator) 본문

자바/Spring Boot

[SpringBoot] day89 : 유효성검사(Validator)

시케 2023. 9. 15. 16:33
728x90
반응형

2023.09.15.금

유효성검사

유효성 검사를 하는데에는 2가지 경우가 있다

1) 클라이언트

웹브라우저, 프론트 단에서 실행하는 방법이며

HTML, JSP 페이지에서 JS를 통해 사용자의 입력값을 검사하는 방식이다

네트워크(트래픽) 낭비를 방지할 수 있어 서버 부하가 적어진다는 장점이 있다

 

2) 서버

백, 개발자 단에서 검사하는 방식이다

클라이언트에서 수행할 수 있는 유효성 검사에 대해서는

잘못된 URL 호출에 대한 유효성 검사는 불가하기 때문에 반드시 필요한 부분이다

 

Q. 사용자의 악의적인 접근을 어떻게 막을까요?

더보기

사용자의 악의적인 접근

즉, 잘못된 URL 호출은 JS로 유효성검사를 하는 것은 한계가 있기 때문에

JAVA를 통해 유효성 검사를 해야합니다

VIEW / CONTROLLER 주의사항

더보기

1. 커맨드 객체 
멤버변수 이름이 각각 id, password 여야 함
name을 보고 해당 판단을 내림
2. model.add() 수행시
id값 이름이 apple이여야 함
3. 해당 요청의 값이 test(POST)여야 함

 

뷰를 통해 해당 정보들을 파악하고 컨트롤러를 작성할 수 있어야 한다

 

Validator 인터페이스를 통한 구현

기존에 진행하던 null 검사 혹은 길이 검사 등은 너무 기본적인 검사이기 때문에

boot에서 이미 제공하고 있다

부트 미사용시 모듈화를 개발자가 직접 작성하는 것이 최선이다

※부트에서 제공하는 것 또한 모듈화의 일종

 

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class VOValidator implements Validator {

	@Override
	public boolean supports(Class<?> clazz) {
		// 유효성 검사할 객체의 클래스 정보를 반환
		return VO.class.isAssignableFrom(clazz);
	}

	@Override
	public void validate(Object target, Errors errors) { // Erros errors 또한 커맨드객체, 참조 변수
		// target: 유효성 검사할 객체
		// errors: 검증이 통과되지 못한 경우, 왜 통과가 되지 않았는지를 반환(이유를 반환)
		
		VO vo = (VO)target;
		String id = vo.getId();
		String password = vo.getPassword();
		
		if(id == null || id.isEmpty() || id.isBlank() || id.trim().isEmpty() || id.trim().isBlank()) {
	         System.out.println("로그: id값이 올바르지 않습니다");
	         errors.rejectValue("id", "id값 없음");
		}
		if(vo.getPassword().length() <= 5) {
			System.out.println("로그: id값이 올바르지 않습니다(id값 5글자 이하)");
			errors.rejectValue("id", "id값 5글자 이하");
		}
		
		//ValidationUtils.rejectIfEmptyOrWhitespace(errors, password, "id값 없음");
		if(password == null || password.isEmpty() || password.isBlank() || password.trim().isEmpty() || password.trim().isBlank()) {
	         System.out.println("로그: password값이 올바르지 않습니다");
	         errors.rejectValue("password", "password값 없음");
		}
	
	}
}

강제하고 싶은 형태가 있기 때문에 인터페이스를 통하여 구현한다

 

@Controller
public class CTRL {
	// 기존 방식
	@RequestMapping(value="/test", method = RequestMethod.GET)
	public String test(VO vo, Model model){
		/////
		if(vo.getId() == null || vo.getId().equals("") || vo.getId().isEmpty() || vo.getId().isBlank()) {
			
		}	
		if(vo.getPassword().length() <= 5) {
			
		}
		/////	
		model.addAttribute("apple", vo.getId());
		return "test";
	}
}
@RequestMapping(value="/test", method = RequestMethod.POST)
public String test2(VO vo, BindingResult br, Model model){
	/////
	VOValidator voV = new VOValidator();
	voV.validate(vo, br); // br는 커맨드객체(참조변수)
	if(br.hasErrors()) {
		System.out.println("로그: 에러발생함!");
	}
	/////
		
	model.addAttribute("apple", vo.getId());
	return "test";
}

 

기존의 방식은 일일히 입력해야 했지만 부트에서 제공하는 인터페이스를 통해 구현한다면 해당 수고를 덜 수 있으며 코드가 간결해진다

 

BindingResult 객체는 커맨드객체이기 때문에 참조변수이다

유효성 검사를 했을때 유효성 검사를 통과 못한 항목이 여러 개일 수도 있으므로

해당되는 여러가지 상황을 받아오기 위해 해당 객체를 만들어 처리한 것이다

(자바에서는 리턴을 하나만 할 수 있기 때문)

 

Errors 객체 또한 외부에서 실제로 들어오는 것이 아닌

많은 양의 에러를 한 번에 반환하기 위해서 객체로 만들어 반환한 것이다

 

new를 코드에서 직접 작성할 경우 유지보수에 불리해지며

컨테이너에게 관리를 맡긴 것이고

컨트롤러에서 미리 만들어서 넘겨주어야 사용 가능하기 때문에 BindingResult 객체를 만든 것이다

 

하지만 현재 코드에도 직접 new를 하는 부분이 있기 때문에

이를 보다 유지보수에 유리한 방향으로 변경해보도록 하자

 

@InitBinder 활용

@InitBinder 어노테이션을 활용한 클래스를 따로 만들어 활용하는 방식이다

해당 어노테이션 클래스를 만든 후

유효성 검사를 하고 싶은 VO에 @Valid 를 통하여 명시해주면 된다

@RequestMapping(value = "/test", method = RequestMethod.POST)
public String test2(@Valid VO vo, BindingResult br, Model model) {

	/* **/
	if (br.hasErrors()) {
		System.out.println("로그: 에러발생함!");
		System.out.println(br.getAllErrors());

		if (br.getFieldError("id") != null) {// id에서 Error가 발생했다는 의미
			System.out.println(br.getFieldError("id").getCode());
		}
	}
	/* **/

	model.addAttribute("apple", vo.getId());
	return "test";
		 
}
@InitBinder
protected void initBinder(WebDataBinder wdb) {
	wdb.setValidator(new VOValidator());
}

 해당 방식을 사용하면 new가 코드 내부에서 사라지기 때문에 유지보수가 용이해지지만

클래스를 따로 만들어 주어야 한다는 점이 번거로워질 수 있다

 

Validation 의존성 사용

dependencies {
	//...
	implementation 'org.springframework.boot:spring-boot-starter-validation' //validation
}

먼저 해당 의존성을 추가해준다

 

@Data
public class VO {
	@NotNull(message = "id값 NULL")
	@NotEmpty(message = "id값 Empty")
	@Size(min=5, max=100, message = "id값 6이상 100이하로 가능")
	private String id;
	
	@NotNull(message = "password값 NULL")
	@NotEmpty(message = "password값 Empty")
	private String password;
}
@RequestMapping(value = "/test", method = RequestMethod.POST)
public String test2(@Valid VO vo, BindingResult br, Model model) {

	/* **/
	if (br.hasErrors()) {
		System.out.println("로그: 에러발생함!");
		System.out.println(br.getAllErrors());

		if (br.getFieldError("id") != null) {// id에서 Error가 발생했다는 의미
			System.out.println(br.getFieldError("id").getDefaultMessage());
		}
	}
	/* **/

	model.addAttribute("apple", vo.getId());
	return "test"; 
}

VO 클래스에서 유효성 검사 조건과 메세지를 지정할 수 있으며

컨트롤러 파트에서 출력 또한 가능하다

 

 

소스코드 보러가기

 

728x90
반응형
Comments