[ 데이터 유효성 검사 ]
1.
데이터 유효성 검사는 프론트와 백엔드 양쪽에서 각각 잡아 줘야 한다.
1)
클라이언트 == 웹 브라우저 == 프론트 == 사용자
>>
HTML, JSP 페이지 에서 Javascript 를 통해 사용자의 입력값을 검사 하기.
보통 프론트에서 데이터 유효성을 검사하는 이유는 아래와 같다.
네트워크 == 트래픽 낭비를 방지 하기 때문 이다.
즉, 서버의 부하가 적어 지기 때문에, 프론트에서 데이터 유효성을 검사 해야 한다.
만약, 클라이언트가 어떤 버튼을 눌러서 서버로 요청을 보내는순간 네트워크를 사용 하게 된다.
하지만, 프론트에서 데이터 유효성을 검사하는것은 만능이 아니다.
왜냐하면 상단 URL에 직접적으로 요청값을 작성하는 경우라면,
즉, 잘못된 URL 호출에 대해서 추가적인 유효성 검사를 해야하는데,
이런 경우는 서버 에서 유효성 검사가 작성 되어야만 한다.
2)
서버 == 백 == 개발자
>>
잘못된 URL 호출에 대해 유효성 검사는 왜 필요 할까 ?
사용자의 악의적인 접근을 어떻게 막을 수 있을까 ? 라는 질문에는 이렇게 정리 할 수 있다.
Javascript (프론트) 으로 유효성 검사를 하는것은 한계가 있기 때문에,
Java (서버) 에서 유효성 검사를 진행 해야 한다.
2.
유효성 검사를 작성한다면, 그냥 하드코딩을 통해서 작성 하면 된다.
if(vo.getId() == null || vo.getId().equals("") || vo.getId().isBlank() || vo.getId().isEmpty()) {
}
if(vo.getPassword().length() <= 5) { }
하지만, 유효성 검사 로직을 모듈화를 해보려고 한다.
단순하게 모듈화 클래스를 만들면 될까 ?
그렇게 했었다면, Spring Boot 에서는 유효성 검사 모듈화 인터페이스가 있어서 그것을 사용 하면 된다.
3.
Spring Boot 에서 유효성 검사 관련해서 모듈화 해놓은 인터페이스가 존재 한다.
>>
Validator 인터페이스 이다.
Vaildator 인터페이스를 구현 받은 클래스를 하나 생성 하면 된다.
코드 확인 하기
package com.hwan.app;
public class VOValidatorTest {
}
에 implement 키워드를 추가 하여 인터페이스를 구현한 클래스를 생성 한다.
package com.hwan.app;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class VOValidatorTest implements Validator {
@Override
public boolean supports(Class<?> clazz) {
// TODO Auto-generated method stub
return false;
}
@Override
public void validate(Object target, Errors errors) {
// TODO Auto-generated method stub
}
}
그러면 위와 같이 미완성 메서드를 강제로 오버라이딩 받게 된다.
@Override
public void validate(Object target, Errors errors) {
}
위의 메서드는 다음과 같다.
Object target은 유효성을 검사할 객체 그 자체를 의미 한다.
Errors errors는 유효성을 검사할 진행했는데, 검증 통과를 못했으면 왜 못했는지 이유를 반환하는 객체 이다.
@Override
public void validate(Object target, Errors errors) {
VO vo = (VO) target; // 다운 캐스팅.
String id = vo.getId();
if(id == null || id.equals("") || id.isBlank() || id.isEmpty() || id.trim().isBlank() || id.trim().isEmpty()) {
System.out.println("log : id cannot be empty");
errors.rejectValue("id", "id cannot be empty");
}
if(id.length() <= 5) {
System.out.println("log : id cannot be less than 5 length");
errors.rejectValue("id", "id cannot be less than 5 length");
}
String password = vo.getPassword();
if(password == null || password.equals("") || password.isBlank() || password.isEmpty() || password.trim().isBlank() || password.trim().isEmpty()) {
System.out.println("log : password is wrong");
errors.rejectValue("password", "password is wrong");
}
}
} // VOValidator
이런식으로 유효성 모듈화 클래스에서 유효성 검사 로직을 작성 할 수 있겠다.
그리고,
javax.validation.Xxx.Xxx 를 import 하기 위해서 라이브러리를 추가 해야 한다.
bulid.gradle 설정 파일에, 아래의 코드를 추가 한다.
implementation 'org.springframework.boot:spring-boot-starter-validation'
그리고, 자동으로 라이브러리를 업데이트 하지 않으므로, build.gradle 을 새로고침 해주면 된다.
이것을 추가하지 않고 아래의 작업을 진행 하면 다음과 같이 import 가 되지 않게 된다.
라이브러리를 추가 하게 되면,
컴파일 에러가 사라지게 된다 ~
4.
CTRL 코드 확인 하기.
서버 == 백엔드 클래스에서 유효성 로직을 구현해뒀으니,
이것을 사용할 컨트롤러 클래스가 필요 하다.
package com.hwan.app;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class CTRL {
@RequestMapping(value = "/")
public String root() {
return "test";
}
@RequestMapping(value = "/test")
public String test(VO vo, BindingResult br, Model model) {
// BindingResult br 이러한 객체를 vov.validate(vo, br) 의 인자로 넣기 위해서는
// BindingResult br 객체가 있어야 한다.
// 직접 new를 하지 않기 위해서 커맨드 객체로 작성 했다.
// ===== 유효성 코드 작성 하기 시작 =====
VOValidator vov = new VOValidator();
vov.validate(vo, br); // br은 커맨드 객체 (참조 변수) 이다.
if(br.hasErrors()) {
System.out.println("log : error !");
System.out.println(br.getAllErrors()); // 에러 메세지 보기 ~ (반환타입이 List 라서 for문과 같이 사용 한다)
}
if(br.getFieldError("id") != null) { // id 에서 에러가 발생 했다는 의미.
System.out.println(br.getFieldError("id").getCode());
}
if(br.getFieldError("password") != null) { // password 에서 에러가 발생 했다는 의미.
System.out.println(br.getFieldError("password").getCode());
}
// ===== 유효성 코드 작성 하기 끝 =====
model.addAttribute("apple", vo.getId());
return "test";
}
} // CTRL
모듈화 클래스 내부에 있는
public void validate(Object target, Errors errors)
위의 메서드를 사용 하기 위해서, 이 컨트롤러 클래스에서 작성하여 메서드를 호출 하는 것을 볼 수 있다.
vov.validate(vo, br);
target이 되는 객체는 VO 타입의 vo 객체이고,
errors 파라미터가 받는 객체는 참조변수 br 이다.
저 둘은 모두 test() 메서드의 인자로 사용 되어, 커맨드 객체로 사용 되고 있다.
그러면 저 객체의 주소값들이 vov.validate() 메서드의 인자로 들어가고, 그대로 같은 주소값도 가리킬 수 있게 된다.
그렇게 유효성 검사를 하고 나서 에러가 발생 했다면,
hasErrors() 메서드를 통해 체크하고, getAllErrors() 메서드를 통해서 에러 내용을 볼 수 있게 된다.
또한,
getFieldError() 메서드로 디테일하게 타겟이 되는 객체안의 프로퍼티를 직접적으로 확인 해서,
getFieldError().getCode() 메서드를 사용 해서 사용자가 정의했던 에러 내용을 불러 올 수 있다.
다양한 메서드들이 존재 하는데, 확인 해서 원하는 메서드를 사용 하면 된다.
이런식으로 확인 할 수 있다.
5.
하지만, 모듈화 클래스에서 유효성 검사 로직 자체를 직접 작성 하는것은 사실 하드코딩이 된다.
if(id == null || id.equals("") || id.isBlank() || id.isEmpty() || id.trim().isBlank() || id.trim().isEmpty()) {
if(id.length() <= 5) {
if(password == null || password.equals("") || password.isBlank() || password.isEmpty() || password.trim().isBlank() || password.trim().isEmpty()) {
그래서, 어노테이션을 사용해서 할 수 있는 방법이 존재 한다.
바로 유효성 검사를 진행할 타겟이 되는 커맨드 객체에 @Vaild 어노테이션을 명시해주는 것이다.
지금은 아이디와 비밀번호 유효성을 검사하는 상황이므로,
해당 프로퍼티들을 가지고 있는 VO 객체에 @Vaild 어노테이션을 명시해주면 된다.
public String test(@Valid VO vo, BindingResult br, Model model) {
그리고,
VO 클래스에 어노테이션 설정을 추가하면 된다.
import lombok.Data;
@Data
public class VO {
private String id;
private String password;
} // VO
기존에 위와 같이 작성된 VO 클래스의 코드를 아래와 같이 작성 하면 된다.
package com.hwan.app;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class VO {
@NotNull(message="id is null") // null 이면 안된다는 것을 명시해주는 어노테이션 이다.
@NotEmpty(message="id is empty") // empty 이면 안된다는 것을 명시해주는 어노테이션 이다.
@Size(min=5, max=100, message="id must over six and under 100") // size 조건을 걸어주는 것을 명시해주는 어노테이션 이다.
private String id;
@NotNull(message="password is null") // null 이면 안된다는 것을 명시해주는 어노테이션 이다.
@NotEmpty(message="password is empty") // empty 이면 안된다는 것을 명시해주는 어노테이션 이다.
private String password;
} // VO
[ 참고 ]
위의,
VO 객체와 VO 클래스에 @Vaild 어노테이션을 명시하는 방법 이전에는 아래의 방법으로도 했었다.
하지만,
최근에는 @ 어노테이션 방식으로 많이 사용하므로 현재는 잘 사용되지 않는 방법인데,
바로 다음과 같다.
CTRL.java
@InitBinder
protected void initBinder(WebDataBinder wdb) {
wdb.setValidator(new VOValidator());
}
컨트롤러 클래스 내부에 위의 메서드를 작성 하고, @InitBinder 어노테이션을 명시 한다.
물론 위의 방법도 @InitBinder 이라는 어노테이션을 사용했지만, 결국 메서드가 있어야 한다.
내가 만든 VOValidator 라는 모듈화 클래스가,
컨트롤러 클래스가 동작했을 때,
동작 하기 전에 먼저 Validator을 new 하기 위한 메서드 이다.
Spring Boot 에서 어노테이션을 사용한 유효성 검사는 이정도로 정리 하고,
추가로 공부 해서 추후에 다시 정리할 예정 이다.
'SpringBoot > 이론' 카테고리의 다른 글
[ Spring Boot ] 서비스 레이어와 인터페이스의 장점 (0) | 2023.09.21 |
---|---|
[ Spring Boot ] JDBC 연결 (0) | 2023.09.19 |
[ Spring Boot ] C - V 간의 다양한 방법으로 데이터 주고 받기 (0) | 2023.09.14 |
[ Spring Boot ] Lombok (롬복) 설치 및 설정 + 프로젝트 기본 설정 (0) | 2023.09.14 |
[ Spring Boot ] 정적 리소스 사용 방법 (0) | 2023.09.14 |