Bean Validation 3

사나이장대산·2024년 11월 14일

Spring

목록 보기
25/26

Bean Validation의 충돌

  • 요구사항
    • 상품
      • id (식별자)
      • name (이름)
      • price (가격)
      • count (재고)
    • 상품 등록 API
      • 식별자 값은 필수가 아니다.
      • name은 null, “”, “ “을 허용하지 않는다.
      • price는 10 ~ 10000 사이의 숫자로 생성한다.
      • count는 1 ~ 999 사이의 숫자로 생성한다.
    • 상품 수정 API
      • 식별자 값이 필수이다.
      • name은 null, “”, “ “을 허용하지 않는다.
      • price는 무제한으로 허용한다.
      • count는 1 ~ 999 사이의 숫자로 생성한다.

일반적으로 수정 API를 만들 때 식별자 id값을 항상 Controller에서 받도록 구성한다. HTTP 요청은 사용자가 임의로 변경하여 요청할 수 있음으로 항상 서버에서 최종적으로 추가 검증을 진행 해야한다. ex) 게시글 수정시 요청자 본인이 쓴 글인지 확인한다.

  • Product 저장 API
@Data
public class ProductRequestDto {

		// 식별자는 Database에서 자동생성
		
    @NotBlank
    private String name;

    @NotNull
    @Range(min = 10, max = 10000)
    private Integer price;

    @NotNull
    @Range(min = 1, max = 999)
    private Integer count;
}

@Slf4j
@RestController
public class ConflictValidationController {

    @PostMapping("/product")
    public String save(
            @Validated @ModelAttribute ProductRequestDto requestDto
    ) {
				log.info("생성 API가 호출 되었습니다.");
        // Validation 성공시 repository 저장로직 호출
        return "상품 생성이 완료되었습니다";
    }

}
  • Postman
    - 정상 요청

  • Controller 정상 호출

  • Validation

    Controller 호출되지 않음

  • Product 수정 API

@Data
public class ProductRequestDto {
	
		@NotBlank
    private String name;

		// price 무제한 요구사항 반영
    @NotNull
    private Integer price;

    @NotNull
    @Range(min = 1, max = 999)
    private Integer count;
}

@Slf4j
@RestController
public class ConflictValidationController {
    @PutMapping("/product/{id}")
    public String update(
            @PathVariable Long id,
            @Validated @ModelAttribute ProductRequestDto test
    ) {
        log.info("수정 API가 호출 되었습니다.");
        // Validation 성공시 repository 수정로직 호출
        return "상품 수정이 완료되었습니다.";
    }
}

@PathVariable의 required 속성의 기본값은 true이다.

  • Postman
    - 수정

  • Controller 정상 호출

  • 저장

  • price 필드에 @Range 가 사라져서 저장시에도 제한없이 저장됨

  • 해결방법

    1. 저장할 Object를 직접 사용하지 않고 SaveRequestDto, UpdateRequestDto 따로 사용한다.
    2. Bean Validation의 groups 기능을 사용한다.

groups

Bean Validation의 groups 속성은 다양한 유효성 검사 시나리오를 정의할 때 사용된다. 동일한 객체에 대한 검증을 상황에 따라 다르게 적용하고 싶을 때 groups를 활용할 수 있다.

  • groups 속성 적용
// 저장용 group
public interface SaveCheck {

}

// 수정용 group
public interface UpdateCheck {

}

@Data
public class ProductRequestDtoV2 {

    // 저장, 수정 @NotBlank Validation 적용
    @NotBlank(groups = {SaveCheck.class, UpdateCheck.class})
    private String name;

    // 사용하는 모든곳에서 @NotNull Validation 적용
    @NotNull
    // 저장만 @Range 반영
    @Range(min = 10, max = 10000, groups = SaveCheck.class)
    private Integer price;

    @NotNull
    @Range(min = 1, max = 999)
    private Integer count;

}

@Slf4j
@RestController
public class ProductController {

    @PostMapping("/v2/product")
    public String save(
            // 저장 속성값 설정
            @Validated(SaveCheck.class) @ModelAttribute ProductRequestDtoV2 requestDtoV2
    ) {
        log.info("생성 API가 호출 되었습니다.");
        // Validation 성공시 repository 저장로직 호출
        return "상품 생성이 완료되었습니다";
    }

    @PutMapping("/v2/product/{id}")
    public String update(
            @PathVariable Long id,
            // 수정 속성값 설정
            @Validated(UpdateCheck.class) @ModelAttribute ProductRequestDto test
    ) {
        log.info("수정 API가 호출 되었습니다.");
        // Validation 성공시 repository 수정로직 호출
        return "상품 수정이 완료되었습니다.";
    }

}
  • Postman
  • 저장 Validation
  • 수정

groups VS DTO 분리

Bean Validation의 충돌이 발생하는 경우 대부분 DTO를 분리하는 방법이 적절하다.

  • groups VS DTO 분리

    • groups 속성을 사용하면 등록과 수정시 각각 다르게 Validation이 적용된다.
      • 가독성이 떨어지고 코드 복잡도가 올라간다.
    • 실무에서는 등록 폼과 수정 폼 자체를 분리해서 사용하기 때문에 DTO 분리 방법을 사용하면 된다.
    • 단, 네이밍은 일관성있게 작성해야 한다.(SaveRequestDto, UpdateRequestDto)
  • DTO 분리

    • 실제로 간단한 프로젝트를 개발해보면 저장, 수정시 Request가 비슷한 경우가 있다.
    • 각각의 장단점이 존재하지만 어설프게 하나로 합칠 경우 유지보수시 엄청난 경험을 할 수 있다.
    • RequestDto가 변한다는건 해당 API의 스펙 자체가 변경되어 많은 수정이 발생한다.
    • 실무에서는 거의 발생하지 않는 경우기 때문에 간단한게 아니라면 대부분 분리한다.
  • @Validated VS @Valid

  1. @Validated

  • 속성값이 존재한다.
  • spring이 제공하는 Annotation
  1. @Valid

  • 속성값이 존재하지 않는다, groups 기능 지원하지 않는다.
  • groups 기능을 사용하려면 @Validated를 사용해야 한다.
  • Java 표준 Annotation
profile
사나이 張大山 포기란 없다.

0개의 댓글