Spring

Validator

요청으로 들어오는 값의 유효성 검사를 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);
    }