서버에 존재하지 않는 리소스에 대한 요청이 왔을 때는 올바른 상태 코드와 함께 예외임을 알려주어야 한다.
@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라는 이름으로 리팩토링하였다.