Spring Boot Validation (Feat ControllerAdvice)

최민길(Gale)·2023년 1월 22일
1

Spring Boot 적용기

목록 보기
17/46

안녕하세요 오늘은 Validation을 효과적으로 진행하기 위한 방법에 대해 포스팅해보도록 하겠습니다.

        ...
        // 존재하지 않은 이메일인 경우
        if(!validationService.checkExistEmail(loginRequestDTO)){
            return ResponseEntity.ok(new ResponseDTO(
                    {예외 처리 내용 출력}
            ));
        }
        ...

우선 기존 방식의 경우 controller 내부에 직접 Validation을 진행하였습니다. 그러다보니 controller 내부에 API가 많아짐에 따라 controller 내부의 코드가 너무 많아 추후 유지 보수 시 가독성이 크게 떨어진다는 단점이 존재합니다.

따라서 이를 해결하기 위해 Validation을 ControllerAdvice를 이용하여 중앙에서 일괄적으로 처리하는 로직을 작성했습니다. 이 방식은 다음과 같이 동작합니다.

  1. @Validated 어노테이션이 설정된 리퀘스트값이 Validator에서 입력 및 비즈니스 로직에 따라 Validation을 처리한다.
  2. 만약 조건에 부합하지 않는다면 ValidationException(직접 만든 커스텀 Exception)을 통해 코드와 메시지를 쏜다.
  3. ControllerAdvice에서 ValidationException을 캐치하여 코드와 메시지를 출력한다.
@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를 적용시켜서 잘못된 값일 경우 외부 접근을 막아놓고 있으나 어떤 오류인지에 대해 리스판스를 보내는 부분이 없어 이 부분을 추가하는 작업도 이어서 진행할 예정입니다. 그럼 긴 글 읽어주셔서 감사합니다!

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글