[스프링(spring)]예외처리해보기

allnight5·2023년 1월 9일
0

스프링

목록 보기
30/62

예외다 우와

1. JAVA Exception 에러 출력

- e.getMessage() : 에러의 원인을 간단하게 출력합니다.(어째서 에러가 발생했는지 보여주기만한다.. num이 애러라면 num만나옴)
- e.toString() : 에러의 Exception 내용과 원인을 출력합니다.
(num이 에러인데 무슨 예외로 인한 것인지 원인 이유를 보여주나 발생위치는 보여주지 않는다)
- e.printStackTrace() : 에러의 발생근원지를 찾아서 단계별로 에러를 출력합니다.
(이유와 어느 클래스에서 발생했는지를 알려준다)

그런데 예외에도 꼭 해결해야하는 예외와 안해도 되는예외가있는데.

2. Exception 의 종류

Checked Exception(해야하는 예외) vs Unchecked Exception(지나가는 예외)

Checked Exception 컴파일시 알고있던거

Unchecked Exception 런타임 실행시 발생되는 예외

1. 예외복구

  1. 네트워크가 환경이 좋지 않아서 서버에 접속이 안되는 상황의 시스템에 적용하면 효율 적이다.
  2. 예외가 발생하면 그 예외를 잡아서 일정 시간만큼 대기하고 다시 재시도를 반복한다.
    그리고 최대 재시도 횟수를 넘기면 예외를 발생시킨다.
  3. 네트워크환경이 한번씩 좋지 못할수있는 어플리케이션에서는 자주사용하는데.. 컴퓨터의 경우 그냥 랜선이니 그럴일 없다.. 노트북은 모르겠다라는 식으로 예외복구 없이 넘어가는 게임들도 있는데 보안상 예외복구가 없는편이 좋으나 사용자의 편의는 예외복구가 있는편이 좋다.

2. 예외처리 회피

  1. 예외가 발생하면 throws를 통해 처리하지 않고 호출한쪽으로 예외를 던지고 그 처리를 회피하는 것이다.
    2.호출한 쪽에서 다시 예외를 받아 처리하도록 하거나, 해당 메소드에서 이 예외를 던지는 것이 최선의 방법이라는 확신이 있을 때만 사용해야 한다.

3. 예외 전환

호출한 쪽으로 던질 때 명확한 의미를 전달하기 위해 다른 예외로 전환하여 던지는 방법
1. 예를 들어 Checked Exception 중 복구가 불가능한 예외가 잡혔다면 이를 Unchecked Exception으로 전환하여서 다른 계층에서 일일이 예외를 선언할 필요가 없도록 할 수도 있다.
2.호출한 쪽에서 예외를 받아서 처리할 때 좀 더 명확하게 인지할 수 있도록 돕기 위한 방법이다.
3. SQLException을 중복예외로 바꿔 발생시킬수있다.
참조 사이트1

서블릿에서 예외가 발생하면 다음과 같은 방식으로 진행이 된다

흐름을 글로 작성하자면
1. WAS(톰캣) -> 필터 -> 서블릿(디스패처 서블릿) -> 인터셉터 -> 컨트롤러

  1. -> 컨트롤러(예외발생) -> 인터셉터 -> 서블릿(디스패처 서블릿) -> 필터 -> WAS(톰캣)

  2. -> WAS(톰캣) -> 필터 -> 서블릿(디스패처 서블릿) -> 인터셉터 -> 컨트롤러(BasicErrorController)

서블릿 예외 문제점

1.불필요한 호출이 발생 한다.
2.예외가 발생하면 다시 오류페이지를 찾기 위해 인터셉터 서블릿을 거치게 된다.
매우 비효율 적이다.
3.비효율적인 이유는 이미 처리를 했는데 한번더 처리가 되기 때문이다.

스프링 부트의 오류 페이지

1.서블릿에서 예외 처리 페이지를 만들기 위해 WebServerCustomizerd를 만든다.
2.예외 종류에 따라 HTML 을 추가 해야 된다.
3.ErrorPageController 를 만들어서 예외를 따로 처리 해야된다.

BasicErrorController

스프링은 위 과정(스프링 부트의 오류 페이지)을 자동으로 처리 해준다.
BasicErrorController 에서 처리를 해주기 때문이다.
개발자는 그냥 페이지만 등록 해주면 된다.

- DefaultErrorAttributes는 전체 항목들에서 설정에 맞게 불필요한 속성들을 제거한다.
    - timestamp: 에러가 발생한 시간
    - status: 에러의 Http 상태
    - error: 에러 코드
    - path: 에러가 발생한 uri
    - exception: 최상위 예외 클래스의 이름(설정 필요)
    - message: 에러에 대한 내용(설정 필요)
    - errors: BindingExecption에 의해 생긴 에러 목록(설정 필요)
    - trace: 에러 스택 트레이스(설정 필요)

server.error.include-message: always
server.error.include-binding-errors: always
server.error.include-stacktrace: always
server.error.include-exception: false

