안녕하세요 오늘은 Validation을 효과적으로 진행하기 위한 방법에 대해 포스팅해보도록 하겠습니다.
...
// 존재하지 않은 이메일인 경우
if(!validationService.checkExistEmail(loginRequestDTO)){
return ResponseEntity.ok(new ResponseDTO(
{예외 처리 내용 출력}
));
}
...
우선 기존 방식의 경우 controller 내부에 직접 Validation을 진행하였습니다. 그러다보니 controller 내부에 API가 많아짐에 따라 controller 내부의 코드가 너무 많아 추후 유지 보수 시 가독성이 크게 떨어진다는 단점이 존재합니다.
따라서 이를 해결하기 위해 Validation을 ControllerAdvice를 이용하여 중앙에서 일괄적으로 처리하는 로직을 작성했습니다. 이 방식은 다음과 같이 동작합니다.
@Component
@RequiredArgsConstructor
public class LoginRequestValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(LoginRequestDTO.class);
}
@Override
// 새로 만든 ValidationException으로 벨리데이션 내용과 함께 에러 주입
// ValidationException을 캐치하는 ExceptionHandler에서 에러 받아서 처리
public void validate(Object target, Errors errors) {
LoginRequestDTO loginRequestDTO = (LoginRequestDTO) target;
// 빠진 내용이 있는 경우
if(ObjectUtils.isEmpty(loginRequestDTO.getId()) || ObjectUtils.isEmpty(loginRequestDTO.getPw()))
throw new ValidationException({코드},{메시지});
...
}
}
각 API의 요청값마다 Validator를 implements한 Validator를 커스텀합니다. 크게 supports와 validate를 오버라이딩하는데, supports의 경우 파라미터의 값이 검증하고자 하는 요청의 타입과 같은지 여부를 확인합니다.
@AllArgsConstructor
@Getter
public class ValidationException extends RuntimeException{
int code;
String message;
}
여기서 같은 값이라면 validate 메소드에서 로직을 처리합니다. 요청값인 target을 형변환 후 내부에서 Validation 처리를 진행합니다. 이 때 ValidationException을 주어 코드와 메시지 정보를 전달합니다.
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(ValidationException.class)
public ResponseEntity<ResponseDTO> sendBadRequest(ValidationException e) {
int code = e.getCode();
String message = e.getMessage();
ResponseDTO responseDTO = new ResponseDTO(false,code,message);
return ResponseEntity.status(400).body(responseDTO);
}
}
ValidationException은 ControllerAdvice 어노테이션이 붙은 ValidationExceptionHandler에서 처리합니다. @ExceptionHandler에서 ValidationException을 감지하여 해당 예외 발생 시 예외 코드와 메시지를 발송하는 역할을 담당합니다.
이를 통해 controller의 코드량을 크게 줄일 수 있어 유지 보수 측면에서 큰 향상을 이루었습니다. 하지만 이 방식의 경우 Body의 데이터에 한해서 적용하고 있어, header, pathVariable, QueryString 등의 값들을 Validation하는 방법은 추후 더 논의해봐야 할 것 같습니다. 현재 header의 경우 Spring Security를 적용시켜서 잘못된 값일 경우 외부 접근을 막아놓고 있으나 어떤 오류인지에 대해 리스판스를 보내는 부분이 없어 이 부분을 추가하는 작업도 이어서 진행할 예정입니다. 그럼 긴 글 읽어주셔서 감사합니다!