본문 바로가기

Spring boot - Custom Global exception Handler 적용 본문

개발/Spring boot

Spring boot - Custom Global exception Handler 적용

자전하는명왕성 2025. 5. 4. 11:05
반응형

Spring boot를 사용 중에 있어, 내 서버의 에러가 곧이 곧대로 반환되는 게 마음에 들지 않았다.

 

따라서, 클라이언트에 보여지는 건, 프론트엔드 개발자와 약속한 코드가 보여지고

직접적인 에러는 서버 콘솔에서 로그로 남게끔 설정하는 과정을 다룬 포스팅을 남긴다.

 

먼저, global exception handler를 적용하는 이유는 다음과 같다.

서버를 만들다보면 어떤 이유에서든 예상하지 못한 예외가 발생할 수 있는데, 

이에 대해 매번 try-catch 로 직접 처리하는 것은 비효율적이고 지저분하며, 잊어버릴 수도 있다.

따라서, 서버 전역에서 예외를 통합 관리하는 구조가 필요한데, 

해당 기능을 해주는 것이 해당 핸들러다.

// CustomException.java
package com.chessy.exception;

public class CustomException extends RuntimeException {
  private final ErrorCode errorCode;

  public CustomException(ErrorCode errorCode) {
    super(errorCode.getMessage());
    this.errorCode = errorCode;
  }

  public CustomException(ErrorCode errorCode, String message) {
    super(message);
    this.errorCode = errorCode;
  }

  public ErrorCode getErrorCode() {
    return errorCode;
  }
}

해당 에러를 어떻게 다룰 것인지에 대한 핸들러다.

만약 

에러 코드 이름=NOT_FOUND_USER

에러 코드 넘버=200 이라고 한다면,

나는 서비스 로직에서 에러 코드 이름을 CustomException에 전달하고, 

해당 클래스에서 에러 코드 이름에 대응하는 에러 코드 넘버를 가져와 반환토록 한다.

 

에러 코드를 매핑한 코드는 다음과 같다.

package com.chessy.exception;

import lombok.Getter;

@Getter
public enum ErrorCode {
  USER_NOT_FOUND(200, "사용자를 찾을 수 없습니다."),

  private final int code;
  private final String message;

  ErrorCode(int code, String message) {
    this.code = code;
    this.message = message;
  }
}

 

마지막 GlobalExceptionHandler를 작성한다.

package com.chessy.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
  // 의도된 에러
  @ExceptionHandler(CustomException.class)
  public ResponseEntity<ErrorResponse> handleCustomException(CustomException ex) {
    log.error("[CustomException] {} - cause: {}", ex.getMessage(), ex.getCause() != null ? ex.getCause() : "-");

    return ResponseEntity
        .status(HttpStatus.INTERNAL_SERVER_ERROR) // HTTP Status는 500 고정
        .body(ErrorResponse.of(ex.getErrorCode().getCode()));
  }

  // 의도되지 않은 에러
  @ExceptionHandler(Exception.class)
  public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex) {
    log.error("[UnknownException] {} - cause: {}", ex.getMessage(), ex.getCause() != null ? ex.getCause() : "-");

    return ResponseEntity
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .body(ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR.getCode()));
  }
}

 

소스 코드는 다음과 같다.

- 추가설명

@ControllerAdvice => 모든 @Controller, @RestController 에 적용되는 전용 어노테이션
	내부적으로 @Component가 포함되어 있어, 별도 bean 등록 없이 사용이 가능하다.

@ExceptionHandler => 특정 예외를 캐치해서 처리할 수 있도록 도와주는 어노테이션
	어떤 타입의 예외를 잡을지 명시할 수 있다.
    
해당 코드의 경우, http 상태 코드는 에러 시 무조건 internal_server 에러로 반환토록 하고,
	내가 의도한 에러와 의도되지 않은 에러를 같은 방식으로 반환할 수 있게 작성했다.

 

반응형
Comments