static class ResponseDto {
private String name;
private String result;
}
DTO는 계층 간 데이터 전달을 위해 사용하는 객체이다. DTO는 순수하게 데이터를 전송하기 위한 객체이기 때문에 비즈니스 로직은 포함되어 있지 않다.
ex) 유저가 DTO에 데이터를 담아서 전송하는 경우, 서버가 DTO를 이용하여 유저에게 데이터를 전송하는 경우 등
Controller에서 코드를 통해 유효성 검사를 할 수도 있지만, DTO에서 유효성 검사를 해야겠다고 생각한 이유는 다음과 같다. 코드로 유효성 검사를 할 경우 중복 코드, 필요 없는 코드가 많아져 코드가 지저분해진다고 느꼈다. 또한 DTO는 데이터를 주고 받는 역할을 하는 만큼 데이터에 관련된 코드는 DTO에서 모두 처리하는 게 더 깔끔하다고 생각이 되어 DTO에서 유효성 검사를 최대한 진행하기로 했다.
사람들이 헷갈리기 쉬운 @NotNull, @NotEmpty, @NotBlank에 대해 조금 더 자세히 알아보겠다!
@NotNull
@NotNull은 해당 값이 null인지 아닌지 확인한다. “” 혹은 “ “ 등 null이 아닌 것들은 모두 통과된다.
@NotEmpty
@NotEmpty는 해당 값이 null 또는 비어 있는지 확인한다. 이 때 비어 있다는 것은 “”를 의미한다. String, Collection, Map, Array에 적용할 수 있다.
@NotBlank
@NotBlank는 해당 값이 null 또는 공백이 아닌지 확인한다. null, “”, “ “ 모두 허용하지 않는다. @NotBlank
는 텍스트에만 적용할 수 있다.
@Pattern(regexp = )
: 해당 값이 정규표현식과 일치하는지 확인한다
@*AssertTrue
: 해당 값이 true 인지 확인한다
@Size(min = , max = )
: 해당 값의 크기가 min과 max 사이인지 확인한다. String, Collection, Map, Array에 적용할 수 있다
@Min(value = )
, @Max(value = )
: @Min
은 해당 값이 min보다 크거나 같은지 확인한다. @Max
는 해당 값이 max보다 작거나 같은지 확인한다. @Min
과 @Max
는 숫자에 적용할 수 있으며, double과 float에는 사용할 수 없다.
@Email
: 유효한 이메일 주소인지 확인한다.
@Positive
, @PositiveOrZero
: 해당 값이 양수인지 확인한다. 숫자에 적용되며 @PositiveOrZero
는 0도 포함해서 확인한다.
@Negative
, @NegativeOrZero
: 해당 값이 음수인지 확인한다, 숫자에 적용되며 @NegativeOrZero
는 0도 포함해서 확인한다.
@Past
, @PastOrPresent
: 날짜 값이 과거인지 아닌지 확인한다. @PastOrPresent
는 현재까지 포함해서 확인한다. 날짜 유형에 적용할 수 있다.
@Future
및 @FutureOrPresent
는
: 날짜 값이 미래인지 아닌지 확인한다. @FutureOrPresent
는 현재까지 포함해서 확인한다. 날짜 유형에 적용할 수 있다.
implementation 'org.springframework.boot:spring-boot-starter-validation'
어노테이션으로 조건을 정해주고, message 속성을 통해 사용자에게 어떤 메세지를 보여줄지 정할 수 있다.
@NotBlank(message = "이름을 입력해야 합니다.")
@Pattern(regexp = "^[가-힣A-Za-z0-9]+$", message = "보관함 이름에는 한글, 영문, 숫자만 가능합니다")
@Size(min = 1, max = 5, message = "이름은 한글자 이상, 다섯글자 이하입니다.")
String name;
@Min(value = 1000, message = "비밀번호는 4자리여야 합니다.")
@Max(value = 9999, message = "비밀번호는 4자리여야 합니다.")
int password;
@NotBlank(message = "걱정 시작 시간을 입력해야 합니다.")
@Pattern(regexp = "^(1[0-2]|[1-9]):[0-5][0-9](AM|PM|am|pm)$", message = "걱정 시간 형식은 1:00~12:59(AM/PM)입니다")
String startTime;
@NotBlank(message = "걱정 종료 시간을 입력해야 합니다.")
@Pattern(regexp = "^(1[0-2]|[1-9]):[0-5][0-9](AM|PM|am|pm)$", message = "걱정 시간 형식은 1:00~12:59(AM/PM)입니다")
String endTime;
@Valid
어노테이션을 달아서 검증을 하겠다고 알려준다@PostMapping("/join")
public BaseResponse<PostUserRes> join(@Valid @RequestBody PostJoinReq postJoinReq) {
// 코드 작성
}
GlobalExceptionHandler
를 작성한다@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public BaseResponse<String> BaseException(MethodArgumentNotValidException e) {
// 에러 메시지 추출
String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
log.error(errorMessage);
// 적절한 BaseResponseStatus 사용
return new BaseResponse<>(BaseResponseStatus.INVALID_PARAMETERS, errorMessage);
}
}
확실히 DTO에서 유효성 검사를 진행하니 코드가 한결 깔끔해진 거 같다. 그리고 DTO에서 데이터 구조 정의, 데이터 유효성 검사까지 모두 진행하니 한 눈에 보기 편하고 명확해진 느낌이다.
Spring - DTO Validation
[Spring Boot] @NotNull, @NotEmpty, @NotBlank 의 차이점 및 사용법
Spring Validation Annotation 총정리
Java Bean Validation Basics | Baeldung
DTO란 무엇이고 왜 사용해야 할까?