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개의 댓글

관련 채용 정보