Curl Up Black Cat


😎 서론

안녕하세요! 오늘은 Spring Boot 에서 API 응답과 예외 처리 모델을 구축해봤던 내용을 공유해 보려고 합니다. 코드들도 하나하나 분석해 볼께요. 스타트~!

 


1. ResponseModel - 응답 모델

먼저, 모든 API 응답에 공통적으로 사용될 ResponseModel 을 살펴봅니다.

@Getter
@Setter
@ToString
@AllArgsConstructor(staticName = "of")
public class ResponseModel<T> {
    private boolean success;
    private T data;
    private ErrorModel error;

    public static <T> ResponseModel<T> of(boolean success, T data) {
        return ResponseModel.of(success, data, null);
    }

    public static ResponseModel of(boolean success, ErrorCode code) {
        return ResponseModel.of(success, null, ErrorModel.of(code.name(), code.getMessage(), null));
    }

    public static ResponseModel of(boolean success, ErrorCode code, Exception ex) {
        return ResponseModel.of(success, null, code, ex);
    }

    public static <T> ResponseModel<T> of(boolean success, T data, ErrorCode code, Exception ex) {
        final ErrorModel error = (code != null) ? ErrorModel.of(code, ex) : null;
        return ResponseModel.of(success, data, error);
    }
}

주요 구성 요소:

1. success: 요청 처리의 성공 여부를 나타냅니다.

2. data: 요청에 대한 결과 데이터를 포함합니다.

3. error: 발생한 오류에 대한 정보를 포함하는 ErrorModel 객체 입니다.

 

ResponseModel 에는 여러 static 생성 메서드가 포함되어 있습니다. 이 메서드들을 통해 성공 또는 오류 상황에 따라 쉽게 응답 객체를 구성할 수 있습니다.

 

2. ErrorModel - 에러 모델

ErrorModel 은 오류에 대한 정보를 표준화하여 전달하기 위한 모델입니다.

 

package com.board.backend.model;

import com.board.backend.common.ErrorCode;
import com.board.backend.common.utils.ObjectUtil;
import com.board.backend.common.utils.StringUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@AllArgsConstructor(staticName = "of")
public class ErrorModel {
    private String code;
    private String message;
    private ErrorData data;

    @Getter
    @Setter
    @ToString
    @AllArgsConstructor(staticName = "of")
    public static class ErrorData {
        private String exceptionMessage;
        private String stackTrace;
    }

    public static ErrorModel of(ErrorCode code, Exception ex) {
        final String exceptionMessage = ObjectUtil.nonEmpty(ex) ? ex.getMessage() : null;
        final String stackTrace = ObjectUtil.nonEmpty(ex) ? getStackTraceAsString(ex) : null;
        final ErrorData data = (StringUtil.nonEmpty(exceptionMessage) || StringUtil.nonEmpty(stackTrace)) ? ErrorData.of(exceptionMessage, stackTrace) : null;
        return ErrorModel.of(code.name(), code.getMessage(), data);
    }

    private static String getStackTraceAsString(Exception ex) {
        final StringBuilder sb = new StringBuilder();
        for (StackTraceElement element : ex.getStackTrace()) {
            sb.append(element.toString());
            sb.append("\n");
        }
        return sb.toString();
    }
}

주요 구성 요소:

1. code: 오류 코드를 나타냅니다.

2. message: 오류 메시지를 나타냅니다.

3. data: 오류 발생 시의 추가 정보를 나타내는 ErrorData 객체입니다.

 

ErrorData 클래스는 발생한 예외의 메시지와 스택 트레이스 정보를 포함합니다. 이를 통해 오류의 원인을 더욱 자세히 파악할 수 있습니다.(스택 트레이스란? 프로그램에서 예외가 발생했을 때 해당 예외의 발생 경로를 추적하는 정보입니다. 이는 메서드 호출 순서와 라인 번호를 포함하여 오류가 발생한 위치와 그 원인을 확인할 수 있게 도와줍니다.)

 

3. ErrorCode - 에러 코드

@Getter
@AllArgsConstructor
public enum ErrorCode {
    SERVER_ERROR("서버 에러"),
    ARGUMENT_TYPE_MISMATCH("잘못된 파라미터타입이 전달되었습니다."),
    BOARD_NOT_FOUND("글이 존재하지 않습니다");
    ...
    private String message;
}

각 오류 유형에 대한 메시지를 함께 제공하여 사용자에게 의미 있는 오류 메시지를 전달 합니다. enum 타입으로 코드와 메시지를 함께 관리하였습니다.

 

4. ExceptionAdvice - 전역 예외 처리기

