본문 바로가기

카테고리 없음

✅ 오늘 한 일

  1. 모든 API 응답을 CommonWrapperResponse로 래핑
    • 모든 컨트롤러의 응답을 일관된 형식으로 감싸기 위해 ResponseBodyAdvice를 구현함.
    • 성공 응답과 에러 응답 모두 공통 포맷으로 제공하도록 함.
    • status, data 필드를 포함하는 CommonWrapperResponse 설계.
더보기
package org.example.expert.common.adsd;

import lombok.Getter;

@Getter
public class CommonWrapperResponse {
    // Getters and Setters
    private int status;
    private Object data;

    // Builder pattern
    public static CommonWrapperResponseBuilder builder() {
        return new CommonWrapperResponseBuilder();
    }

    public static class CommonWrapperResponseBuilder {
        private int status;
        private Object data;

        public CommonWrapperResponseBuilder status(int status) {
            this.status = status;
            return this;
        }

        public CommonWrapperResponseBuilder data(Object data) {
            this.data = data;
            return this;
        }

        public CommonWrapperResponse build() {
            return new CommonWrapperResponse(status, data);
        }
    }

    private CommonWrapperResponse(int status, Object data) {
        this.status = status;
        this.data = data;
    }

}
  1. ErrorResponse를 사용한 에러 응답 포맷 표준화
    • 모든 예외 응답은 ErrorResponse 클래스를 통해 처리.
    • 필드:
      • message: 사용자에게 보여줄 메시지
      • code: HttpStatus 이름 (예: BAD_REQUEST)
      • status: HttpStatus 객체 자체
더보기
@Getter
public class ErrorResponse {
    private String message;
    private String code;
    private HttpStatus status;

    // Constructor, Getters, Setters
    public ErrorResponse(String message, String code, HttpStatus status) {
        this.message = message;
        this.code = code;
        this.status = status;
    }

}
  1. @RestControllerAdvice로 예외 전역 처리
    • InvalidRequestException, AuthException, ServerException, Exception 등을 전역 처리함.
    • 응답 코드와 메시지를 명확히 지정하여 클라이언트에서 쉽게 파싱 가능.
더보기
package org.example.expert.common.adsd;

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.example.expert.common.exception.ErrorResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.List;

@RestControllerAdvice
public class CommonControllerAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return !Void.TYPE.equals(returnType.getParameterType());
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof ErrorResponse) {
            ErrorResponse errorResponse = (ErrorResponse) body;
            HttpStatus httpStatus = errorResponse.getStatus();
            response.setStatusCode(httpStatus);
            return wrapResponse(errorResponse, httpStatus);
        }
        return wrapResponse(body, HttpStatus.OK);
    }



    private CommonWrapperResponse wrapResponse(Object response, HttpStatus status) {
        return CommonWrapperResponse.builder()
                .status(status.value())
                .data(response)
                .build();
    }

}
  1. Validation 예외 처리 (MethodArgumentNotValidException)
    • 필드 검증 실패 시 발생하는 예외에 대해 각 필드별 메시지를 Map<String, String> 형태로 반환.
    • 추후 ErrorResponse로 통합 가능성 고려.
  2. 빈 리스트 응답 예외 처리
    • ResponseBodyAdvice 내부에서 리스트가 비어있는 경우, 정의되지 않은 오류 메시지와 함께 500 응답 반환 처리.

🧠 배운 점

  • ResponseBodyAdvice를 활용하면 모든 API 응답에 대해 공통 포맷을 적용할 수 있음.
  • HttpStatus를 직접 응답 객체에 포함시키면 클라이언트 입장에서 에러 처리 및 디버깅이 훨씬 쉬움.
  • supports() 메서드에서 Void.TYPE을 제외하면 void 리턴 메서드는 자동으로 제외되어 불필요한 래핑을 방지할 수 있음.
  • 전역 예외 처리기(@RestControllerAdvice)는 예외 처리 로직을 각 컨트롤러에서 분리시켜 코드의 가독성과 재사용성을 높여줌.

🤔 다음에 개선할 점

  • MethodArgumentNotValidException 응답도 ErrorResponse 포맷으로 통일할 예정.
  • 응답에 timestamp, path, requestId 등을 추가하여 로깅 및 디버깅에 유용하게 만들기.
  • Swagger/OpenAPI 문서와 연동하여 예외 응답 포맷도 명세서에 반영되도록 개선.
  • 필요 시 ExceptionCode enum을 도입해 에러코드 관리 고도화.

  • API 응답은 무조건 200 OK로 통일하고 내부적으로 ErrorResponse 또는 비즈니스 코드로 판단하는 구조는 프론트엔드 협업과 유지보수에 매우 유리.

 

💡 느낀 점

하나의 API에서도 성공, 실패 응답이 다양하게 나올 수 있기 때문에, 통일된 구조로 관리하는 건 생각보다 훨씬 중요하다.
특히 프론트엔드와 협업 시, 공통 포맷 덕분에 개발 및 디버깅이 매우 수월해질 것으로 기대된다.
이번 구조는 앞으로 다른 프로젝트에도 충분히 재사용 가능할 만큼 잘 정리된 느낌!