Spring

[Spring] Rest API ExceptionHandler

이주여이 2025. 1. 19. 23:28

Rest API 서버 만들면서 예외 처리에 대한 부분이 필요했다.

 

설명은 없고 메모용이라 간략간략하게 확인만 하자.

 

필자는 STS4(Spring Tool Suite 4)에서 진행한다.

 

패키지 구조는 아래와 같다.

 

 

@Getter
@AllArgsConstructor
public enum ApiStatus {
    SUCCESS(200, "요청이 성공적으로 완료되었습니다."),
    CREATED(201, "리소스가 성공적으로 생성되었습니다."),
    BAD_REQUEST(400, "요청이 유효하지 않습니다."),
    UNAUTHORIZED(401, "권한이 확인되지 않았습니다."),
    FORBIDDEN(403, "접근이 거부되었습니다."),
    NOT_FOUND(404, "요청한 리소스를 찾을 수 없습니다."),
    REQUIRED_ERROR(422, "필수 값이 누락되었습니다."),
    INTERNAL_SERVER_ERROR(500, "서버에서 예기치 못한 오류가 발생했습니다.");

    private final int statusCode;
    private final String message;
}

 

예외 던질 때 담을 HTTP 상태코드 및 메세지가 담겨있다.

 

@Data
@Builder
public class ApiResponse<T> {
    private int status;
    private String message;

    @Builder.Default // builder 패턴으로 객체 생성 시 기본 값 지정
    private String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

    private T data;

    @Builder.Default
    private List<String> errors = List.of();

    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.<T>builder()
                .status(ApiStatus.SUCCESS.getStatusCode())
                .message(ApiStatus.SUCCESS.getMessage())
                .data(data)
                .build();
    }

    public static <T> ApiResponse<T> error(int status, String message, List<String> errors) {
        return ApiResponse.<T>builder()
                .status(status)
                .message(message)
                .errors(errors)
                .build();
    }
}

 

ApiResponse는 ResponseEntity의 지네릭 타입에 명시할 클래스이다.

 

status는 ApiStatus의 HTTP 상태 코드를, message는 ApiStatus에 있는 메세지를, timestamp는 API 호출 시 호출한 시간을 찍어내기 위함이며 data는 return할 데이터이다.

 

성공 시에는 상태 코드랑 메세지가 동일하기에 미리 적어두었으며 data(VO)만 받아 객체를 생성한다. 반대로 실패 시에는 해당 객체 생성 시 인자 값으로 담은 상태 코드와 메세지, 에러를 받는다.

 

@Getter
public class ApiException extends RuntimeException {
    private ApiStatus apiStatus;

    public ApiException(ApiStatus apiStatus) {
        this.apiStatus = apiStatus;
    }
}

 

직접 만든 예외다.

 

해당 예외를 던질 때 해당 값은 ApiStatus(ENUM)에 있는 값들 중 하나를 고를 수 있다.

 

@RestControllerAdvice
public class ApiExceptionAdvice {
    @ExceptionHandler(ApiException.class)
    public ResponseEntity<ApiResponse<Object>> customExceptionHandler(HttpServletRequest request, final ApiException e) {
        ApiResponse<Object> apiResponse = ApiResponse.<Object>builder()
                .status(e.getApiStatus().getStatusCode())
                .message(e.getApiStatus().getMessage())
                .errors(null)
                .build();

        return ResponseEntity
                .ok(apiResponse);
    }

    @ExceptionHandler(DataIntegrityViolationException.class)
    public ResponseEntity<ApiResponse<Object>> dataIntegrityViolationExceptionHandler(HttpServletRequest request, final DataIntegrityViolationException e) {
        ApiResponse<Object> apiResponse = ApiResponse.<Object>builder()
                .status(ApiStatus.REQUIRED_ERROR.getStatusCode())
                .message(ApiStatus.REQUIRED_ERROR.getMessage())
                .errors(null)
                .build();

        return ResponseEntity
                .ok(apiResponse);
    }
}

 

위에 있는 customExceptionHandler는 내가 만들어놓은 ApiException을 던졌을 때 받기위한 메소드이며 dataIntegerityViolationExceptionHanlder는 fk 없이 INSERT 때릴 때 INSERT 때리기도 전에 해당 예외가 터지길래 추가해봤다. 물론 Vaildation 사용해서 작업을 해야하는 부분이지만 귀찮아서 안했으므로 땜빵용이다.

 

errors는 나중에 작업할꺼다.

 

이제 테스트해보자.

 

    @PostMapping
    public ResponseEntity<ApiResponse<Object>> insBoard(BoardInsDto dto) {
        int insBoardRows = service.insBoard(dto);

        if (Util.isNull(insBoardRows)) {
            throw new ApiException(ApiStatus.REQUIRED_ERROR);
        } else {
            int result = dto.getIboard();
            ApiResponse<Object> apiResponse = ApiResponse.success(result);

            return ResponseEntity.ok(apiResponse);
        }
    }

 

 

잘잡아준다.

'Spring' 카테고리의 다른 글

[Spring] Eureka 서버 및 클라이언트 구축하기  (0) 2025.03.04
[Spring] Swagger UI 초기 세팅  (0) 2025.01.26
[Spring] CustomExceptionHanlder  (1) 2024.11.29
[Spring] 검색 결과 미리보기  (0) 2024.11.17
[Spring] 공개/비공개글  (2) 2024.11.03