RequestBody의 값을 검증하기 위해서 @Valid를 사용합니다.
대부분의 예시를 보면, Validator 를 커스텀해서 어노테이션 형태로 사용합니다.
저는 ChannelTypeValidator 를 만들어서 사용했습니다. APP, PC, MOBILE 등 다양한 채널로 유저가 유입될 수 있습니다.
세가지의 채널을 모두 검사할 수도 있지만, 1~2 종류의 채널만 허용할 수도 있습니다.
그런 상황일 때 Validator 를 추가로 생성하지 않고 구현 클래스 안에서 분기 처리를 하도록 커스텀했습니다.
예시
Validator Interface
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ChannelTypeValidator.class)
public @interface ChannelTypeCheck {
String message() default "채널 타입이 유효하지 않습니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String code();
}
DTO 에서 ChannelType 을 검증할 커스텀 어노테이션입니다.
code 라는 변수를 추가로 정의했습니다. 나머지 변수는 ConstraintValidator 에 정의되어 있습니다.
code를 통해 유효성 검사 로직을 분기해서 처리합니다.
Validator Implement
public class ChannelTypeValidator implements ConstraintValidator<ChannelTypeCheck, Object> {
private String message;
private String code;
@Override
public void initialize(ChannelTypeCheck constraintAnnotation) {
message = constraintAnnotation.message();
code = constraintAnnotation.code();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
boolean flag = true;
if ("VALID_CODE_APM".equals(code)) {
if (!value.equals("APP") && !value.equals("PC") && !value.equals("MOBILE")) {
flag = false;
}
}
if ("VALID_CODE_AP".equals(code)) {
if (!value.equals("APP") && !value.equals("PC")) {
flag = false;
}
}
return flag;
}
}
ConstraintValidator 를 상속받아 실질적으로 유효성 검사 분기 로직을 처리하는 구현부입니다.
정의한 code 를 체크하고 code 에 따른 유효성 검사를 수행합니다.
DTO 1
@Getter
@Builder
@AllArgsConstructor
public class TransferAccountDTO {
@ChannelTypeCheck(code = "VALID_CODE_APM", message = "채널 타입이 유효하지 않습니다. (APP, PC, MOBILE)")
private String channelType;
private Long sendUserId;
private Long receiveUserId;
private int money;
}
모든 ChannelType 을 검사하는 DTO 입니다. code, message 를 정의합니다.
DTO 2
@Getter
@Builder
@AllArgsConstructor
public class TransferAccountValidTestDTO {
@ChannelTypeCheck(code = "VALID_CODE_AP", message = "채널 타입이 유효하지 않습니다. (APP, PC)")
private String channelType;
private Long sendUserId;
private Long receiveUserId;
private int money;
}
모든 APP, PC 채널 타입만 검사하는 DTO 입니다. code, message 를 정의합니다.
Controller
@RestController
@RequiredArgsConstructor
public class AccountController {
private final TransferService transferService;
// 1. 채널 타입이 APP, PC, MOBILE 중 하나인지 체크하는 경우
@PostMapping("/account/apm")
public ResponseEntity<Object> transferAccount(@Valid TransferAccountDTO TransferAccountDTO) {
transferService.transferToAccount(TransferAccountValidTestDTO);
return new ResponseEntity<>(HttpStatus.OK);
}
// 2. 채널 타입이 APP, PC 중 하나인지 체크하는 경우
@PostMapping("/account/ap")
public ResponseEntity<Object> transferAccount(@Valid TransferAccountValidTestDTO transferAccountValidTestDTO) {
transferService.transferToAccount(TransferAccountValidTestDTO);
return new ResponseEntity<>(HttpStatus.OK);
}
}
'Spring' 카테고리의 다른 글
Save, SaveAndFlush 차이 (0) | 2023.11.28 |
---|---|
Save 메서드 동작 원리 (1) | 2023.11.28 |
Valid, Validated 어노테이션 (0) | 2023.09.24 |
조회 쿼리 메서드 사용 시 불필요한 Join 이 사용되는 이유 (0) | 2023.09.14 |
엔티티 저장 시 Select 쿼리를 호출 하는 이유 (0) | 2023.09.14 |