
저번에 DTO에 대해서 학습한 후, 이제 DTO를 실제 나의 프로젝트에 적용해봤다.
그런데 생각해보면 DTO가 항상 올바른 형태로 들어올까? 잘못된 형태로 들어올 수도 있다고 생각한다. 예를 들면 String 형태로 성별을 저장하는데 갑자기 성별에 Integer 값이 들어온다던가 등등 잘못될 경우는 얼마든지 있다. 잘못된 데이터는 서버 내 잘못 된 작동을 얼마든지 야기할 수 있기 때문에 꼭 올바른 데이터 형태로 받아야 한다.
이런 경우 때문에 DTO는 Validation, 즉 검증이 필요하다. DTO로 정해진 올바른 형태로 값들이 다 들어온건지 검증하는 것이다.
Spring에서는 DTO Validation을 위한 의존성을 추가해야 한다.
implementation 'org.springframework.boot:spring-boot-starter-validation'
현재 내 프로젝트에서는 사용자 일정 추가를 위한 Request DTO가 사용되고, 검증할 내용은 아래와 같다.
public record CreateScheduleRequestDTO(@NotBlank(message = "일정 제목은 필수 입력 값입니다.") String title,
@NotNull(message = "일정 국가는 필수 입력 값입니다.") Country country,
@NotNull(message = "일정 지역은 필수 입력 값입니다.") City city,
@NotNull(message = "시작 날짜는 필수 입력 값입니다.") LocalDate startDate,
@NotNull(message = "끝 날짜는 필수 입력 값입니다.") LocalDate endDate,
String memo
){
...
}
메모를 제외한 나머지는 필수 입력 값으로 들어와야 한다. @NotBlank와 @NotNull의 차이를 간단히 말하자면 @NotNull은 해당 파라미터가 null이면 안되고, @NotBlank는 @NotNull에 " ", "" 등 빈 값이 들어오면 안되는 조건까지 추가된 것이다.
이제 위 DTO에 대해 Controller 계층에서 @Valid 애노테이션으로 검증하는 코드를 추가한다.
@PostMapping
@Operation(summary = "일정 추가")
public ResponseEntity<BaseResponse<Object>> createSchedule(@AuthUser SecurityUser user, @RequestBody @Valid CreateScheduleRequestDTO createScheduleRequestDTO, Errors errors){
if (errors.hasErrors())
throw new ScheduleRequestException(errors);
scheduleService.createSchedule(user.getUser(), createScheduleRequestDTO);
BaseResponse<Object> response = new BaseResponse<>(true, HttpStatus.OK,
ScheduleResultMessage.CREATE_SCHEDULE_SUCCESS.getMessage(), null);
return ResponseEntity.ok().body(response);
}
@Valid 애노테이션은 검증하고자 하는 DTO의 바로 앞에 붙여주면 된다.
DTO 검증에 실패한 경우, errors로 예외처리를 할 수 있게 만들었고, 나는 ScheduleRequestException(errors) 객체를 만들어 잘못 된 데이터들에 대해 아까 쓴 message가 출력되도록 구성했다. ScheduleRequestException에 대한 ExceptionHandler는 아래와 같다.
@ExceptionHandler(ScheduleRequestException.class)
public ResponseEntity<BaseResponse<Object>> handler(ScheduleRequestException e){
ArrayList<String> messages = e.errors.getFieldErrors()
.stream()
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
.collect(Collectors.toCollection(ArrayList::new));
return ResponseEntity.badRequest()
.body(new BaseResponse<>(false, HttpStatus.BAD_REQUEST, "잘못된 입력입니다.", messages));
}
확실히 DTO를 사용하니 @Valid 통해 데이터 유효성 검증도 가능하고 잘못 들어온 데이터가 무엇인지 한 번에 알 수 있는 것도 강한 장점이 되는 것 같다.