본문으로 바로가기

[Spring Boot] REST API 예외처리(Response Json)

category Backend/Spring 2021. 12. 1. 14:37

0. 환경

  • 인텔리제이
  • Spring Boot 2.5.6
  • lombok 사용
  • java 11(AdoptOpenJDK-11.0.11)

1. 사용할 주요 어노테이션

1.1. @RestControllerAdvice

  • 모든 @Controller 즉, 전역에서 발생할 수 있는 예외를 잡아 처리해주는 어노테이션
  • @Controller나 @RestController에서 발생한 예외를 한 곳에서 관리하고 처리할 수 있게 도와주는 어노테이션
  • @RestControllerAdvice = @ControllerAdvice + @ResponseBody
  • @RestControllerAdvice(basePackages = “com.test.api”) basePackage를 이용하여 특정 패키지 하위에만 적용할 수도 있습니다.
@RestControllerAdvice
public class ExceptionController {
	// (...) 생략
}

1.2. @ExceptionHandler

  • 지정한 예외 클래스를 받아서 해당 클래스 발생 시 특정 메서드를 실행해주는 어노테이션
  • @Controller, @RestController가 적용된 Bean내에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능
  • @ControllerAdvice, @RestControllerAdvice 명시된 클래스 내부 메서드에 사용합니다.
//Exception 예외 발생 시 ServerException 메소드 실행
@ExceptionHandler(Exception.class)
public Response ServerException(Exception e) {
    return new Response("500", "서버 에러");
}

1.3. @ResponseStatus

Response Status Code를 지정할 수 있습니다.

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 상태코드 500 return

2. 예시 코드 작성

[예시]

http://localhost:8080/test?param=1 -> 커스텀 예외 발생

http://localhost:8080/test?param=2 -> ArithmeticException 예외 발생

없는 주소 -> NoHandlerFoundException 예외 발생

예외 처리 소스코드

2.1 사용자 정의 예외 클래스(Custom Exception)

  • RuntimeException를 상속하여 Custom Exception Class를 작성합니다. 
//테스트 사용자 정의 예외
public class TestException extends RuntimeException {

    public TestException() {
        super();
    }

    public TestException(String message) {
        super(message);
    }

    public TestException(String message, Throwable cause) {
        super(message, cause);
    }

    public TestException(Throwable cause) {
        super(cause);
    }

    protected TestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

2.2. 예외 컨트롤러 작성(@RestControllerAdvice, @ExceptionHandler)

  • @RestControllerAdvice, @ExceptionHandler, @ResponseStatus를 이용해 작성합니다.
import com.test.api.exception.TestException;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

@RestControllerAdvice
public class ExceptionController {

    @ExceptionHandler(TestException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response TestException(Exception e) {
        e.printStackTrace();
        return new Response("501", "테스트 커스텀 예외 입니다.");
    }

    @ExceptionHandler(ArithmeticException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response ServerException(Exception e) {
        e.printStackTrace();
        return new Response("500", "서버 에러");
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public Response NotFoundException(Exception e) {
        e.printStackTrace();
        return new Response("404", "찾을 수 없습니다.");
    }

    //Response DTO
    @Data
    @AllArgsConstructor
    static class Response {
        private String code;
        private String msg;
    }
}

2.3. 컨트롤러 

import com.test.api.domain.Test;
import com.test.api.exception.TestException;
import com.test.api.service.TestService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

@Log4j2
@RestController
@RequiredArgsConstructor
public class TestController {

    @GetMapping(value = "/test")
    public String test(@RequestParam(required = false) String param) throws Exception {

        if(param.equals("1")) {
            throw new TestException("테스트 예외 입니다.");
        } else if(param.equals("2")) {
            //ArithmeticException 발생
            int div = 1/0;
        }
        return "success";
    }
}

2.4. 404 예외를 위한 추가 설정

  • resources/application.properties에 꼭 추가 설정해야 NoHandlerFoundException를 핸들링할 수 있습니다.
spring.mvc.throw-exception-if-no-handler-found=true
spring.web.resources.add-mappings=false

3. 결과

테스트를 위해 스프링 부트를 실행하고 웹 브라우저 또는 테스트 툴(Postman)을 이용해 확인해 봅니다.

 

[http://localhost:8080/test?param=1]

 

[http://localhost:8080/test?param=2]

 

[http://localhost:8080/test2(없는 주소)]