안녕하세요. 오늘은 Spring boot 에서 request에 들어오는 요청값을 검증하는 법을 알아보겠습니다.
현재 제가 공부로 진행중인 프로젝트에서 소스를 조금 가져오겠습니다.
먼저 AccountController
에 있는 createAccount
메소드를 가져오겠습니다.
@PostMapping
public ResponseEntity createAccount(@RequestBody @Valid AccountDto dto, Errors errors) {
if (errors.hasErrors()) {
return ResponseEntity.badRequest().build();
}
Account account = accountService.save(dto);
return ResponseEntity.ok(
AccountDto.builder()
.displayName(account.getDisplayName())
.email(account.getEmail())
.build()
);
}
현재 소스코드에서 보면 @RequestBody
와 @Valid
어노테이션이 AccountDto
요청값에 붙어있는데,
@RequestBody
어노테이션은 @RequestMapping
에 의해 POST 방식으로 전송된 HTTP 요청 데이터를 제가 지정해준 AccouontDto
에 맞춰서 변환해주는 역할을 합니다.
@Valid
어노테이션이 바로 요청데이터를 검증하는 어노테이션입니다.
여기서 그냥 @Valid
어노테이션을 붙인다고해서 바로 검증을 할 수 있는 것은 아니고 실제 AccountDto
에도 필드별로 검증하기 위해 다양한 어노테이션을 붙여줍니다.
@Getter
@Setter
@Builder
@AllArgsConstructor
public class AccountDto {
@NotEmpty @NotBlank
private String displayName;
@NotEmpty @NotBlank @Email
private String email;
@NotEmpty
@NotBlank
@JsonIgnore
private String password;
public Account toEntity(){
return Account.builder()
.displayName(this.displayName)
.password(this.password)
.email(this.email)
.build();
}
}
각 필드마다 위에 @NotEmpty
, @Email
, @NotBlank
등 다양한 어노테이션이 보입니다.
실제로 @javax.validation.constraints.
까지 입력하시고, Ctrl + Space
키를 눌러보시면 하단에
이렇게 많은 어노테이션이 존재합니다.
각 어노테이션은 이름에서도 대충 유추를 할 수 있지만, 더 자세히 알고 싶으시면 여기를 더 참고하시면 좋을 것 같습니다.
이제 이런식으로 각 요청값을 @Valid
어노테이션을 이용하여서 걸러낼 수 있다는 것을 알아봤는데 , 만약 요청값이 @Email
인데 이메일 형식이 아닌값이 들어오는 등, 요청값이 검증에 실패할 경우엔 어떻게 될까요?
위의 컨트롤러 소스코드를 다시보시면 createAccount
의 두번째 인자로 Errors
를 받고 있는 것을 보실 수 있습니다.
Validation 과정에서 실패하거나 에러가 발생하면 이 Errors
에 에러들이 담기기 때문에 저같은 경우는 errors.hasErrors()
를 이용해서 에러 발생시 예외처리를 해주고 있습니다.
하지만 위에 javax.validation.constraints.*
에 제가 필요한 검증이 어노테이션으로 존재하지 않는 경우에는 어떤 방식으로 검증을 해야할까요?
최근 백기선님의 REST API
강의를 하면서 배웠던 방식을 한번 보겠습니다. 예를 들기 위해 임의로 Event
라는 객체를 아주 간단히 만들어 보겠습니다.
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter @Setter
@Entity
public class Event {
@Id
@GeneratedValue
private Long id;
private String name;
private String description;
private LocalDate startDate;
private LocalDate endDate;
}
이를 요청값으로 받을 때는 Entity
클래스가 DTO클래스를 따로 생성하는게 좋기 때문에 이번에는 EventDto
클래스를 만들어 보겠습니다.
@Getter
@Setter
@Builder
@NoArgsConstructor @AllArgsConstructor
public class EventDto {
@NotEmpty
private String name;
@NotEmpty
private String description;
@NotNull
private LocalDate startDate;
@NotNull
private LocalDate endDate;
현재는 위에 방식과 마찬가지로 @Valid
를 사용한 방식만을 사용하고 있습니다.
하지만 현재 만일 startDate 보다 endDate가 더 이른 날짜가 들어온다면 이를 검증할 수 없다는 문제가 있습니다.( startDate = 20190218, endDate = 20190201)
이를 해결하기 위하여 저희는 EventValidator
라는 클래스를 생성할 것입니다.
@Component
public class EventValidator {
public void validate(EventDto eventDto, Errors errors){
if(endDate.isBefore(startDate)){
errors.rejectValue("endDate", "wrongValue", "endDate is wrong");
}
}
이런식으로 EventValidator
를 @Component
로 등록해준 뒤,
EventController
에서 불러와 사용할 수 있습니다.
@Autowired
private EventValidator eventValidator;
@PostMapping
public ResponseEntity createEvent(@RequestBody @Valid EventDto eventDto, Errors errors) {
if(errors.hasErrors()) {
return ResponseEntity.badRequest().body(errors);
}
eventValidator.validate(eventDto, errors);
if(errors.hasErrors()){
return ResponseEntity.badRequest().body(errors);
}
...이벤트 생성코드 작성...
return ResponseEntity.ok().build();
현재는 startDate
와 endDate
만 검증을 해봤는데, 실제로 필요한 검증을 직접 추가하시고 소스코드도 더 리팩토링하셔서 사용을 하시면 될 것 같습니다!
이상으로 오늘은 스프링 부트에서 요청값을 검증하는 방법에 대하여 알아봤습니다!
아직 모르는게 많아 게시글에 잘못된 정보가 있을 수 있습니다. 혹시 잘못된 정보가 있다면, 댓글 혹은 메일로 알려주시면 최대한 빨리 수정하겠습니다!