Spring Boot를 이용한 RESTful Web Services 개발 #15 HTTP Status Code 제어를 위한 Exception Handling

Jake Seo·2021년 9월 16일
0

Spring-boot-restful

목록 보기
15/17

존재하지 않는 데이터의 처리

서버에 존재하지 않는 리소스에 대한 요청이 왔을 때는 올바른 상태 코드와 함께 예외임을 알려주어야 한다.

현재의 상태

retreieveUser() 메소드

    @GetMapping("/{id}")
    public User retrieveUser(@PathVariable int id){
        return service.findOne(id);
    }

findOne() 메소드

    public User findOne(int id) {
        return users.stream()
                .filter((user) -> user.getId() == id)
                .findFirst()
                .orElse(null);
    }

어딜 봐도 예외 처리에 대한 부분이 없다. 단순히 null을 반환하기만 한다.

존재하지 않는 유저의 조회 결과

그냥 정상적으로 200 OK를 반환해버린다. 서버측에서는 아무런 에러가 없기 때문이다.

retrieveUser() 메소드 수정

    @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()을 던지게 하자.

@RestControllerAdvice를 이용해 예외처리 하기

@RestControllerAdvice 란?

@RestController에서 발생하는 예외에 대해 올바른 응답값을 제공하려는 목적으로 사용하는 예외처리용 REST 컨트롤러 애노테이션이다.

관련 링크

UserNotFoundException 클래스 생성하기

먼저 해당 예외가 발생했을 때 사용할 Exception을 생성하자. Exception을 상세하게 만들수록 나중에 프로젝트가 복잡해졌을 때 어떤 부분에서 예외가 발생했는지 알기 쉽다. 또한 같이 협업하는 프론트엔드 개발자를 위해서도 필수적이다.

@Getter
public class UserNotFoundException extends RuntimeException {
    private final int userId;

    public UserNotFoundException(int id) {
        this.userId = id;
    }
}
  • RuntimeException을 상속하여 unchecked로 예외를 거는 것이 여러모로 좋다.
  • id를 받아서 해당 User에 대한 id가 없다고 메시지를 던질 것이다.

CommonControllerAdvice 클래스 생성하기

@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 관련 정보, 공식 문서

ErrorResult 클래스 생성하기

@Data
@RequiredArgsConstructor
public class ErrorResult {
    private LocalDateTime timeStamp = LocalDateTime.now();
    private final int httpStatusCode;
    private final String message;
}

UserNotFoundException 클래스 생성하기

@Getter
public class UserNotFoundException extends RuntimeException {
    private final int userId;

    public UserNotFoundException(int id) {
        this.userId = id;
    }
}

messages.properties 작성하기

error.user_not_found=해당 유저 ID({0})를 찾을 수 없습니다.

결과 확인하기

에러 시에 알맞은 Http Status Code를 반환하고, JSON으로 잘 응답해준다.

클래스 이름 리팩토링

  • ErrorResult 클래스는 ExceptionResponse라는 이름으로 리팩토링하였다.
profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글