Spring Boot 에서 예외 처리의 중요성을 강조하며, 이를 위해 @ControllerAdvice@ExceptionHandler 를 통해 효과적인 전역 예외 처리 방식을 제공합니다.

@ControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity handleException(Exception ex, WebRequest request) {
        final ResponseModel responseModel = ResponseModel.of(false, ErrorCode.SERVER_ERROR, ex);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(responseModel);
    }

    @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class})
    public ResponseEntity handleTypeException(Exception ex) {
        final ResponseModel<Object> responseModel = ResponseModel.of(false, ErrorCode.ARGUMENT_TYPE_MISMATCH,  ex);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseModel);
    }
    ...
}

 

1) @ControllerAdvice 의 역할

@ControllerAdvice 는 Spring 에서 모든 컨트롤러에서 발생할 수 있는 예외들을 한곳에서 관리하고 처리하기 위해 도입된 어노테이션입니다. 이를 통해 코드의 중복을 줄이고, 일관된 예외 처리 로직을 구성할 수 도와줍니다. 

 

2) @ExceptionHandler 

@ExceptionHandler 는 특정 예외를 처리할 메서드를 지정하기 위한 Spring MVC 의 어노테이션입니다. 그 자체로도 유용하지만, @ControllerAdvice와 결합하여 사용할 경우, 애플리케이션 전체의 예외를 중앙에서 효율적으로 관리할 수 있게 됩니다.

 

예시:

// 단일 지정
@ExceptionHandler({CustomException.class})

// 다중 지정
@ExceptionHandler({CustomException1.class, CustomException2.class, CustomException3.class})

이렇게 설정하면, 위에서 지정된 예외들이 발생했을 때 동일한 방식으로 처리됩니다.

 

3) 확장성

@ControllerAdvice 의 장점중 하나는 그 확장성입니다. 새로운 예외를 추가하거나 예외 처리 방식을 수정해야 할 때, @ExceptionHandler 를 적절한 메서드에 추가하거나 수정함으로써 중앙에서 예외 처리 로직을 쉽게 관리하고 확장할 수 있습니다.


🤠 결론

Spring Boot 환경에서 나름대로 생각해서 구현했던 응답과 예외 처리 방법에 대해 알아보았습니다. 일관된 응답과 정확한 예외 처리는 API의 신뢰성을 높이는 핵심 요소로, 이를 통해 사용자와의 신뢰 관계를 구축하고, 개발팀 간의 협업 효율을 높일 수 있습니다. 🙂


😎 서론

안녕하세요! 오늘은 스프링 부트에서 JSON 데이터를 최적화하는 간단한 방법에 대해 알려드리려 합니다. 서비스에서 작은 최적화도 사용자 경험에 변화를 가져올 있기에, 이를 활용하면 많은 이점을 얻을 있습니다.

 


🤔 왜 null 값을 제거해야 하나요?

JSON 응답 데이터에서 null 값은 다음과 같은 문제점을 발생시킬 수 있습니다.

  • 데이터크기: 불필요한 null 값은 전송되는 데이터의 크기를 불필요하게 증가시킵니다.
  • 클라이언트 처리: 클라이언트에서는 null 값에 대한 추가적인 처리가 필요하게 됩니다.

 

Before

{
    "id": 14,
    "title": "제목",
    "content": "내용",
    "writer": "홍길동",
    "created_id": null
}

🥸 Jackson 라이브러리로 Null 값 제거하기

Spring Boot에서는 Jackson 라이브러리를 활용하여 JSON 응답 데이터를 효과적으로 관리할 있습니다. 아래의 설정을 사용하면, null 값을 가진 필드는 JSON 응답에서 자동으로 제외됩니다.

 

package com.board.backend.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public ObjectMapper objectMapper() {
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return objectMapper;
    }
}

 

적용 후의 결과 입니다.

 

After

{
    "id": 14,
    "title": "제목",
    "content": "내용",
    "writer": "홍길동"
}

🤠 마치며

JSON 내의 null 값을 제거하는 것은 간단한 절차처럼 보일 수 있지만, 이러한 작은 최적화가 서비스의 응답 시간 개선과 클라이언트의 처리 부담 감소에 큰 도움을 줍니다. 특히, 대용량 데이터를 다루는 서비스나 리소스가 제한된 모바일 환경에서는 이런 최적화가 더욱 중요합니다.

 

데이터를 깔끔하게 관리하고, 사용자에게 필요한 정보만을 효율적으로 전달하는 것은 좋은 서비스의 기본입니다. 글을 통해 소개된 방법이 여러분의 서비스에 적용되어, 사용자 경험을 단계 향상시키는 도움이 되기를 바랍니다.

 

+ Recent posts