남은 문제점

  1.  매번 메서드마다 값을 검증해야한다.
    (BeanValidation 이전의 필드 단위로 추가할때보단 낫겠지만) 
    -> 개발자가 까먹을 수 있고 검증 부분에서 버그가 발생할 여지가 높다.
  2.  세 번 이상의 반복적인 작업은 피해야한다.
  3.  응답값에 HashMap 대신 따로 응답 클래스를 만들어주는게 좋다.
  4.  여러개의 에러처리가 힘들다. (filedErrors 에서 하나만 가지고 옴)

지금까지 만든 컨트롤러의 검증 방식은 아직 위와 같은 문제점이 남아있다. 

컨트롤러에 메서드가 추가될 때마다 검증 로직을 작성해야하고 이는 결국 반복 작업이 되며 잠재적 버그의 위험으로 남는다.

직전 게시글에서도 언급했듯 세 번 이상의 반복적인 작업은 피하는게 옳다.

이는 코드 뿐 아니라 개발에 관한 모든것에 해당하는 사항이고 따라서 자동화를 고려해야한다.

 


ControllerAdvice 와 ExceptionHandler

이러한 문제에 대해 꾸준히 고민하던 우리의 선배 개발자들은 좋은 방안을 마련해두었다.

스프링에서는 @ExceptionHandler와 @ControllerAdvice 라는 강력한 기능을 제공한다.

이 두 애노테이션은 컨트롤러에서 발생한 예외를 한 곳에서 처리해줘 우리의 고민을 해결해줄 것이다.

이를 이용해서 우리의 컨트롤러와 테스트코드를 수정해보자.

 

PostControllerAdvice

예외 발생 시 ErrorResponse 상태코드, 기본 에러 메시지, 예외가 발생한 필드명 에러 메시지들을 담아서 반환한다.

@RestControllerAdvice
public class PostControllerAdvice {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ErrorResponse invalidRequestHandler(MethodArgumentNotValidException e) {

        ErrorResponse response = new ErrorResponse("400", "잘못된 요청입니다.");
        for (FieldError fieldError : e.getFieldErrors()) {
            response.addValidation(fieldError.getField(), fieldError.getDefaultMessage());
        }
        return response;
    }
}

 

ㄴ @RestControllerAdvice

@ControllerAdvice + @ResponseBody 의 역할을 한다.

 

ㄴ @ExceptionHandler

이 애노테이션으로 컨트롤러에서 발생 할 때 처리할 예외를 정한다.

 

ㄴ @ResponseStatus 

예외 처리 시 응답값으로 보내 줄 상태코드 기본 메시지를 설정한다.

 

ErrorReposnse

예외가 발생한 필드명과 에러 메시지를 담기 위한 객체이다.

@Getter
@RequiredArgsConstructor
public class ErrorResponse {

    private final String code;
    private final String message;

    private Map<String, String> validation = new HashMap<>();

    public void addValidation(String fieldName, String errorMessage) {
        this.validation.put(fieldName, errorMessage);
    }
}

 

PostController

@PostMapping("/posts")
public Map<String, String> post(@RequestBody @Valid PostCreate params) throws Exception {
	return Map.of();
}

 

PostControllerTest

title 에 null 을, content 에 공백을 포함한 빈문자열(" ")을 입력하면 응답 값으로 상태코드 400과

필드명("title", "content") 에러 메시지가 넘어오는지 확인하는 테스트이다.

@DisplayName("/posts 요청 시 title 값은 필수다.")
@Test
void post() throws Exception {
    mockMvc.perform(
            MockMvcRequestBuilders.post("/posts")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"title\" : null, \"content\" : \" \"}")
        )
        .andExpect(status().isBadRequest())
        .andExpect(jsonPath("$.code").value("400"))
        .andExpect(jsonPath("$.message").value("잘못된 요청입니다."))
        .andExpect(jsonPath("$.validation.title").value("제목을 입력해주세요."))
        .andExpect(jsonPath("$.validation.content").value("내용을 입력해주세요."))
        .andDo(print());
}

테스트 결과를 확인하면 예상했던대로 값들이 잘 넘어오는것을 알 수 있다.

+ Recent posts