서버에 존재하지 않는 리소스에 대한 요청이 왔을 때는 올바른 상태 코드와 함께 예외임을 알려주어야 한다.
@GetMapping("/{id}")
public User retrieveUser(@PathVariable int id){
return service.findOne(id);
}
public User findOne(int id) {
return users.stream()
.filter((user) -> user.getId() == id)
.findFirst()
.orElse(null);
}
어딜 봐도 예외 처리에 대한 부분이 없다. 단순히 null
을 반환하기만 한다.
그냥 정상적으로 200 OK
를 반환해버린다. 서버측에서는 아무런 에러가 없기 때문이다.
@GetMapping("/{id}")
public User retrieveUser(@PathVariable int id){
User user = service.findOne(id);
if(user == null) {
throw new UserNotFoundException(id);
}
return user;
}
null
일 경우, new UserNotFoundException()
을 던지게 하자.
@RestController
에서 발생하는 예외에 대해 올바른 응답값을 제공하려는 목적으로 사용하는 예외처리용 REST 컨트롤러 애노테이션이다.
먼저 해당 예외가 발생했을 때 사용할 Exception
을 생성하자. Exception
을 상세하게 만들수록 나중에 프로젝트가 복잡해졌을 때 어떤 부분에서 예외가 발생했는지 알기 쉽다. 또한 같이 협업하는 프론트엔드 개발자를 위해서도 필수적이다.
@Getter
public class UserNotFoundException extends RuntimeException {
private final int userId;
public UserNotFoundException(int id) {
this.userId = id;
}
}
RuntimeException
을 상속하여 unchecked
로 예외를 거는 것이 여러모로 좋다.id
를 받아서 해당 User
에 대한 id
가 없다고 메시지를 던질 것이다.@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
public class CommonControllerAdvice {
private final MessageSource messageSource;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
@ExceptionHandler(UserNotFoundException.class)
public ErrorResult notFoundExceptionHandler(UserNotFoundException e) {
log.error("[Not Found Exception] :", e);
return new ErrorResult(
HttpStatus.NOT_FOUND.value(),
messageSource.getMessage("error.user_not_found", new Object[]{e.getUserId()}, null)
);
}
}
@ResponseStatus
애노테이션은 이 예외가 처리될 때 반환할 HTTP 상태코드이다. 만일 로직에 따라 동적으로 예외를 변경해야 한다면, ResponseEntity
형태로 반환해야 한다.@ExceptionHandler
컨트롤러가 어떤 클래스의 예외를 반환할 때, 해당 메소드가 동작하는지에 대해 기술하는 부분이다.@RestControllerAdvice
는 @RestController
에서 예외가 발생했을 때, 이 @RestControllerAdvice
를 한번 거쳐서 적절히 처리될 수 있는 예외가 있는지 확인해주는 컨트롤러를 설정하는 애노테이션이다.@ControllerAdvice
와 @ResponseBody
를 합친 애노테이션이다.messageSource
를 이용해 messages.properties
에 정의된 메시지를 가져온다.@RestControllerAdvice 관련 정보, 공식 문서
@ControllerAdvice 관련 정보, 공식 문서
@Data
@RequiredArgsConstructor
public class ErrorResult {
private LocalDateTime timeStamp = LocalDateTime.now();
private final int httpStatusCode;
private final String message;
}
@Getter
public class UserNotFoundException extends RuntimeException {
private final int userId;
public UserNotFoundException(int id) {
this.userId = id;
}
}
error.user_not_found=해당 유저 ID({0})를 찾을 수 없습니다.
에러 시에 알맞은 Http Status Code를 반환하고, JSON으로 잘 응답해준다.
ErrorResult
클래스는 ExceptionResponse
라는 이름으로 리팩토링하였다.