예외 - API

바그다드·2023년 5월 24일
0

예외

목록 보기
4/9
  • 지난 포스팅까지 해서 HTML을 반환하는 예외 처리 방법에 대해서 알아보았다. 그런데 그 방법처럼 예외로서 HTML을 반환하면 어떻게 될까? 코드로 결과를 확인해보자

WebServerCustomizer

  • 우선 지난번에 생성했던 에러페이지를 등록하는 클래스를 빈으로 등록해주자
    코드는 바뀐 것이 없고 @Component의 주석만 제거하였다
// 이 부분 
@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
    @Override
    public void customize(ConfigurableWebServerFactory factory) {

        // 상태코드 발생 시
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");
        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error-page/500");


        // 예외 발생 시
        // RuntimeException의 자식 예외 또한 같이 처리해준다.
        ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500");

        factory.addErrorPages(errorPage404, errorPage500, errorPageEx);

    }
}

RestController생성

  • API응답 결과를 확인하기 위해 컨트롤러를 생성해주자
@Slf4j
@RestController
public class ApiExceptionController {

    @GetMapping("/api/members/{id}")
    public MemberDto getMember(@PathVariable("id") String id) {

        if (id.equals("ex")) {
            throw new RuntimeException("잘못된 사용자");
        }

        return new MemberDto(id, "hello" + id);
    }

    @Data
    @AllArgsConstructor
    static class MemberDto{
        private String memberId;
        private String name;
    }
}
  • 이제 파라미터로 넘어온 id값이 ex와 일치하면 예외를 발생시킬 것이다.

    응답 결과를 확인해보면 json형식의 데이터가 아닌 html문서가 온것을 확인할 수 있다. 그런데 api의 경우 이런 문서가 아니라 json형식의 데이터가 올 것이라 예상할 것이기 때문에 이렇게 응답을 보내는 것은 문제가 있다. 그렇다면 어떻게 해야할까?

ErrorPageController수정

@Slf4j
@Controller
public class ErrorPageController {

    //생략
    
    @RequestMapping("/error-page/500")
    public String errorPage500(HttpServletRequest request, HttpServletResponse response) {
        log.info("errorPage 500");
        printErrorInfo(request);
        return "error-page/500";
    }

    @RequestMapping(value = "/error-page/500", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String,Object>> errorPage500Api(
            HttpServletRequest request, HttpServletResponse response) {

        log.info("API errorPage 500");

        Map<String, Object> result = new HashMap<>();
        Exception ex = (Exception) request.getAttribute(ERROR_EXCEPTION);
        result.put("status", request.getAttribute(ERROR_STATUS_CODE));
        result.put("message", ex.getMessage());

        Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        return new ResponseEntity<>(result, HttpStatus.valueOf(statusCode));

    }
    
    //생략
}
  • 기존에 생성해두었던 ErrorPageController에서 500에러를 처리하는 메서드를 추가로 생성했는데 @RequestMapping의 파라미터 중에 produces에 주목하자.
    errorPage500과 errorPage500Api의 url패턴은 동일하다.
    다만, errorPage500Api에는 produces = MediaType.APPLICATION_JSON_VALUE라는 값을 주었는데, 이는 클라이언트의 요청 헤더에 Accept의 value가 application/json일 경우 해당 메서드가 우선권을 가지게 된다.
  • errorPage500Api가 수행될 경우 예외 객체에 담긴 메세지와 상태코드를 반환하게 된다.
    ResponseEntity(T body, HttpStatus status)

    Accept를 application/json으로 설정한 응답 결과다. 기존에 HTML문서가 반환된 것과 달리 json데이터 형식으로 반환된 것을 알 수 있다.
  • 그런데 스프링 부트에서는 API 예외 처리도 기본적으로 제공한다.

스프링 부트 에러 처리

//@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
  • WebServerCustomizer 빈 등록을 해제해주자.

    따로 설정을 하지 않았음에도 스프링 부트에서 기본 메세지를 제공하는 것을 확인할 수 있다.
    api도 마찬가지로 더 자세한 에러 메세지를 확인할 수 있는데, application.properties에 다음 설정을 추가해주자.
server.error.include-exception=true
server.error.include-message=always
server.error.include-stacktrace=always
server.error.include-binding-errors=always

  • 응답 결과를 확인 해보면 더 자세한 정보가 반환되는 것을 확인할 수 있다. 다만 이것도 마찬가지로 보안상의 문제가 발생할 수 있으므로 최소한의 정보만 노출하고, 나머지는 로그로 확인하자!!!
profile
꾸준히 하자!

0개의 댓글