3.배달플랫폼 - Exception Handler, Interceptor 인증

ys·2024년 3월 2일

배달플랫폼

목록 보기
4/8

  • 처음 request가 들어오고, 모든 과정에서 일어나는 모든 예외는 Exception Handler라는 곳에 모이게 된다
  • 그러므로 우리는 이곳에서 예외처리를 공통으로 하고, 사용자에게 response로 내려줄 수 있다

서버는 항상 요청을 하면, 🤔응답은 OK즉 성공 아니면 실패 둘중에 하나이다!!

  • Ok이면 내가 원하는 응답이 내려오는 것이고
  • 실패하면, 예외가 일어나게 되는 것이다
  • 별도의 예외처리가 아니라면, 굳이 try, catch문을 코드에 작성할 필요가 없다
  • 나중에 ExceptionHandler에서 성공응답과, 실패응답을 내주면 된다
  • 앞서 봤듯이, ErrorCodeIfs를 만들어 놨기 때문에, 이를 구현한 에러를 만들어서 상황에 맞게 실패응답을 내려주면 되는 것이다

ExceptionHandler

  • 예외 처리하는 부분의 패키지를 하나 만들어준다

GlobalExceptionHandler

@Slf4j
@RestControllerAdvice
@Order(value = Integer.MAX_VALUE) // 가장 마지막에 실행 적용
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<Api<Object>> exception(Exception exception){

        log.error("",exception);
        return ResponseEntity.status(500).body(Api.ERROR(ErrorCode.SERVER_ERROR));
    }
  • @RestControllerAdvice : ExceptionHandler을 AOP적용해 예외를 전역적으로 처리할 수 있게 하는 어노테이션
  • @Order : 순서를 지정해주는 어노테이션, 작을 수록 우선순위이다
  • @ExceptionHandler : 스프링에서 어노테이션을 이용하여서, 간단하게 예외처리를 할 수 있다

@RestControllerAdvice로 예외를 모아 전역적으로 처리할 수 있게 만들고, @ExceptionHandler을 이용해 예외를 처리한다

  • 글로벌 에러는, 가장 마지막에 실행되는 ExceptionHandler이고, 500의 서버 에러를 내보낸다
  • 이때 ResponseEntity에 500 상태코드를 적고, body에 Api.ERROR을 넣어서 반환한다

🤔try,catch을 이용해 바로 예외처리를 하면 안될까?

  • 다음과 같이 예외가 나는 부분을 try,catch로 잡아서 return을 해주면 안될까??
  • 반환 타입이 맞지 않는다!!!
  • 응답 타입을 ResponseEntity<Api<AccountMeResponse>>로 바꿔주야 한다
    • catch부분의 서버에 예외를 잡는 부분의 코드가 나가는데
    • 서버에서 오류에 대한 stackTrace도 남지 않게된다...
    • 예외가 발생하지 않았는데, catch의 에러 응답 코드가 나간 경우다
    • 이런 코드는 좋지 않은 코드다!!!

결국 보면 ✅Api의 응답성공 아니면 실패이다

  • 모든 에러 코드는 @ExceptionHandler를 통해, Exception Handler부분에서 처리가 가능하다
  • 그리고 이부분에서 errorCode에 대한 StackTrace를 모두 모아, debugging을 할 것이다
  • 결국 예외처리를 하지 않는 이상 굳이 try,catch를 할 필요가 없다

✅코드의 분리!!!

  • 우리가 작성하는 컨트롤러, ✅서비스 코드는 항상 성공 케이스만 본다
  • 그 부분에서, Error가 나는 것들은 -> 예외 처리 핸들러를 이용해 한자리에 모아서, StackTrace를 찍은 후에, 응답을 내려준다

우리는 이 ✅StackTrace를 가지고 debugging을 잘 해주면 된다!

  • stack trace는 어디에서 오류가 났는지, 어떤 이유인지 ... 등등 자세하게 내려줘야 한다
  • 아주 중요하다

RuntimeException을 상속 받는 ApiException을 만들자!

  • 우리는 이 Exception을 우리 ErrorCode와 묶어서 사용할 것이다
  • 인터페이스인 ErrorCodeIfs와 설명 부분을 필드로 만든다
@Getter
public class ApiException extends RuntimeException implements ApiExceptionIfs{

    private final ErrorCodeIfs errorCodeIfs;
    private final String errorDescription;

    public ApiException(ErrorCodeIfs errorCodeIfs){
        super(errorCodeIfs.getDescription());
        this.errorCodeIfs = errorCodeIfs;
        this.errorDescription = errorCodeIfs.getDescription();
    }

    public ApiException(ErrorCodeIfs errorCodeIfs, String errorDescription){
        super(errorDescription);
        this.errorCodeIfs = errorCodeIfs;
        this.errorDescription = errorDescription;
    }

    public ApiException(ErrorCodeIfs errorCodeIfs, Throwable tx){
        super(tx);
        this.errorCodeIfs = errorCodeIfs;
        this.errorDescription = errorCodeIfs.getDescription();
    }

    public ApiException(ErrorCodeIfs errorCodeIfs, Throwable tx, String errorDescription){
        super(tx);
        this.errorCodeIfs = errorCodeIfs;
        this.errorDescription = errorDescription;
    }


}
  • 이 ApiException은 runttimeException을 상속받고
  • super을 통해 부모에게 메시지를 던져버린다
  • 아까 errorCodeIfs를 만들 때 봤듯이, messageDescription을 받는 방법을 확장성 있게 관리하기 위해서 ✅여러 메서드들을 오버로딩한다

ApiExceptionIfs

  • 나중에 어떻게 Exception을 사용할지 모르고
  • 더욱 구체적인 오류를 만들 수 있기 때문에, ✅인터페이스를 만들어 확장을 해준다
  • ApiExceptionIfs를 구현하고, @Getter 메서드를 이용해 오버라이딩 해준다
public interface ApiExceptionIfs {

    ErrorCodeIfs getErrorCodeIfs();

    String getErrorDescription();
}

ApiExceptionHandler

  • 이제 만들어둔 ApiException을 처리하는 ApiExceptionHandler을 만들어주자
@Slf4j
@RestControllerAdvice
@Order(value = Integer.MIN_VALUE)  // 최우선 처리
public class ApiExceptionHandler {

    @ExceptionHandler(value = ApiException.class)
    public ResponseEntity<Api<Object>> apiException(ApiException apiException){
        log.error("",apiException);

        ErrorCodeIfs errorCode = apiException.getErrorCodeIfs();

        return ResponseEntity
                .status(errorCode.getHttpStatusCode())
                .body(Api.ERROR(errorCode, apiException.getErrorDescription()));
    }
}
  • 순서는 최우선 처리를 해준다
  • 에러 StackTrace를 내주고
  • @ExceptionHandler(value = ApiException.class)로 apiException을 처리한다
  • 파라미터의 Apiexception에서 에러코드를 가져오고
  • ResponseEntity에 가져온 에러의 status를 넣어주고, body에 errorCode와, 설명을 Api<>의 result부분에 넣어 반환해준다
  1. ApiException은 예외랑, 예외설명을 가지고 있다
  2. ApiException발생 -> ApiExceptionHandler로 이동
  3. Handler는 ApiException에서 예외와 설명을 꺼낸후, 반환값 responseEntity에 status값을 채우고
  4. body는 비었고, result의 3개의 필드에, 예외와 설명을 가지고 값을 채운 ✅Api<Object> (result부분에 값을 채운) 을 반환해준다!!!
profile
개발 공부,정리

0개의 댓글