요청으로 들어오는 값의 유효성 검사를 Validator를 이용해 진행해보자.
우선, 내가 검증할 내용은 다음과 같다.
public class StationRequest {
private String name;
public String getName(){
return name;
}
}
StationRequest라는 객체가 존재한다. 이 객체는 이름을 받아 넘겨주는 DTO의 역할을 수행한다.
여기서 제약 사항은, name은 널이 되어서는 안되며 무조건 이름 뒤에는 "역"이 붙어야한다. (ex. 강남역)
따라서 이 검증을 Validator에게 맡겨보자.
우리가 만들 Custom Validator는 Validator를 구현한다.
Validator에는 필수로 구현해야하는 메서드들이 존재하는데, supports와 validate이다.
supports는 검증을 수행할 클래스를 의미한다. 우리는 여기서 StationRequest를 전달받은 뒤 검증을 수행할 것이므로 StationRequest를 할당한다.
validate는 실제 검증 로직을 수행하는 클래스이다. 우리는 역 이름이 null인지, "역"을 포함한 이름인지를 검증하는 로직을 담는다. 만약 검증 결과가 올바르지 못한 경우에는 errors에 우리가 원하는 에러 값을 담을 수 있다.
public class StationValidator implements Validator {
private static final Pattern PATTERN = Pattern.compile("^[가-힣|A-Z|a-z| 0-9]*역$");
@Override
public boolean supports(Class<?> clazz) {
return StationRequest.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
StationRequest stationRequest = (StationRequest) target;
String name = stationRequest.getName();
if (name == null) {
errors.rejectValue("name", "required", "역 이름은 필수로 입력해야합니다.");
} else if (!PATTERN.matcher(name).matches()) {
errors.rejectValue("name", "bad", "올바르지 않은 역 이름입니다.");
}
}
}
이제, 이 Validator를 Controller에 바인딩해주자.
우선, 여기서는 javax.validate 패키지를 사용하지 않을 것이기 때문에 사용하는 메서드 내부에서 Validator를 바인딩해주는 방법을 이용할 것이다.
public ResponseEntity<StationResponse> createStation(@RequestBody StationRequest stationRequest, Errors errors) {
new StationValidator().validate(stationRequest, errors);
if (errors.hasErrors()) {
return ResponseEntity.badRequest().build();
}
StationResponse stationResponse = stationService.create(stationRequest.getName());
return ResponseEntity.created(URI.create("/stations/" + stationResponse.getId())).body(stationResponse);
}
error가 발생한 경우, 두번째 파라미터로 받은 Errors에 담기므로 errors.hasErrors()를 통해 잡을 수 있다.
만약 javax.validate를 사용한다면 다음과 같이 validator를 바인딩할 수 있다.
@InitBinder("Station")
public void initBinderForEvent(WebDataBinder webDataBinder) {
webDataBinder.addValidators(new StationValidator());
}
public ResponseEntity<StationResponse> createStation(@RequestBody @Valid StationRequest stationRequest, BindingResults bindingResults) {
if (bindingResults.hasErrors()) {
return ResponseEntity.badRequest().build();
}
StationResponse stationResponse = stationService.create(stationRequest.getName());
return ResponseEntity.created(URI.create("/stations/" + stationResponse.getId())).body(stationResponse);
}