일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 동등성동일성
- 검증 실패 예외처리
- 공유기작동방식
- DispatcherServlet
- 동기비동기블로킹논블로킹
- 중첩클래스
- rest api 검증
- java enum
- 클라이언트요청반응
- Wrapper class
- multipart바인딩
- rest api
- HTTP프로토콜
- 옵티마저
- 스프링요청반응
- 왜불변객체인가
- multiparfile데이터
- 디스패처서블릿
- HttpServlet
- fcm성능비교
- httpservlet기술
- 프로세스 생성
- equals
- fcmv1
- 옵티마이저
- fcm데이터구조
- biblecash
- 불변객체
- 래퍼클래스
- 데이터베이스파서
- Today
- Total
개발은 아름다워
[ Spring ] REST API 형식으로 검증 및 검증 실패 예외처리까지 본문
바로 전에는 content-type에 따른 어노테이션을 정리하였다. 지난 글을 보면 @Valid가 보였터인데, content-type에 따른 어노테이션을 정리하기 위한 코드가 아니라, 검증 구현을 어떻게 할지 코드를 작성중이였다. 각설하고 오늘 정리할 내용은 스프링의 Bean Validation 및 검증 예외 처리까지 간단하게 작성해보려고 한다.
간단하게 기능을 구현하는 코드들만 작성했다. 내부의 작동원리를 알기 위해서는 스프링 예외처리에 대한 공부가 필요하다. 공부를 깊게 하고 싶다면 김영한 강사님의 스프링mvc2편을 매우 매우 추천!!
바인딩 시점 파악
일단 @Valid가 사용되는 시점은 컨트롤러가 클라이언트로부터 받은 데이터를 객체로 바인딩하는 시점이다.
무슨 말인가? 상황과 코드를 보면 이해가 될 것이다.
회원가입을 위해 name과 password를 보내는 아주 간단한 상황이다.
클라이언트에서 서버로 회원가입 데이터를 보내는 상황
1) http://localhost:8080/modelAttribute 로
2) form-data로
3) name과 password를
4) Headers의 content-type : application/x-www-form-urlencoded 로 서버에 데이터를 보낸다.
서버에서 클라이언트로부터 받은 데이터를 처리하는 상황
@Slf4j
@RestController
public class TestController {
// content-type : application/x-www-form-urlencoded -> @ModelAttribute
@PostMapping("/modelAttribute")
public UserDto modelAttribute(@Valid @ModelAttribute UserDto user){
log.info("클라이언트로부터 받은 데이터: name = {} , password = {}",user.getName(),user.getPassword());
return user;
}
@Data
public static class UserDto{
@NotEmpty(message = "반드시 입력")
private String name;
@NotEmpty(message = "반드시 입력 되어야 합니다.")
private String password;
public UserDto(String name, String password) {
this.name = name;
this.password = password;
}
}
UserDto 타입의 객체인 user가 생성되어 클라이언트로부터 받은 name과 password가 입력이 된다. 이게 바인딩이라는 것이다. @Valid는 바인딩 시점에 UserDto 클래스 필드에 적용한 유효성 검사 어노테이션들이 동작하게 된다.
정상 흐름
클라이언트에서 다음과 같이 데이터를 서버에 보내면
컨트롤러에서 데이터를 받은 후 바인딩처리까지 된것을 확인할 수 있다.
예외 흐름
여기서 말하는 예외 흐름이란, 유효성 검사 어노테이션들에 해당하게 되었을 때를 의미한다. @NotEmpty 인데, 입력 값이 없는 name 전달된 경우에 발생하는 예외를 말하는 것이다. 유효성 검사 어노테이션을 통과하지 못하면 어떻게 될까?
name 데이터를 없이 서버에 보내면
WARN 3676 --- [test] [nio-8080-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver :
Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public hello.test.controller.TestController$UserDto
hello.test.controller.TestController.modelAttribute(hello.test.controller.TestController$UserDto): [Field error in object 'userDto' on field 'name': rejected value []; codes [NotEmpty.userDto.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userDto.name,name]; arguments []; default message [name]]; default message [반드시 입력]] ]
서버에서는 MethodArgumentNotValidException이 발생했고 이를 DefaultHandlerExceptionResolver 처리했음을 알 수 있다.또한 MethodArgumentNotValidException 객체 안에는 defaule message로 [반드시 입력] 이 있음을 알 수 있다.
그렇다면 클라이언트는 어떤 응답을 받았을까?
다음과 같은 응답을 받았을 알 수 있다. 이는 스프링에서 만들어서 클라이언트에 보내준 응답값이다.
위와 같은 경우에는 서버,클라이언트 입장에서 각각 문제가 있다. 공통적인 문제는 에러 내용이 무엇인지 정확하게 파악하기 어렵다는 것이다. 그리고 서버에서는 반드시 로그가 남게 된다는 문제도 있다. 로그가 필요하지 않다면 남게하지 않을 수 있어야하는데 말이다.
검증 실패 예외처리 깔끔하게
errorCode는 어떤 에러인지 알려주는 부분이다."VALID_ERROR"는 검증에 실패했다는 것을 의미한다.
errorMessage는 에러의 내용을 알려주는 것이다.
위 두개의 조건을 만족하는 예외 처리를 만들면 다음과 같다.
public enum ErrorCode {
VALID_ERROR,
}
@Slf4j
@RestControllerAdvice
public class ExHandlers {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResult> methodArgValidHandler(MethodArgumentNotValidException e) {
log.info("errorMessage : {} ",e.getFieldError().getDefaultMessage());
ErrorResult errorResult = new ErrorResult(VALID_ERROR,e.getFieldError().getDefaultMessage());
return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST);
}
@Data
@AllArgsConstructor
static class ErrorResult{
private ErrorCode errorCode;
private String errorMessage;
}
}
@RestControllerAdvice 예외가 발생하면 예외를 받은 후 클라이언트에 응답값으로 JSON형식으로 데이터를 만들어서 내려준다.
@ExceptionHandler는 발생한 예외 타입과 맞는 경우에 실행된다.
MethodArgumentNotValidException 발생한 경우 위에 작성한 코드가 예외를 처리하게 된다.
예외 처리를 만들지 않은 경우와 만든 경우를 비교해보면
[nio-8080-exec-8] hello.test.exception.ExHandlers : errorMessage : 반드시 입력
클라이언트와 서버 입장에 둘다 명확하게 에러를 파악할 수 있게 되었다.
정리
배웠던 내용이지만, 다시 코드를 하나씩 짜다보면 또 잊게된다. 그리고 실제 코드를 짜봐야 '아! 이게 이렇게 쓰면 되는구나!' 싶게 된다. 역시 백문이 불여일타,
'스프링' 카테고리의 다른 글
[ Spring ] 고대의 서블릿을 찾아서(1) - 서블릿으로만 웹 앱을 만들어보자 (2) | 2024.10.12 |
---|---|
[ Spring ] HTTP 요청 데이터와 HtttpServletRequest,HttpServletResponse 객체의 역할 (1) | 2024.10.12 |
[ Spring ] DispatcherServlet 니 누구야? (0) | 2024.10.12 |
[ Spring ] customException API를 만들어보자(feat. 체크예외와 언체크예외 이해하기) (0) | 2024.10.12 |
[ Spring ] Content-Type에 따른 어노테이션 간단 정리 - 제약을 통한 정확한 의사소통 (0) | 2024.10.12 |