Spring은 아래와 같은 도구들로 ExceptionResolver를 동작시켜 에러를 처리할 수 있는데, 각각의 방식 대해 자세히 살펴보도록 하자.

  1. @ResponseStatus
  2. @ResponseStatusException
  3. @ExceptionHandler
  4. @ControllerAdvice, @RestControllerAdvice

[ @ResponseStatus ]
어노테이션 이름에서 예측가능하듯이 @ResponseStatus는 에러 HTTP 상태를 변경하도록 도와주는 어노테이션이다. @ResponseStatus는 다음과 같은 경우들에 적용할 수 있다.

  • Exception 클래스 자체
  • 메소드에 @ExceptionHandler와 함께
  • 클래스에 @RestControllerAdvice와 함께

한계점

  • WAS까지 예외가 전달되고, WAS의 에러 요청 전달이 진행됨
  • 외부에서 정의한 Exception 클래스에는 @ResponseStatus를 붙여줄 수 없음

[ ResponseStatusException ]
ResponseStatusException는 HttpStatus와 함께 선택적으로 reason과 cause를 추가할 수 있고, 언체크 예외을 상속받고 있어 명시적으로 에러를 처리해주지 않아도 된다.

  • 기본적인 예외 처리를 빠르게 적용할 수 있으므로 손쉽게 프로토타이핑할 수 있음
  • HttpStatus를 직접 설정하여 예외 클래스와의 결합도를 낮출 수 있음
  • 불필요하게 많은 별도의 예외 클래스를 만들지 않아도 됨
  • 프로그래밍 방식으로 예외를 직접 생성하므로 예외를 더욱 잘 제어할 수 있음

한계점

  • 직접 예외 처리를 프로그래밍하므로 일관된 예외 처리가 어려움
  • 예외 처리 코드가 중복될 수 있음
  • Spring 내부의 예외를 처리하는 것이 어려움
  • 예외가 WAS까지 전달되고, WAS의 에러 요청 전달이 진행됨

[ @ExceptionHandler ]

@ExceptionHandler는 매우 유연하게 에러를 처리할 수 있는 방법을 제공하는 기능이다.

  • @ControllerAdvice나 @RestControllerAdvice가 있는 클래스의 메소드

ExceptionHandler는 @ResponseStatus와 달리 에러 응답(payload)을 자유롭게 다룰 수 있다는 점에서 유연하다.

  • code: 어떠한 종류의 에러가 발생하는지에 대한 에러 코드
  • message: 왜 에러가 발생했는지에 대한 설명
  • erros: 어느 값이 잘못되어 @Valid에 의한 검증이 실패한 것인지를 위한 에러 목록

[ @ControllerAdvice와 @RestControllerAdvice ]

우리는 이러한 ControllerAdvice를 이용함으로써 다음과 같은 이점을 누릴 수 있다.

  • 하나의 클래스로 모든 컨트롤러에 대해 전역적으로 예외 처리가 가능함
  • 직접 정의한 에러 응답을 일관성있게 클라이언트에게 내려줄 수 있음
  • 별도의 try-catch문이 없어 코드의 가독성이 높아짐

한계점

여러 ControllerAdvice가 있을 때 @Order 어노테이션으로 순서를 지정하지 않는다면 Spring은 ControllerAdvice를 임의의 순서로 처리할 수 있으므로 일관된 예외 응답을 위해서는 이러한 점에 주의해야 한다.

  • 한 프로젝트당 하나의 ControllerAdvice만 관리하는 것이 좋다.
  • 만약 여러 ControllerAdvice가 필요하다면 basePackages나 annotations 등을 지정해야 한다.
  • 직접 구현한 Exception 클래스들은 한 공간에서 관리한다.

[ Spring의 예외 처리 흐름 ]

1. ExceptionHandlerExceptionResolver가 동작함
    1. 예외가 발생한 컨트롤러 안에 적합한 @ExceptionHandler가 있는지 검사함
    2. 컨트롤러의 @ExceptionHandler에서 처리가능하다면 처리하고, 그렇지 않으면 ControllerAdvice로 넘어감
    3. ControllerAdvice안에 적합한 @ExceptionHandler가 있는지 검사하고 없으면 다음 처리기로 넘어감
2. ResponseStatusExceptionResolver가 동작함
    1. @ResponseStatus가 있는지 또는 ResponseStatusException인지 검사함
    2. 맞으면 ServletResponse의 sendError()로 예외를 서블릿까지 전달되고, 서블릿이 BasicErrorController로 요청을 전달함
3. DefaultHandlerExceptionResolver가 동작함
    1. Spring의 내부 예외인지 검사하여 맞으면 에러를 처리하고 아니면 넘어감
4. 적합한 ExceptionResolver가 없으므로 예외가 서블릿까지 전달되고, 서블릿은 SpringBoot가 진행한 자동 설정에 맞게 BasicErrorController로 요청을 다시 전달함
profile
공부기록하기

0개의 댓글